OR-Tools  9.3
quadratic_program.h
Go to the documentation of this file.
1// Copyright 2010-2021 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#ifndef PDLP_QUADRATIC_PROGRAM_H_
15#define PDLP_QUADRATIC_PROGRAM_H_
16
17#include <cstdint>
18#include <limits>
19#include <optional>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include "Eigen/Core"
25#include "Eigen/SparseCore"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/types/optional.h"
30#include "ortools/linear_solver/linear_solver.pb.h"
31
33
34// Represents the quadratic program (QP):
35// min_x (objective_vector^T x + (1/2) x^T objective_matrix x) s.t.
36// constraint_lower_bounds <= constraint_matrix x <= constraint_upper_bounds
37// variable_lower_bounds <= x <= variable_upper_bounds
38//
39// constraint_lower_bounds and variable_lower_bounds may include negative
40// infinities. constraint_upper_bounds and variable_upper_bounds may
41// contain positive infinities. Other than that all entries of all fields must
42// be finite. The objective_matrix must be symmetric and positive semi-definite.
43//
44// For convenience, the struct also stores a scaling factor and objective
45// offset. These factors can be used to transform objective values based on the
46// problem definition above into objective values that are meaningful for the
47// user. See ApplyObjectiveScalingAndOffset.
48//
49// This struct is also intended for use with linear programs (LPs), which are
50// QPs with a zero objective_matrix.
51//
52// The dual is documented at
53// https://developers.google.com/optimization/lp/pdlp_math.
55 QuadraticProgram(int64_t num_variables, int64_t num_constraints) {
56 ResizeAndInitialize(num_variables, num_constraints);
57 }
59
60 // QuadraticPrograms may be copied or moved. Eigen::SparseMatrix doesn't
61 // have move operations so we use custom implementations based on swap.
62 QuadraticProgram(const QuadraticProgram& other) = default;
64 : objective_vector(std::move(other.objective_vector)),
65 objective_matrix(std::move(other.objective_matrix)),
66 constraint_lower_bounds(std::move(other.constraint_lower_bounds)),
67 constraint_upper_bounds(std::move(other.constraint_upper_bounds)),
68 variable_lower_bounds(std::move(other.variable_lower_bounds)),
69 variable_upper_bounds(std::move(other.variable_upper_bounds)),
70 problem_name(std::move(other.problem_name)),
71 variable_names(std::move(other.variable_names)),
72 constraint_names(std::move(other.constraint_names)),
73 objective_offset(other.objective_offset),
74 objective_scaling_factor(other.objective_scaling_factor) {
75 constraint_matrix.swap(other.constraint_matrix);
76 }
79 objective_vector = std::move(other.objective_vector);
80 objective_matrix = std::move(other.objective_matrix);
81 constraint_matrix.swap(other.constraint_matrix);
82 constraint_lower_bounds = std::move(other.constraint_lower_bounds);
83 constraint_upper_bounds = std::move(other.constraint_upper_bounds);
84 variable_lower_bounds = std::move(other.variable_lower_bounds);
85 variable_upper_bounds = std::move(other.variable_upper_bounds);
86 problem_name = std::move(other.problem_name);
87 variable_names = std::move(other.variable_names);
88 constraint_names = std::move(other.constraint_names);
89 objective_offset = other.objective_offset;
90 objective_scaling_factor = other.objective_scaling_factor;
91 return *this;
92 }
93
94 // Initializes the quadratic program with num_variables variables and
95 // num_constraints constraints. Lower and upper bounds are set to to negative
96 // and positive infinity, repectively. The objective matrix is cleared. All
97 // other matrices and vectors are set to zero. Resets the optional names
98 // (program_name, variable_names, and constraint_names). objective_offset is
99 // set to 0 and objective_scaling_factor is set to 1.
100 void ResizeAndInitialize(int64_t num_variables, int64_t num_constraints) {
101 constexpr double kInfinity = std::numeric_limits<double>::infinity();
103 objective_matrix.reset();
104 constraint_matrix.resize(num_constraints, num_variables);
106 Eigen::VectorXd::Constant(num_constraints, -kInfinity);
108 Eigen::VectorXd::Constant(num_constraints, kInfinity);
110 Eigen::VectorXd::Constant(num_variables, -kInfinity);
111 variable_upper_bounds = Eigen::VectorXd::Constant(num_variables, kInfinity);
112 problem_name.reset();
113 variable_names.reset();
114 constraint_names.reset();
115 objective_offset = 0.0;
117 }
118
119 // Returns objective_scaling_factor * (objective + objective_offset).
120 // objective_scaling_factor is useful for modeling maximization problems.
121 // For example, max c'x = -1 * min (-c)'x. objective_offset can be a
122 // by-product of presolve transformations that eliminate variables.
123 double ApplyObjectiveScalingAndOffset(double objective) const {
124 return objective_scaling_factor * (objective + objective_offset);
125 }
126
127 Eigen::VectorXd objective_vector;
128 // If this field isn't set, the objective matrix is interpreted to be zero,
129 // i.e., this is a linear programming problem.
130 std::optional<Eigen::DiagonalMatrix<double, Eigen::Dynamic>> objective_matrix;
131 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> constraint_matrix;
134 // The problem, constraint, and variable names are optional.
135 absl::optional<std::string> problem_name;
136 absl::optional<std::vector<std::string>> variable_names;
137 absl::optional<std::vector<std::string>> constraint_names;
138
139 // These fields are provided for convenience; they don't change the
140 // mathematical definition of the problem, but they could change the objective
141 // values reported to the user.
144};
145
146// Returns InvalidArgument if vector or matrix dimensions are inconsistent.
147// Returns OkStatus otherwise.
149
150inline bool IsLinearProgram(const QuadraticProgram& qp) {
151 return !qp.objective_matrix.has_value();
152}
153
154// Checks if the lower and upper bounds of the problem are consistent, i.e. for
155// each variable and constraint bound we have lower_bound <= upper_bound. If
156// the input is consistent the method returns true, otherwise it returns false.
157// See also HasValidBounds(const ShardedQuadraticProgram&).
158bool HasValidBounds(const QuadraticProgram& qp);
159
160// Converts an MPModelProto into a QuadraticProgram.
161// Returns an error if general constraints are present.
162// If relax_integer_variables is true integer variables are relaxed to
163// continuous; otherwise integer variables are an error.
164// If include_names is true (the default is false), the problem, constraint,
165// and variable names are included in the QuadraticProgram; otherwise they are
166// left empty.
167// Maximization problems are converted to minimization by negating the
168// objective and setting objective_scaling_factor to -1, which preserves the
169// reported objective values.
170absl::StatusOr<QuadraticProgram> QpFromMpModelProto(
171 const MPModelProto& proto, bool relax_integer_variables,
172 bool include_names = false);
173
174// Returns InvalidArgument if the given quadratic program is too large to
175// convert to MPModelProto and OkStatus otherwise.
176absl::Status CanFitInMpModelProto(const QuadraticProgram& qp);
177
178// Converts a QuadraticProgram into an MPModelProto. To preserve objective
179// values in the conversion, the objective vector, objective matrix, and
180// objective offset are scaled by objective_scaling_factor, and if
181// objective_scaling_factor is negative, then the proto is a maximization
182// problem (otherwise it's a minimization problem). Returns InvalidArgumentError
183// if objective_scaling_factor is zero or if CanFitInMpModelProto() fails.
184absl::StatusOr<MPModelProto> QpToMpModelProto(const QuadraticProgram& qp);
185
186// Like matrix.setFromTriplets(triplets), except that setFromTriplets results
187// in having three copies of the nonzeros in memory at the same time, because it
188// first fills one matrix from triplets, and then transposes it into another.
189// This avoids having the third copy in memory by sorting the triplets,
190// reserving space in the matrix, and then inserting in sorted order.
191// Compresses the matrix (SparseMatrix.makeCompressed()) after loading it.
192// NOTE: This intentionally passes triplets by copy, because it modifies them.
193// To avoid the copy, pass a move reference.
195 std::vector<Eigen::Triplet<double, int64_t>> triplets,
196 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix);
197
198// Utility functions for internal use only.
199namespace internal {
200// Like CanFitInMpModelProto() but has an extra argument for the largest number
201// of variables, constraints, or objective non-zeros that should be counted as
202// convertible. CanFitInMpModelProto() passes 2^31 - 1 for this argument and
203// unit tests pass small values.
204absl::Status TestableCanFitInMpModelProto(const QuadraticProgram& qp,
205 int64_t largest_ok_size);
206
207// Modifies a vector of Eigen::Triplets in place, combining consecutive entries
208// with the same row and column, summing their values. This is most effective
209// if the triplets are sorted by row and column, so that multiple entries for
210// the same entry will be consecutive.
212 std::vector<Eigen::Triplet<double, int64_t>>& triplets);
213} // namespace internal
214} // namespace operations_research::pdlp
215
216#endif // PDLP_QUADRATIC_PROGRAM_H_
CpModelProto proto
void CombineRepeatedTripletsInPlace(std::vector< Eigen::Triplet< double, int64_t > > &triplets)
absl::Status TestableCanFitInMpModelProto(const QuadraticProgram &qp, const int64_t largest_ok_size)
absl::StatusOr< QuadraticProgram > QpFromMpModelProto(const MPModelProto &proto, bool relax_integer_variables, bool include_names)
absl::Status ValidateQuadraticProgramDimensions(const QuadraticProgram &qp)
absl::Status CanFitInMpModelProto(const QuadraticProgram &qp)
void SetEigenMatrixFromTriplets(std::vector< Eigen::Triplet< double, int64_t > > triplets, Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix)
bool HasValidBounds(const QuadraticProgram &qp)
absl::StatusOr< MPModelProto > QpToMpModelProto(const QuadraticProgram &qp)
bool IsLinearProgram(const QuadraticProgram &qp)
int64_t Zero()
NOLINT.
double ApplyObjectiveScalingAndOffset(double objective) const
absl::optional< std::vector< std::string > > constraint_names
absl::optional< std::vector< std::string > > variable_names
QuadraticProgram(QuadraticProgram &&other) noexcept
QuadraticProgram(const QuadraticProgram &other)=default
QuadraticProgram & operator=(const QuadraticProgram &other)=default
absl::optional< std::string > problem_name
QuadraticProgram(int64_t num_variables, int64_t num_constraints)
QuadraticProgram & operator=(QuadraticProgram &&other)
void ResizeAndInitialize(int64_t num_variables, int64_t num_constraints)
Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > constraint_matrix
std::optional< Eigen::DiagonalMatrix< double, Eigen::Dynamic > > objective_matrix