Files
ortools-clone/ortools/linear_solver/linear_expr.h

226 lines
7.6 KiB
C
Raw Permalink Normal View History

2025-01-10 11:35:44 +01:00
// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2025-11-05 11:34:49 +01:00
#ifndef ORTOOLS_LINEAR_SOLVER_LINEAR_EXPR_H_
#define ORTOOLS_LINEAR_SOLVER_LINEAR_EXPR_H_
2019-07-15 17:42:24 -07:00
/**
* \file
* This file allows you to write natural code (like a mathematical equation) to
* model optimization problems with MPSolver. It is syntactic sugar on top of
2019-07-15 17:42:24 -07:00
* the MPSolver API, it provides no additional functionality. Use of these APIs
* makes it much easier to write code that is both simple and big-O optimal for
* creating your model, at the cost of some additional constant factor
* overhead. If model creation is a bottleneck in your problem, consider using
* the MPSolver API directly instead.
*
* This file contains two classes:
* 1. LinearExpr: models offset + sum_{i in S} a_i*x_i for decision var x_i,
* 2. LinearRange: models lb <= sum_{i in S} a_i*x_i <= ub,
* and it provides various operator overloads to build up "LinearExpr"s and
* then convert them to "LinearRange"s.
*
* Recommended use (avoids dangerous code):
*
* \code
MPSolver solver = ...;
const LinearExpr x = solver.MakeVar(...); * Note: implicit conversion
const LinearExpr y = solver.MakeVar(...);
const LinearExpr z = solver.MakeVar(...);
const LinearExpr e1 = x + y;
const LinearExpr e2 = (e1 + 7.0 + z)/3.0;
const LinearRange r = e1 <= e2;
solver.MakeRowConstraint(r);
\endcode
*
2019-07-16 10:13:02 -07:00
* \b WARNING, AVOID THIS TRAP:
2019-07-15 17:42:24 -07:00
*
* \code
MPSolver solver = ...;
MPVariable* x = solver.MakeVar(...);
LinearExpr y = x + 5;
\endcode
*
* In evaluating "x+5" above, x is NOT converted to a LinearExpr before the
* addition, but rather is treated as a pointer, so x+5 gives a new pointer to
* garbage memory.
*
* For this reason, when using LinearExpr, it is best practice to:
* 1. use double literals instead of ints (e.g. "x + 5.0", not "x + 5"),
* 2. Immediately convert all MPVariable* to LinearExpr on creation, and only
* hold references to the "LinearExpr"s.
*
* Likewise, the following code is NOT recommended:
* \code
MPSolver solver = ...;
MPVariable* x = solver.MakeVar(...);
MPVariable* y = solver.MakeVar(...);
LinearExpr e1 = LinearExpr(x) + y + 5;
\endcode
*
* While it is correct, it violates the natural assumption that the + operator
* is associative. Thus you are setting a trap for future modifications of the
* code, as any of the following changes would lead to the above failure mode:
*
2019-07-16 10:13:02 -07:00
* * \code LinearExpr e1 = LinearExpr(x) + (y + 5); \endcode
* * \code LinearExpr e1 = y + 5 + LinearExpr(x); \endcode
2019-07-15 17:42:24 -07:00
*/
#include <ostream>
#include <string>
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
#include "absl/container/flat_hash_map.h"
namespace operations_research {
// NOTE(user): forward declaration is necessary due to cyclic dependencies,
// MPVariable is defined in linear_solver.h, which depends on LinearExpr.
class MPVariable;
2019-07-15 17:42:24 -07:00
/**
* LinearExpr models a quantity that is linear in the decision variables
* (MPVariable) of an optimization problem, i.e.
*
* offset + sum_{i in S} a_i*x_i,
*
* where the a_i and offset are constants and the x_i are MPVariables. You can
* use a LinearExpr "linear_expr" with an MPSolver "solver" to:
* * Set as the objective of your optimization problem, e.g.
*
* solver.MutableObjective()->MaximizeLinearExpr(linear_expr);
*
* * Create a constraint in your optimization, e.g.
*
* solver.MakeRowConstraint(linear_expr1 <= linear_expr2);
*
* * Get the value of the quantity after solving, e.g.
*
* solver.Solve();
* linear_expr.SolutionValue();
*
* LinearExpr is allowed to delete variables with coefficient zero from the map,
* but is not obligated to do so.
*/
class LinearExpr {
2020-10-22 23:36:58 +02:00
public:
LinearExpr();
2019-07-16 10:13:02 -07:00
/// Possible implicit conversions are intentional.
2020-10-22 23:36:58 +02:00
LinearExpr(double constant); // NOLINT
2019-07-15 17:42:24 -07:00
/***
* Possible implicit conversions are intentional.
2019-07-15 17:55:13 -07:00
*
2019-07-15 17:42:24 -07:00
* Warning: var is not owned.
*/
2020-10-29 14:25:39 +01:00
LinearExpr(const MPVariable* var); // NOLINT
2019-07-15 17:42:24 -07:00
/**
* Returns 1-var.
2019-07-15 17:55:13 -07:00
*
2019-07-15 17:42:24 -07:00
* NOTE(user): if var is binary variable, this corresponds to the logical
* negation of var.
* Passing by value is intentional, see the discussion on binary ops.
*/
static LinearExpr NotVar(LinearExpr var);
2020-10-29 14:25:39 +01:00
LinearExpr& operator+=(const LinearExpr& rhs);
LinearExpr& operator-=(const LinearExpr& rhs);
LinearExpr& operator*=(double rhs);
LinearExpr& operator/=(double rhs);
LinearExpr operator-() const;
double offset() const { return offset_; }
2020-10-29 14:25:39 +01:00
const absl::flat_hash_map<const MPVariable*, double>& terms() const {
2017-04-28 16:17:22 +02:00
return terms_;
}
2019-07-15 17:42:24 -07:00
/**
* Evaluates the value of this expression at the solution found.
2019-07-15 17:55:13 -07:00
*
2019-07-15 17:42:24 -07:00
* It must be called only after calling MPSolver::Solve.
*/
double SolutionValue() const;
/**
* A human readable representation of this. Variables will be printed in order
* of lowest index first.
*/
std::string ToString() const;
2020-10-22 23:36:58 +02:00
private:
double offset_;
2020-10-29 14:25:39 +01:00
absl::flat_hash_map<const MPVariable*, double> terms_;
};
2020-10-29 14:25:39 +01:00
std::ostream& operator<<(std::ostream& stream, const LinearExpr& linear_expr);
// NOTE(user): in the ops below, the non-"const LinearExpr&" are intentional.
// We need to create a new LinearExpr for the result, so we lose nothing by
// passing one argument by value, mutating it, and then returning it. In
// particular, this allows (with move semantics and RVO) an optimized
// evaluation of expressions such as
// a + b + c + d
// (see http://en.cppreference.com/w/cpp/language/operators).
2020-10-29 14:25:39 +01:00
LinearExpr operator+(LinearExpr lhs, const LinearExpr& rhs);
LinearExpr operator-(LinearExpr lhs, const LinearExpr& rhs);
LinearExpr operator*(LinearExpr lhs, double rhs);
LinearExpr operator/(LinearExpr lhs, double rhs);
LinearExpr operator*(double lhs, LinearExpr rhs);
2019-07-15 17:42:24 -07:00
/**
* An expression of the form:
2019-07-15 17:55:13 -07:00
*
2019-07-15 17:42:24 -07:00
* \code lower_bound <= sum_{i in S} a_i*x_i <= upper_bound. \endcode
* The sum is represented as a LinearExpr with offset 0.
*
* Must be added to model with
2019-07-16 10:13:02 -07:00
* \code
MPSolver::AddRowConstraint(const LinearRange& range,
const std::string& name);
\endcode
2019-07-15 17:42:24 -07:00
*/
class LinearRange {
2020-10-22 23:36:58 +02:00
public:
LinearRange() : lower_bound_(0), upper_bound_(0) {}
2019-07-16 10:13:02 -07:00
/**
* The bounds of the linear range are updated so that they include the offset
* from "linear_expr", i.e., we form the range:
* \code
lower_bound - offset <= linear_expr - offset <= upper_bound - offset.
\endcode
*/
2020-10-29 14:25:39 +01:00
LinearRange(double lower_bound, const LinearExpr& linear_expr,
double upper_bound);
double lower_bound() const { return lower_bound_; }
2020-10-29 14:25:39 +01:00
const LinearExpr& linear_expr() const { return linear_expr_; }
double upper_bound() const { return upper_bound_; }
2020-10-22 23:36:58 +02:00
private:
double lower_bound_;
// invariant: linear_expr_.offset() == 0.
LinearExpr linear_expr_;
double upper_bound_;
};
2020-10-29 14:25:39 +01:00
LinearRange operator<=(const LinearExpr& lhs, const LinearExpr& rhs);
LinearRange operator==(const LinearExpr& lhs, const LinearExpr& rhs);
LinearRange operator>=(const LinearExpr& lhs, const LinearExpr& rhs);
2021-09-13 13:10:16 +02:00
// TODO(user): explore defining more overloads to support:
// solver.AddRowConstraint(0.0 <= x + y + z <= 1.0);
2020-10-22 23:36:58 +02:00
} // namespace operations_research
2025-11-05 11:34:49 +01:00
#endif // ORTOOLS_LINEAR_SOLVER_LINEAR_EXPR_H_