Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sharded_optimization_utils.h
Go to the documentation of this file.
1// Copyright 2010-2024 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"
28#include "ortools/pdlp/solve_log.pb.h"
29
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`.
78QuadraticProgramStats ComputeStats(const ShardedQuadraticProgram& qp);
79
80// `LInfRuizRescaling` and `L2NormRescaling` rescale the (scaled) constraint
81// matrix of the LP by updating the scaling vectors in-place. More specifically,
82// the (scaled) constraint matrix always has the format: `diag(row_scaling_vec)
83// * sharded_qp.Qp().constraint_matrix * diag(col_scaling_vec)`, and
84// `row_scaling_vec` and `col_scaling_vec` are updated in-place. On input,
85// `row_scaling_vec` and `col_scaling_vec` provide the initial scaling.
86
87// With each iteration of `LInfRuizRescaling` scaling, `row_scaling_vec`
88// (`col_scaling_vec`) is divided by the sqrt of the row (col) LInf norm of the
89// current (scaled) constraint matrix. The (scaled) constraint matrix approaches
90// having all row and column LInf norms equal to 1 as the number of iterations
91// goes to infinity. This convergence is fast (linear). More details of Ruiz
92// rescaling algorithm can be found at:
93// http://www.numerical.rl.ac.uk/reports/drRAL2001034.pdf.
95 int num_iterations, Eigen::VectorXd& row_scaling_vec,
96 Eigen::VectorXd& col_scaling_vec);
97
98// `L2NormRescaling` divides `row_scaling_vec` (`col_scaling_vec`) by the sqrt
99// of the row (col) L2 norm of the current (scaled) constraint matrix. Unlike
100// `LInfRescaling`, this function does only one iteration because the scaling
101// procedure does not converge in general. This is not Ruiz rescaling for the L2
102// norm.
104 Eigen::VectorXd& row_scaling_vec,
105 Eigen::VectorXd& col_scaling_vec);
106
111
113 Eigen::VectorXd row_scaling_vec;
114 Eigen::VectorXd col_scaling_vec;
115};
116
117// Applies the rescaling specified by `rescaling_options` to `sharded_qp` (in
118// place). Returns the scaling vectors that were applied.
119ScalingVectors ApplyRescaling(const RescalingOptions& rescaling_options,
120 ShardedQuadraticProgram& sharded_qp);
121
123 double value = 0.0;
124 Eigen::VectorXd gradient;
125};
126
127// Computes the value of the primal part of the Lagrangian function defined at
128// https://developers.google.com/optimization/lp/pdlp_math, i.e.,
129// c^T x + (1/2) x^T Qx - y^T Ax and its gradient with respect to the primal
130// variables x, i.e., c + Qx - A^T y. `dual_product` is A^T y. Note: The
131// objective constant is omitted. The result is undefined and invalid if any
132// primal bounds are violated.
134 const Eigen::VectorXd& primal_solution,
135 const Eigen::VectorXd& dual_product);
136
137// Returns a subderivative of the concave dual penalty function that appears in
138// the Lagrangian:
139// -p(`dual`; -`constraint_upper_bound`, -`constraint_lower_bound`) =
140// { `constraint_upper_bound * dual` when `dual < 0`, 0 when `dual == 0`,
141// and `constraint_lower_bound * dual` when `dual > 0`}
142// (as defined at https://developers.google.com/optimization/lp/pdlp_math).
143// The subderivative is not necessarily unique when dual == 0. In this case, if
144// only one of the bounds is finite, we return that one. If both are finite, we
145// return `primal_product` projected onto the bounds, which causes the dual
146// Lagrangian gradient to be zero when the constraint is not violated. If both
147// are infinite, we return zero. The value returned is valid only when the
148// function is finite-valued.
149double DualSubgradientCoefficient(double constraint_lower_bound,
150 double constraint_upper_bound, double dual,
151 double primal_product);
152
153// Computes the value of the dual part of the Lagrangian function defined at
154// https://developers.google.com/optimization/lp/pdlp_math, i.e., -h^*(y) and
155// the gradient of the Lagrangian with respect to the dual variables y, i.e.,
156// -Ax - \grad_y h^*(y). Note the asymmetry with `ComputePrimalGradient`: the
157// term -y^T Ax is not part of the value. Because h^*(y) is piece-wise linear, a
158// subgradient is returned at a point of non-smoothness. `primal_product` is Ax.
159// The result is undefined and invalid if any duals violate their bounds.
161 const Eigen::VectorXd& dual_solution,
162 const Eigen::VectorXd& primal_product);
163
169
170// Estimates the maximum singular value of A by applying the method of power
171// iteration to A^T A. If `primal_solution` or `dual_solution` is provided,
172// restricts to the "active" part of A, that is, the columns (rows) for
173// variables that are not at their bounds in the solution. The estimate will
174// have `desired_relative_error` with probability at least 1 -
175// `failure_probability`. The number of iterations will be approximately
176// log(`primal_size` / `failure_probability`^2)/(2 * `desired_relative_error`).
177// Uses a mersenne-twister portable random number generator to generate the
178// starting point for the power method, in order to have deterministic results.
180 const ShardedQuadraticProgram& sharded_qp,
181 const std::optional<Eigen::VectorXd>& primal_solution,
182 const std::optional<Eigen::VectorXd>& dual_solution,
183 double desired_relative_error, double failure_probability,
184 std::mt19937& mt_generator);
185
186// Checks if the lower and upper bounds of the problem are consistent, i.e. for
187// each variable and constraint bound we have lower_bound <= upper_bound,
188// lower_bound < inf, and upper_bound > -inf. If the input is consistent the
189// method returns true, otherwise it returns false.
190bool HasValidBounds(const ShardedQuadraticProgram& sharded_qp);
191
192// Projects `primal` onto the variable bounds constraints.
193// If `use_feasibility_bounds == true`, all finite variable bounds are replaced
194// by zero.
196 Eigen::VectorXd& primal,
197 bool use_feasibility_bounds = false);
198
199// Projects `dual` onto the dual variable bounds; see
200// https://developers.google.com/optimization/lp/pdlp_math#dual_variable_bounds.
202 Eigen::VectorXd& dual);
203
204} // namespace operations_research::pdlp
205
206#endif // PDLP_SHARDED_OPTIMIZATION_UTILS_H_
ShardedWeightedAverage & operator=(ShardedWeightedAverage &&)=default
void Add(const Eigen::VectorXd &datapoint, double weight)
double Weight() const
Returns the sum of the weights of the datapoints added so far.
void Clear()
Clears the sum to zero, i.e., just constructed.
ShardedWeightedAverage(ShardedWeightedAverage &&)=default
Validation utilities for solvers.proto.
LagrangianPart ComputeDualGradient(const ShardedQuadraticProgram &sharded_qp, const VectorXd &dual_solution, const VectorXd &primal_product)
QuadraticProgramStats ComputeStats(const ShardedQuadraticProgram &qp)
Returns a QuadraticProgramStats for a ShardedQuadraticProgram.
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)
void ProjectToPrimalVariableBounds(const ShardedQuadraticProgram &sharded_qp, VectorXd &primal, const bool use_feasibility_bounds)
void ProjectToDualVariableBounds(const ShardedQuadraticProgram &sharded_qp, VectorXd &dual)
double DualSubgradientCoefficient(const double constraint_lower_bound, const double constraint_upper_bound, const double dual, const double primal_product)
void L2NormRescaling(const ShardedQuadraticProgram &sharded_qp, VectorXd &row_scaling_vec, VectorXd &col_scaling_vec)
bool HasValidBounds(const ShardedQuadraticProgram &sharded_qp)
ScalingVectors ApplyRescaling(const RescalingOptions &rescaling_options, ShardedQuadraticProgram &sharded_qp)
int64_t weight
Definition pack.cc:510