20#include "absl/algorithm/container.h"
21#include "absl/status/status.h"
22#include "absl/status/statusor.h"
23#include "absl/strings/str_cat.h"
24#include "absl/strings/str_format.h"
25#include "absl/strings/str_join.h"
35absl::Status ValidateOptions(
const FeasibilityCheckerOptions& options) {
36 const auto tolerance_is_valid = [](
const double tolerance) {
37 return tolerance >= 0 && !std::isnan(tolerance);
39 if (!tolerance_is_valid(options.absolute_constraint_tolerance)) {
41 <<
"invalid absolute_constraint_tolerance value: "
42 << options.absolute_constraint_tolerance;
44 if (!tolerance_is_valid(options.integrality_tolerance)) {
46 <<
"invalid integrality_tolerance value: "
47 << options.integrality_tolerance;
49 if (!tolerance_is_valid(options.nonzero_tolerance)) {
51 <<
"invalid nonzero_tolerance value: " << options.nonzero_tolerance;
53 return absl::OkStatus();
56bool IsNearlyLessThan(
const double lhs,
const double rhs,
57 const double absolute_tolerance) {
58 return lhs <= rhs + absolute_tolerance;
61bool IsNearlyEqualTo(
const double actual,
const double target,
62 const double absolute_tolerance) {
63 return std::fabs(actual - target) <= absolute_tolerance;
66absl::Status ValidateVariables(
const Model&
model,
68 for (
const Variable variable :
model.Variables()) {
69 if (!variable_values.contains(variable)) {
71 <<
"Variable present in `model` but not `variable_values`: "
75 for (
const auto [variable, unused] : variable_values) {
76 if (variable.storage() !=
model.storage()) {
78 <<
"Variable present in `variable_values` but not `model`: "
82 return absl::OkStatus();
85ModelSubset::Bounds CheckBoundedConstraint(
87 const FeasibilityCheckerOptions& options) {
88 return {.lower = !IsNearlyLessThan(
lower_bound, expr_value,
89 options.absolute_constraint_tolerance),
91 options.absolute_constraint_tolerance)};
95ModelSubset::Bounds CheckVariableBounds(
97 const FeasibilityCheckerOptions& options) {
98 return CheckBoundedConstraint(variable_values.at(variable),
99 variable.lower_bound(), variable.upper_bound(),
104ModelSubset::Bounds CheckLinearConstraint(
105 const LinearConstraint constraint,
107 const FeasibilityCheckerOptions& options) {
108 const BoundedLinearExpression bounded_expr =
109 constraint.AsBoundedLinearExpression();
110 return CheckBoundedConstraint(
111 bounded_expr.expression.Evaluate(variable_values),
112 bounded_expr.lower_bound, bounded_expr.upper_bound, options);
116ModelSubset::Bounds CheckQuadraticConstraint(
117 const QuadraticConstraint constraint,
119 const FeasibilityCheckerOptions& options) {
120 const BoundedQuadraticExpression bounded_expr =
121 constraint.AsBoundedQuadraticExpression();
122 return CheckBoundedConstraint(
123 bounded_expr.expression.Evaluate(variable_values),
124 bounded_expr.lower_bound, bounded_expr.upper_bound, options);
128bool CheckSecondOrderConeConstraint(
const SecondOrderConeConstraint constraint,
130 const FeasibilityCheckerOptions& options) {
131 double args_to_norm_value = 0.0;
132 for (
const LinearExpression& expr : constraint.ArgumentsToNorm()) {
135 args_to_norm_value +=
MathUtil::IPow(expr.Evaluate(variable_values), 2);
137 return IsNearlyLessThan(std::sqrt(args_to_norm_value),
138 constraint.UpperBound().Evaluate(variable_values),
139 options.absolute_constraint_tolerance);
143bool CheckSos1Constraint(
const Sos1Constraint constraint,
145 const FeasibilityCheckerOptions& options) {
147 bool previous_nonzero =
false;
148 for (
int i = 0;
i < constraint.num_expressions(); ++
i) {
149 const bool expr_is_nonzero =
150 !IsNearlyEqualTo(constraint.Expression(i).Evaluate(variable_values),
151 0.0, options.nonzero_tolerance);
152 if (expr_is_nonzero && previous_nonzero) {
156 previous_nonzero |= expr_is_nonzero;
162bool CheckSos2Constraint(
const Sos2Constraint constraint,
164 const FeasibilityCheckerOptions& options) {
166 bool preceding_was_nonzero =
false;
168 bool any_other_before_was_nonzero =
false;
169 for (
int i = 0;
i < constraint.num_expressions(); ++
i) {
170 const bool expr_is_nonzero =
171 !IsNearlyEqualTo(constraint.Expression(i).Evaluate(variable_values),
172 0.0, options.nonzero_tolerance);
173 if (expr_is_nonzero && any_other_before_was_nonzero) {
179 any_other_before_was_nonzero |= preceding_was_nonzero;
180 preceding_was_nonzero = expr_is_nonzero;
187bool CheckIndicatorConstraint(
const IndicatorConstraint constraint,
189 const FeasibilityCheckerOptions& options) {
190 if (!constraint.indicator_variable().has_value()) {
194 if (!IsNearlyEqualTo(variable_values.at(*constraint.indicator_variable()),
195 constraint.activate_on_zero() ? 0.0 : 1.0,
196 options.nonzero_tolerance)) {
201 const BoundedLinearExpression bounded_expr = constraint.ImpliedConstraint();
205 return CheckBoundedConstraint(
206 bounded_expr.expression.Evaluate(variable_values),
207 bounded_expr.lower_bound, bounded_expr.upper_bound, options)
222 CheckVariableBounds(variable, variable_values, options);
223 if (!violations.
empty()) {
226 if (variable.is_integer()) {
227 const double variable_value = variable_values.at(variable);
228 const double rounded_variable_value = std::round(variable_value);
229 if (std::fabs(rounded_variable_value - variable_value) >
238 CheckLinearConstraint(linear_constraint, variable_values, options);
239 if (!violations.
empty()) {
245 model.QuadraticConstraints()) {
247 quadratic_constraint, variable_values, options);
248 if (!violations.
empty()) {
255 model.SecondOrderConeConstraints()) {
256 if (!CheckSecondOrderConeConstraint(soc_constraint, variable_values,
263 if (!CheckSos1Constraint(sos1_constraint, variable_values, options)) {
269 if (!CheckSos2Constraint(sos2_constraint, variable_values, options)) {
275 model.IndicatorConstraints()) {
276 if (!CheckIndicatorConstraint(indicator_constraint, variable_values,
281 return violated_constraints;
287std::string VariableValuesAsString(std::vector<Variable> variables,
289 absl::c_sort(variables, [](
const Variable lhs,
const Variable rhs) {
290 return lhs.typed_id() < rhs.typed_id();
294 absl::StrJoin(variables,
", ",
295 [&](std::string*
const out,
const Variable variable) {
296 absl::StrAppendFormat(
297 out,
"{%s, %v}", absl::FormatStreamed(variable),
298 RoundTripDoubleFormat(variable_values.at(variable)));
305std::string ViolatedConstraintAsString(
307 const absl::string_view constraint_type) {
308 return absl::StrFormat(
309 "violated %s %s: %s, with variable values %s", constraint_type,
310 absl::FormatStreamed(violated_constraint), violated_constraint.ToString(),
311 VariableValuesAsString(violated_constraint.NonzeroVariables(),
317void AppendViolatedConstraintsAsStrings(
318 const std::vector<T>& violated_constraints,
320 const absl::string_view constraint_type, std::vector<std::string>& output) {
321 for (
const T violated_constraint : violated_constraints) {
322 output.push_back(ViolatedConstraintAsString(
323 violated_constraint, variable_values, constraint_type));
333 <<
"violated_constraints and model are inconsistent";
336 std::vector<std::string> result;
339 result.push_back(absl::StrFormat(
340 "violated variable bound: %v ≤ %s ≤ %v, with variable value %v",
342 absl::FormatStreamed(variable),
348 result.push_back(absl::StrFormat(
349 "violated variable integrality: %s, with variable value %v",
350 absl::FormatStreamed(variable),
355 result.push_back(absl::StrFormat(
356 "violated linear constraint %s: %s, with variable values %s",
357 absl::FormatStreamed(linear_constraint), linear_constraint.ToString(),
358 VariableValuesAsString(
model.RowNonzeros(linear_constraint),
361 AppendViolatedConstraintsAsStrings(
363 "quadratic constraint", result);
364 AppendViolatedConstraintsAsStrings(
366 variable_values,
"second-order cone constraint", result);
367 AppendViolatedConstraintsAsStrings(
369 "SOS1 constraint", result);
370 AppendViolatedConstraintsAsStrings(
372 "SOS2 constraint", result);
373 AppendViolatedConstraintsAsStrings(
375 variable_values,
"indicator constraint", result);
#define RETURN_IF_ERROR(expr)
static T IPow(T base, int exp)
An object oriented wrapper for quadratic constraints in ModelStorage.
absl::flat_hash_map< Variable, V > VariableMap
absl::StatusOr< std::vector< std::string > > ViolatedConstraintsAsStrings(const Model &model, const ModelSubset &violated_constraints, const VariableMap< double > &variable_values)
std::vector< typename Set::key_type > SortedElements(const Set &set)
absl::StatusOr< ModelSubset > CheckPrimalSolutionFeasibility(const Model &model, const VariableMap< double > &variable_values, const FeasibilityCheckerOptions &options)
std::vector< typename Map::key_type > SortedKeys(const Map &map)
StatusBuilder InvalidArgumentErrorBuilder()
double integrality_tolerance
absl::flat_hash_map< Variable, Bounds > variable_bounds
absl::flat_hash_set< SecondOrderConeConstraint > second_order_cone_constraints
absl::flat_hash_set< Variable > variable_integrality
absl::flat_hash_set< Sos1Constraint > sos1_constraints
absl::Status CheckModelStorage(const ModelStorage *expected_storage) const
absl::flat_hash_map< LinearConstraint, Bounds > linear_constraints
absl::flat_hash_set< IndicatorConstraint > indicator_constraints
absl::flat_hash_map< QuadraticConstraint, Bounds > quadratic_constraints
absl::flat_hash_set< Sos2Constraint > sos2_constraints