OR-Tools  9.6
sharded_optimization_utils.h
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // These are internal helper functions and classes that implicitly or explicitly
15 // operate on a `ShardedQuadraticProgram`. Utilities that are purely linear
16 // algebra operations (e.g., norms) should be defined in sharder.h instead.
17 
18 #ifndef PDLP_SHARDED_OPTIMIZATION_UTILS_H_
19 #define PDLP_SHARDED_OPTIMIZATION_UTILS_H_
20 
21 #include <limits>
22 #include <optional>
23 #include <random>
24 
25 #include "Eigen/Core"
27 #include "ortools/pdlp/sharder.h"
28 #include "ortools/pdlp/solve_log.pb.h"
29 
30 namespace operations_research::pdlp {
31 
32 // This computes weighted averages of vectors.
33 // It satisfies the following: if all the averaged vectors have component i
34 // equal to x then the average has component i exactly equal to x, without any
35 // floating-point roundoff. In practice the above is probably still true with
36 // "equal to x" replaced with "at least x" or "at most x". However unrealistic
37 // counter examples probably exist involving a new item with weight 10^15 times
38 // greater than the total weight so far.
40  public:
41  // Initializes the weighted average by creating a vector sized according to
42  // the number of elements in `sharder`. Retains the pointer to `sharder`, so
43  // `sharder` must outlive this object.
44  explicit ShardedWeightedAverage(const Sharder* sharder);
45 
48 
49  // Adds `datapoint` to the average weighted by `weight`. CHECK-fails if
50  // `weight` is negative.
51  void Add(const Eigen::VectorXd& datapoint, double weight);
52 
53  // Clears the sum to zero, i.e., just constructed.
54  void Clear();
55 
56  // Returns true if there is at least one term in the average with a positive
57  // weight.
58  bool HasNonzeroWeight() const { return sum_weights_ > 0.0; }
59 
60  // Returns the sum of the weights of the datapoints added so far.
61  double Weight() const { return sum_weights_; }
62 
63  // Computes the weighted average of the datapoints added so far, i.e.,
64  // sum_i weight[i] * datapoint[i] / sum_i weight[i]. The results are set to
65  // zero if `HasNonzeroWeight()` is false.
66  Eigen::VectorXd ComputeAverage() const;
67 
68  int NumTerms() const { return num_terms_; }
69 
70  private:
71  Eigen::VectorXd average_;
72  double sum_weights_ = 0.0;
73  int num_terms_ = 0;
74  const Sharder* sharder_;
75 };
76 
77 // Returns a `QuadraticProgramStats` for a `ShardedQuadraticProgram`.
78 QuadraticProgramStats ComputeStats(const ShardedQuadraticProgram& qp,
79  double infinite_constraint_bound_threshold =
80  std::numeric_limits<double>::infinity());
81 
82 // `LInfRuizRescaling` and `L2NormRescaling` rescale the (scaled) constraint
83 // matrix of the LP by updating the scaling vectors in-place. More specifically,
84 // the (scaled) constraint matrix always has the format: `diag(row_scaling_vec)
85 // * sharded_qp.Qp().constraint_matrix * diag(col_scaling_vec)`, and
86 // `row_scaling_vec` and `col_scaling_vec` are updated in-place. On input,
87 // `row_scaling_vec` and `col_scaling_vec` provide the initial scaling.
88 
89 // With each iteration of `LInfRuizRescaling` scaling, `row_scaling_vec`
90 // (`col_scaling_vec`) is divided by the sqrt of the row (col) LInf norm of the
91 // current (scaled) constraint matrix. The (scaled) constraint matrix approaches
92 // having all row and column LInf norms equal to 1 as the number of iterations
93 // goes to infinity. This convergence is fast (linear). More details of Ruiz
94 // rescaling algorithm can be found at:
95 // http://www.numerical.rl.ac.uk/reports/drRAL2001034.pdf.
97  int num_iterations, Eigen::VectorXd& row_scaling_vec,
98  Eigen::VectorXd& col_scaling_vec);
99 
100 // `L2NormRescaling` divides `row_scaling_vec` (`col_scaling_vec`) by the sqrt
101 // of the row (col) L2 norm of the current (scaled) constraint matrix. Unlike
102 // `LInfRescaling`, this function does only one iteration because the scaling
103 // procedure does not converge in general. This is not Ruiz rescaling for the L2
104 // norm.
106  Eigen::VectorXd& row_scaling_vec,
107  Eigen::VectorXd& col_scaling_vec);
108 
112 };
113 
115  Eigen::VectorXd row_scaling_vec;
116  Eigen::VectorXd col_scaling_vec;
117 };
118 
119 // Applies the rescaling specified by `rescaling_options` to `sharded_qp` (in
120 // place). Returns the scaling vectors that were applied.
121 ScalingVectors ApplyRescaling(const RescalingOptions& rescaling_options,
122  ShardedQuadraticProgram& sharded_qp);
123 
125  double value = 0.0;
126  Eigen::VectorXd gradient;
127 };
128 
129 // Computes the value of the primal part of the Lagrangian function defined at
130 // https://developers.google.com/optimization/lp/pdlp_math, i.e.,
131 // c^T x + (1/2) x^T Qx - y^T Ax and its gradient with respect to the primal
132 // variables x, i.e., c + Qx - A^T y. `dual_product` is A^T y. Note: The
133 // objective constant is omitted. The result is undefined and invalid if any
134 // primal bounds are violated.
136  const Eigen::VectorXd& primal_solution,
137  const Eigen::VectorXd& dual_product);
138 
139 // Returns a subderivative of the concave dual penalty function that appears in
140 // the Lagrangian:
141 // -p(`dual`; -`constraint_upper_bound`, -`constraint_lower_bound`) =
142 // { `constraint_upper_bound * dual` when `dual < 0`, 0 when `dual == 0`,
143 // and `constraint_lower_bound * dual` when `dual > 0`}
144 // (as defined at https://developers.google.com/optimization/lp/pdlp_math).
145 // The subderivative is not necessarily unique when dual == 0. In this case, if
146 // only one of the bounds is finite, we return that one. If both are finite, we
147 // return `primal_product` projected onto the bounds, which causes the dual
148 // Lagrangian gradient to be zero when the constraint is not violated. If both
149 // are infinite, we return zero. The value returned is valid only when the
150 // function is finite-valued.
151 double DualSubgradientCoefficient(double constraint_lower_bound,
152  double constraint_upper_bound, double dual,
153  double primal_product);
154 
155 // Computes the value of the dual part of the Lagrangian function defined at
156 // https://developers.google.com/optimization/lp/pdlp_math, i.e., -h^*(y) and
157 // the gradient of the Lagrangian with respect to the dual variables y, i.e.,
158 // -Ax - \grad_y h^*(y). Note the asymmetry with `ComputePrimalGradient`: the
159 // term -y^T Ax is not part of the value. Because h^*(y) is piece-wise linear, a
160 // subgradient is returned at a point of non-smoothness. `primal_product` is Ax.
161 // The result is undefined and invalid if any duals violate their bounds.
163  const Eigen::VectorXd& dual_solution,
164  const Eigen::VectorXd& primal_product);
165 
170 };
171 
172 // Estimates the maximum singular value of A by applying the method of power
173 // iteration to A^T A. If `primal_solution` or `dual_solution` is provided,
174 // restricts to the "active" part of A, that is, the columns (rows) for
175 // variables that are not at their bounds in the solution. The estimate will
176 // have `desired_relative_error` with probability at least 1 -
177 // `failure_probability`. The number of iterations will be approximately
178 // log(`primal_size` / `failure_probability`^2)/(2 * `desired_relative_error`).
179 // Uses a mersenne-twister portable random number generator to generate the
180 // starting point for the power method, in order to have deterministic results.
182  const ShardedQuadraticProgram& sharded_qp,
183  const std::optional<Eigen::VectorXd>& primal_solution,
184  const std::optional<Eigen::VectorXd>& dual_solution,
185  double desired_relative_error, double failure_probability,
186  std::mt19937& mt_generator);
187 
188 // Checks if the lower and upper bounds of the problem are consistent, i.e. for
189 // each variable and constraint bound we have lower_bound <= upper_bound. If
190 // the input is consistent the method returns true, otherwise it returns false.
191 // See also `HasValidBounds(const QuadraticProgram&)`.
192 bool HasValidBounds(const ShardedQuadraticProgram& sharded_qp);
193 
194 // Projects `primal` onto the variable bounds constraints.
196  Eigen::VectorXd& primal);
197 
198 // Projects `dual` onto the dual variable bounds; see
199 // https://developers.google.com/optimization/lp/pdlp_math#dual_variable_bounds.
201  Eigen::VectorXd& dual);
202 
203 } // namespace operations_research::pdlp
204 
205 #endif // PDLP_SHARDED_OPTIMIZATION_UTILS_H_
void Add(const Eigen::VectorXd &datapoint, double weight)
ShardedWeightedAverage(ShardedWeightedAverage &&)=default
ShardedWeightedAverage & operator=(ShardedWeightedAverage &&)=default
LagrangianPart ComputeDualGradient(const ShardedQuadraticProgram &sharded_qp, const VectorXd &dual_solution, const VectorXd &primal_product)
LagrangianPart ComputePrimalGradient(const ShardedQuadraticProgram &sharded_qp, const VectorXd &primal_solution, const VectorXd &dual_product)
void LInfRuizRescaling(const ShardedQuadraticProgram &sharded_qp, const int num_iterations, VectorXd &row_scaling_vec, VectorXd &col_scaling_vec)
SingularValueAndIterations EstimateMaximumSingularValueOfConstraintMatrix(const ShardedQuadraticProgram &sharded_qp, const std::optional< VectorXd > &primal_solution, const std::optional< VectorXd > &dual_solution, const double desired_relative_error, const double failure_probability, std::mt19937 &mt_generator)
double DualSubgradientCoefficient(const double constraint_lower_bound, const double constraint_upper_bound, const double dual, const double primal_product)
bool HasValidBounds(const QuadraticProgram &qp)
void ProjectToDualVariableBounds(const ShardedQuadraticProgram &sharded_qp, VectorXd &dual)
void L2NormRescaling(const ShardedQuadraticProgram &sharded_qp, VectorXd &row_scaling_vec, VectorXd &col_scaling_vec)
ScalingVectors ApplyRescaling(const RescalingOptions &rescaling_options, ShardedQuadraticProgram &sharded_qp)
QuadraticProgramStats ComputeStats(const ShardedQuadraticProgram &qp, const double infinite_constraint_bound_threshold)
void ProjectToPrimalVariableBounds(const ShardedQuadraticProgram &sharded_qp, VectorXd &primal)
int64_t weight
Definition: pack.cc:510