Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
gscip_ext.cc
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
15
16#include <cmath>
17#include <limits>
18#include <string>
19#include <vector>
20
21#include "absl/strings/string_view.h"
24
25namespace operations_research {
26
27namespace {
28
29std::string MaybeExtendName(absl::string_view base_name,
30 absl::string_view extension) {
31 if (base_name.empty()) {
32 return "";
33 }
34 return absl::StrCat(base_name, "/", extension);
35}
36
37} // namespace
38
39GScipLinearExpr::GScipLinearExpr(SCIP_VAR* variable) { terms[variable] = 1.0; }
40
41GScipLinearExpr::GScipLinearExpr(double offset) : offset(offset) {}
42
44 const GScipLinearExpr& right) {
45 left.offset -= right.offset;
46 for (const auto& term : right.terms) {
47 left.terms[term.first] -= term.second;
48 }
49 return left;
50}
51
53 expr.offset = -expr.offset;
54 for (auto& term : expr.terms) {
55 term.second = -term.second;
56 }
57 return expr;
58}
59
60// Returns the range -inf <= left.terms - right.terms <= right.offset -
61// left.offset
62GScipLinearRange GScipLe(const GScipLinearExpr left,
63 const GScipLinearExpr& right) {
64 GScipLinearExpr diff = GScipDifference(left, right);
65 GScipLinearRange result;
66 result.lower_bound = -std::numeric_limits<double>::infinity();
67 result.upper_bound = -diff.offset;
68 for (const auto& term : diff.terms) {
69 result.variables.push_back(term.first);
70 result.coefficients.push_back(term.second);
71 }
72 return result;
73}
74
75absl::Status GScipCreateAbs(GScip* gscip, SCIP_Var* x, SCIP_Var* abs_x,
76 absl::string_view name) {
77 return GScipCreateMaximum(
78 gscip, GScipLinearExpr(abs_x),
80}
81
82absl::Status GScipCreateMaximum(GScip* gscip, const GScipLinearExpr& resultant,
83 const std::vector<GScipLinearExpr>& terms,
84 absl::string_view name) {
85 // TODO(user): it may be better to write this in terms of the disjuntive
86 // constraint, we need to support disjunctions in gscip.h to do this.
87 //
88 // z_i in {0,1}, indicates if y = x_i
89 //
90 // x_i <= y
91 // z_i => y <= x_i
92 // \sum_i z_i == 1
93 std::vector<SCIP_VAR*> indicators;
94 for (int i = 0; i < terms.size(); ++i) {
95 auto z = gscip->AddVariable(0.0, 1.0, 0.0, GScipVarType::kInteger,
96 MaybeExtendName(name, absl::StrCat("z_", i)));
97 RETURN_IF_ERROR(z.status());
98 indicators.push_back(*z);
99 }
100
101 for (int i = 0; i < terms.size(); ++i) {
102 // x_i <= y
104 gscip
105 ->AddLinearConstraint(
106 GScipLe(terms.at(i), resultant),
107 MaybeExtendName(name, absl::StrCat("x_", i, "_le_y")))
108 .status());
109 // z_i => y <= x_i
110 {
111 GScipLinearRange y_less_x = GScipLe(resultant, terms.at(i));
112 CHECK_EQ(y_less_x.lower_bound, -std::numeric_limits<double>::infinity());
113 GScipIndicatorConstraint ind;
114 ind.indicator_variable = indicators.at(i);
115 ind.variables = y_less_x.variables;
116 ind.coefficients = y_less_x.coefficients;
117 ind.upper_bound = y_less_x.upper_bound;
119 gscip
120 ->AddIndicatorConstraint(
121 ind, MaybeExtendName(
122 name, absl::StrCat("y_le__x_", i, "_if_z_", i)))
123 .status());
124 }
125 }
126
127 // sum_i z_i = 1.
128 GScipLinearRange z_use;
129 z_use.upper_bound = 1.0;
130 z_use.lower_bound = 1.0;
131 z_use.variables = indicators;
132 z_use.coefficients = std::vector<double>(indicators.size(), 1.0);
133
134 return gscip->AddLinearConstraint(z_use, MaybeExtendName(name, "one_z"))
135 .status();
136}
137
138absl::Status GScipCreateMinimum(GScip* gscip, const GScipLinearExpr& resultant,
139 const std::vector<GScipLinearExpr>& terms,
140 absl::string_view name) {
141 std::vector<GScipLinearExpr> negated_terms;
142 negated_terms.reserve(terms.size());
143 for (const GScipLinearExpr& e : terms) {
144 negated_terms.push_back(GScipNegate(e));
145 }
146 return GScipCreateMaximum(gscip, GScipNegate(resultant), negated_terms, name);
147}
148
150 GScip* gscip, std::vector<SCIP_Var*> quadratic_variables1,
151 std::vector<SCIP_Var*> quadratic_variables2,
152 std::vector<double> quadratic_coefficients, absl::string_view name) {
153 constexpr double kInf = std::numeric_limits<double>::infinity();
154 auto obj_term =
155 gscip->AddVariable(-kInf, kInf, 1.0, GScipVarType::kContinuous,
156 MaybeExtendName(name, "obj"));
157 RETURN_IF_ERROR(obj_term.status());
158 GScipQuadraticRange range;
159 range.quadratic_variables1 = quadratic_variables1;
160 range.quadratic_variables2 = quadratic_variables2;
161 range.quadratic_coefficients = quadratic_coefficients;
162 range.linear_coefficients = {-1.0};
163 range.linear_variables = {*obj_term};
164 if (gscip->ObjectiveIsMaximize()) {
165 // maximize z
166 // z <= Q(x, y)
167 // => 0 <= Q(x, y) - z <= inf
168 range.lower_bound = 0.0;
169 } else {
170 // minimize z
171 // z >= Q(x, y)
172 // => 0 >= Q(x, y) - z >= -inf
173 range.upper_bound = 0.0;
174 }
175 return gscip->AddQuadraticConstraint(range, MaybeExtendName(name, "cons"))
176 .status();
177}
178
180 GScip* gscip, const GScipIndicatorRangeConstraint& indicator_range,
181 absl::string_view name, const GScipConstraintOptions& options) {
182 if (std::isfinite(indicator_range.range.upper_bound)) {
183 GScipIndicatorConstraint ub_constraint;
184 ub_constraint.upper_bound = indicator_range.range.upper_bound;
185 ub_constraint.variables = indicator_range.range.variables;
186 ub_constraint.coefficients = indicator_range.range.coefficients;
187 ub_constraint.indicator_variable = indicator_range.indicator_variable;
188 ub_constraint.negate_indicator = indicator_range.negate_indicator;
189 RETURN_IF_ERROR(gscip
190 ->AddIndicatorConstraint(
191 ub_constraint, MaybeExtendName(name, "ub"), options)
192 .status());
193 }
194 if (std::isfinite(indicator_range.range.lower_bound)) {
195 // want z -> lb <= a * x
196 // <=> z -> -lb >= -a * x
197 GScipIndicatorConstraint lb_constraint;
198 lb_constraint.upper_bound = -indicator_range.range.lower_bound;
199 lb_constraint.variables = indicator_range.range.variables;
200 for (const double c : indicator_range.range.coefficients) {
201 lb_constraint.coefficients.push_back(-c);
202 }
203 lb_constraint.indicator_variable = indicator_range.indicator_variable;
204 lb_constraint.negate_indicator = indicator_range.negate_indicator;
205 RETURN_IF_ERROR(gscip
206 ->AddIndicatorConstraint(
207 lb_constraint, MaybeExtendName(name, "lb"), options)
208 .status());
209 }
210 return absl::OkStatus();
211}
212
213} // namespace operations_research
#define RETURN_IF_ERROR(expr)
const std::string name
A name for logging purposes.
absl::Status status
Definition g_gurobi.cc:44
In SWIG mode, we don't want anything besides these top-level includes.
GScipLinearExpr GScipDifference(GScipLinearExpr left, const GScipLinearExpr &right)
Returns left - right.
Definition gscip_ext.cc:43
absl::Status GScipCreateMinimum(GScip *gscip, const GScipLinearExpr &resultant, const std::vector< GScipLinearExpr > &terms, absl::string_view name)
Definition gscip_ext.cc:138
GScipLinearExpr GScipNegate(GScipLinearExpr expr)
Returns -expr.
Definition gscip_ext.cc:52
GScipLinearRange GScipLe(const GScipLinearExpr left, const GScipLinearExpr &right)
Definition gscip_ext.cc:62
absl::Status GScipCreateAbs(GScip *gscip, SCIP_Var *x, SCIP_Var *abs_x, absl::string_view name)
Definition gscip_ext.cc:75
absl::Status GScipCreateMaximum(GScip *gscip, const GScipLinearExpr &resultant, const std::vector< GScipLinearExpr > &terms, absl::string_view name)
Definition gscip_ext.cc:82
absl::Status GScipCreateIndicatorRange(GScip *gscip, const GScipIndicatorRangeConstraint &indicator_range, absl::string_view name, const GScipConstraintOptions &options)
Supports unbounded variables in indicator_range.range.variables.
Definition gscip_ext.cc:179
absl::Status GScipAddQuadraticObjectiveTerm(GScip *gscip, std::vector< SCIP_Var * > quadratic_variables1, std::vector< SCIP_Var * > quadratic_variables2, std::vector< double > quadratic_coefficients, absl::string_view name)
Definition gscip_ext.cc:149
const Variable x
Definition qp_tests.cc:127
const std::optional< Range > & range
Definition statistics.cc:37
absl::flat_hash_map< SCIP_VAR *, double > terms
Definition gscip_ext.h:51