2019-11-14 12:41:44 -08:00
|
|
|
| [home](README.md) | [boolean logic](boolean_logic.md) | [integer arithmetic](integer_arithmetic.md) | [channeling constraints](channeling.md) | [scheduling](scheduling.md) | [Using the CP-SAT solver](solver.md) | [Model manipulation](model.md) | [Python API](https://google.github.io/or-tools/python/ortools/sat/python/cp_model.html) |
|
2019-06-05 11:07:46 +02:00
|
|
|
| ----------------- | --------------------------------- | ------------------------------------------- | --------------------------------------- | --------------------------- | ------------------------------------ | ------------------------------ | -------------------------------- |
|
2019-03-15 11:45:46 +01:00
|
|
|
|
2018-05-22 15:07:11 +02:00
|
|
|
# Integer arithmetic recipes for the CP-SAT solver.
|
|
|
|
|
|
2021-09-03 15:25:40 +02:00
|
|
|
https://developers.google.com/optimization/
|
2018-05-22 15:07:11 +02:00
|
|
|
|
2019-06-07 10:22:49 +02:00
|
|
|
<!--ts-->
|
2022-02-23 17:35:20 +01:00
|
|
|
* [Integer arithmetic recipes for the CP-SAT solver.](#integer-arithmetic-recipes-for-the-cp-sat-solver)
|
|
|
|
|
* [Introduction](#introduction)
|
|
|
|
|
* [Integer variables](#integer-variables)
|
|
|
|
|
* [Interval domain](#interval-domain)
|
|
|
|
|
* [Non-contiguous domain](#non-contiguous-domain)
|
|
|
|
|
* [Boolean variables](#boolean-variables)
|
|
|
|
|
* [Other methods](#other-methods)
|
|
|
|
|
* [Linear constraints](#linear-constraints)
|
|
|
|
|
* [C++ and Java linear constraints and linear expressions](#c-and-java-linear-constraints-and-linear-expressions)
|
|
|
|
|
* [Python and C# linear constraints and linear expressions](#python-and-c-linear-constraints-and-linear-expressions)
|
|
|
|
|
* [Generic linear constraint](#generic-linear-constraint)
|
|
|
|
|
* [Limitations](#limitations)
|
|
|
|
|
* [Rabbits and Pheasants examples](#rabbits-and-pheasants-examples)
|
|
|
|
|
* [Python code](#python-code)
|
|
|
|
|
* [C++ code](#c-code)
|
|
|
|
|
* [Java code](#java-code)
|
|
|
|
|
* [C# code](#c-code-1)
|
|
|
|
|
* [Earliness-Tardiness cost function.](#earliness-tardiness-cost-function)
|
|
|
|
|
* [Python code](#python-code-1)
|
|
|
|
|
* [C++ code](#c-code-2)
|
|
|
|
|
* [Java code](#java-code-1)
|
|
|
|
|
* [C# code](#c-code-3)
|
|
|
|
|
* [Step function.](#step-function)
|
|
|
|
|
* [Python code](#python-code-2)
|
|
|
|
|
* [C++ code](#c-code-4)
|
|
|
|
|
* [Java code](#java-code-2)
|
|
|
|
|
* [C# code](#c-code-5)
|
2022-01-03 18:57:09 +01:00
|
|
|
|
|
|
|
|
|
2019-06-07 10:22:49 +02:00
|
|
|
<!--te-->
|
|
|
|
|
|
2018-05-22 15:07:11 +02:00
|
|
|
|
|
|
|
|
## Introduction
|
2018-05-22 17:13:52 +02:00
|
|
|
|
2018-05-24 14:30:14 +02:00
|
|
|
The CP-SAT solver can express integer variables and constraints.
|
2018-06-05 15:56:55 +02:00
|
|
|
|
|
|
|
|
## Integer variables
|
|
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
Integer variables can take on 64-bit signed integer values. When creating them,
|
|
|
|
|
a domain must be provided.
|
2019-05-07 08:31:53 +02:00
|
|
|
|
|
|
|
|
### Interval domain
|
|
|
|
|
|
|
|
|
|
To create a single contiguous integer domain, just call the `NewIntVar` method
|
2019-05-07 16:41:00 +02:00
|
|
|
with the lower and upper bounds. For instance, to create a variable that can
|
|
|
|
|
take on any value between 0 and 10, inclusive:
|
2019-05-07 08:31:53 +02:00
|
|
|
|
|
|
|
|
- **C++**: `IntVar x = model.NewIntVar({0, 10}).WithName("x");`
|
|
|
|
|
- **Python**: `x = model.NewIntVar(0, 10, 'x')`
|
|
|
|
|
- **Java**: `IntVar x = model.newIntVar(0, 10, "x");`
|
|
|
|
|
- **C#**: `IntVar x = model.NewIntVar(0, 10, "x");`
|
|
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
### Non-contiguous domain
|
2019-05-07 08:31:53 +02:00
|
|
|
|
2019-05-16 16:50:11 +02:00
|
|
|
An instance of the Domain class is needed to create variables with
|
2019-05-07 16:41:00 +02:00
|
|
|
non-contiguous domains. Here, the variable can be any of 1, 3, 4, or 6:
|
2019-05-07 08:31:53 +02:00
|
|
|
|
|
|
|
|
- **C++**: `model.NewIntVar(Domain::FromValues({1, 3, 4, 6});`
|
|
|
|
|
- **Python**: `model.NewIntVarFromDomain(cp_model.Domain.FromValues([1, 3, 4,
|
|
|
|
|
6]), 'x')`
|
|
|
|
|
- **Java**: `model.newIntVarFromDomain(Domain.fromValues(new long[] {1, 3, 4,
|
|
|
|
|
6}), "x");`
|
|
|
|
|
- **C#**: `model.NewIntVarFromDomain(Domain.FromValues(new long[] {1, 3, 4,
|
|
|
|
|
6}), "x");`
|
|
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
Variables can also be created using a list of intervals. Below, the variable
|
|
|
|
|
created is constrained to be 1, 2, 4, 5, or 6:
|
2019-05-07 08:31:53 +02:00
|
|
|
|
|
|
|
|
- **C++**: `model.NewIntVar(Domain::FromIntervals({{1, 2}, {4, 6}});`
|
|
|
|
|
- **Python**: `model.NewIntVarFromDomain(cp_model.Domain.FromIntervals([[1,
|
|
|
|
|
2], [4, 6]]), 'x')`
|
|
|
|
|
- **Java**: `model.newIntVarFromDomain(Domain.fromIntervals(new long[][] {{1,
|
|
|
|
|
2}, {4, 6}}), "x");`
|
|
|
|
|
- **C#**: `model.NewIntVarFromDomain(Domain.FromIntervals(new long[][] { new
|
2019-05-07 16:41:00 +02:00
|
|
|
long[] {1, 2}, new long[] {4, 6} }), "x");`
|
2019-05-07 08:31:53 +02:00
|
|
|
|
|
|
|
|
### Boolean variables
|
|
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
To create a Boolean variable, use the `NewBoolVar` method. Please note that
|
|
|
|
|
Boolean variables are typed differently than integer variables, and that this
|
|
|
|
|
type is not uniform across languages.
|
2019-05-07 08:31:53 +02:00
|
|
|
|
|
|
|
|
- **C++**: `BoolVar x = model.NewBoolVar().WithName("x");`
|
|
|
|
|
- **Python**: `x = model.NewBoolVar('x')`
|
|
|
|
|
- **Java**: `Literal x = model.newBoolVar("x");`
|
|
|
|
|
- **C#**: `ILiteral x = model.NewBoolVar("x");`
|
|
|
|
|
|
|
|
|
|
### Other methods
|
|
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
To exclude a single value, use ranges combined with int64min and int64max
|
2022-01-04 19:35:22 +01:00
|
|
|
values, e.g., `[[int64min, -3], [-1, int64max]]`, or use the `Complement`
|
|
|
|
|
method.
|
2019-05-07 08:31:53 +02:00
|
|
|
|
|
|
|
|
To create a variable with a single value domain, use the `NewConstant()` API (or
|
|
|
|
|
`newConstant()` in Java).
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2018-06-05 15:56:55 +02:00
|
|
|
## Linear constraints
|
|
|
|
|
|
2019-05-07 08:31:53 +02:00
|
|
|
### C++ and Java linear constraints and linear expressions
|
|
|
|
|
|
|
|
|
|
**C++** and **Java** APIs do not use arithmetic operators (+, \*, -, <=...).
|
2022-01-04 19:35:22 +01:00
|
|
|
Linear constraints are created using a method of the model factory, such as
|
2019-05-07 16:41:00 +02:00
|
|
|
`cp_model.AddEquality(x, 3)` in C++, or `cp_model.addGreaterOrEqual(x, 10)` in
|
|
|
|
|
Java.
|
2018-06-05 15:56:55 +02:00
|
|
|
|
2019-05-07 08:31:53 +02:00
|
|
|
Furthermore, helper methods can be used to create sums and scalar products like
|
2022-01-04 19:35:22 +01:00
|
|
|
`LinearExpr::Sum({x, y, z})` in C++, and `LinearExpr.weightedSum(new IntVar[]
|
|
|
|
|
{x, y, z}, new long[] {1, 2, 3})` in Java.
|
2018-06-05 15:56:55 +02:00
|
|
|
|
2019-05-07 08:31:53 +02:00
|
|
|
### Python and C\# linear constraints and linear expressions
|
2019-05-03 16:30:50 +02:00
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
**Python** and **C\#** CP-SAT APIs support general linear arithmetic (+, \*, -,
|
|
|
|
|
==, >=, >, <, <=, !=). You need to use the Add method of the cp_model, as in
|
2019-05-07 08:31:53 +02:00
|
|
|
`cp_model.Add(x + y != 3)`.
|
2019-05-03 16:30:50 +02:00
|
|
|
|
2019-05-07 08:31:53 +02:00
|
|
|
### Generic linear constraint
|
2018-06-05 15:56:55 +02:00
|
|
|
|
2022-01-04 19:35:22 +01:00
|
|
|
in **all languages**, the cp_model factory offers a generic method to constrain
|
|
|
|
|
a linear expression to be in a domain. This is used in the step function
|
|
|
|
|
examples below.
|
2019-05-03 16:30:50 +02:00
|
|
|
|
2019-05-07 08:31:53 +02:00
|
|
|
### Limitations
|
2019-05-03 16:30:50 +02:00
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
- Everything must be linear. Multiplying two variables is not supported
|
|
|
|
|
with this API; instead, `model.AddProductEquality()` must be used.
|
2018-06-05 15:56:55 +02:00
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
- In C++, there is a typing issue when using an array of Boolean variables in
|
2019-05-07 08:31:53 +02:00
|
|
|
a sum or a scalar product. Use the `LinearExpr.BooleanSum()` method instead.
|
2018-06-05 15:56:55 +02:00
|
|
|
|
2019-05-07 16:41:00 +02:00
|
|
|
- The Python construct `sum()` is supported, but `min()`, `max()`
|
2019-05-07 08:31:53 +02:00
|
|
|
or any `numpy` constructs like `np.unique()` are not.
|
2018-06-05 15:56:55 +02:00
|
|
|
|
|
|
|
|
## Rabbits and Pheasants examples
|
|
|
|
|
|
2018-06-26 21:24:10 +02:00
|
|
|
Let's solve a simple children's puzzle: the Rabbits and Pheasants problem.
|
2018-06-05 15:56:55 +02:00
|
|
|
|
2019-03-25 11:26:14 +01:00
|
|
|
In a field of rabbits and pheasants, there are 20 heads and 56 legs. How many
|
2018-06-26 21:24:10 +02:00
|
|
|
rabbits and pheasants are there?
|
2018-06-05 15:56:55 +02:00
|
|
|
|
|
|
|
|
### Python code
|
|
|
|
|
|
2018-07-17 10:31:58 -07:00
|
|
|
```python
|
2018-07-16 18:40:14 -07:00
|
|
|
"""Rabbits and Pheasants quizz."""
|
|
|
|
|
|
2018-06-05 15:56:55 +02:00
|
|
|
from ortools.sat.python import cp_model
|
|
|
|
|
|
2018-07-16 18:40:14 -07:00
|
|
|
|
2018-11-28 10:37:45 +01:00
|
|
|
def RabbitsAndPheasantsSat():
|
2018-06-05 15:56:55 +02:00
|
|
|
"""Solves the rabbits + pheasants problem."""
|
|
|
|
|
model = cp_model.CpModel()
|
|
|
|
|
|
|
|
|
|
r = model.NewIntVar(0, 100, 'r')
|
|
|
|
|
p = model.NewIntVar(0, 100, 'p')
|
|
|
|
|
|
|
|
|
|
# 20 heads.
|
|
|
|
|
model.Add(r + p == 20)
|
|
|
|
|
# 56 legs.
|
|
|
|
|
model.Add(4 * r + 2 * p == 56)
|
|
|
|
|
|
2018-07-16 18:40:14 -07:00
|
|
|
# Solves and prints out the solution.
|
2018-06-05 15:56:55 +02:00
|
|
|
solver = cp_model.CpSolver()
|
|
|
|
|
status = solver.Solve(model)
|
|
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
if status == cp_model.OPTIMAL:
|
2018-06-05 15:56:55 +02:00
|
|
|
print('%i rabbits and %i pheasants' % (solver.Value(r), solver.Value(p)))
|
2018-07-16 18:40:14 -07:00
|
|
|
|
|
|
|
|
|
2018-11-28 10:37:45 +01:00
|
|
|
RabbitsAndPheasantsSat()
|
2018-06-05 15:56:55 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### C++ code
|
|
|
|
|
|
2018-07-17 10:31:58 -07:00
|
|
|
```cpp
|
2022-02-15 18:00:11 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#include "ortools/base/logging.h"
|
2018-11-05 16:24:47 +01:00
|
|
|
#include "ortools/sat/cp_model.h"
|
2022-02-15 18:00:11 +01:00
|
|
|
#include "ortools/sat/cp_model.pb.h"
|
|
|
|
|
#include "ortools/sat/cp_model_solver.h"
|
|
|
|
|
#include "ortools/util/sorted_interval_list.h"
|
2018-06-05 15:56:55 +02:00
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
namespace sat {
|
|
|
|
|
|
2018-11-28 10:37:45 +01:00
|
|
|
void RabbitsAndPheasantsSat() {
|
2018-11-05 16:24:47 +01:00
|
|
|
CpModelBuilder cp_model;
|
|
|
|
|
|
|
|
|
|
const Domain all_animals(0, 20);
|
|
|
|
|
const IntVar rabbits = cp_model.NewIntVar(all_animals).WithName("rabbits");
|
|
|
|
|
const IntVar pheasants =
|
|
|
|
|
cp_model.NewIntVar(all_animals).WithName("pheasants");
|
|
|
|
|
|
2021-12-09 15:29:49 +01:00
|
|
|
cp_model.AddEquality(rabbits + pheasants, 20);
|
|
|
|
|
cp_model.AddEquality(4 * rabbits + 2 * pheasants, 56);
|
2018-11-05 16:24:47 +01:00
|
|
|
|
2019-04-05 14:58:33 +02:00
|
|
|
const CpSolverResponse response = Solve(cp_model.Build());
|
2018-06-05 15:56:55 +02:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
if (response.status() == CpSolverStatus::OPTIMAL) {
|
2018-06-05 15:56:55 +02:00
|
|
|
// Get the value of x in the solution.
|
2018-11-05 16:24:47 +01:00
|
|
|
LOG(INFO) << SolutionIntegerValue(response, rabbits) << " rabbits, and "
|
|
|
|
|
<< SolutionIntegerValue(response, pheasants) << " pheasants";
|
2018-06-05 15:56:55 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-16 18:40:14 -07:00
|
|
|
} // namespace sat
|
|
|
|
|
} // namespace operations_research
|
|
|
|
|
|
|
|
|
|
int main() {
|
2018-11-28 10:37:45 +01:00
|
|
|
operations_research::sat::RabbitsAndPheasantsSat();
|
2018-07-16 18:40:14 -07:00
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
2018-06-05 15:56:55 +02:00
|
|
|
```
|
|
|
|
|
|
2018-08-01 17:16:44 -07:00
|
|
|
### Java code
|
|
|
|
|
|
|
|
|
|
```java
|
2020-06-25 18:31:47 +02:00
|
|
|
package com.google.ortools.sat.samples;
|
|
|
|
|
|
2020-11-03 18:40:54 +01:00
|
|
|
import com.google.ortools.Loader;
|
2018-08-03 16:42:45 -07:00
|
|
|
import com.google.ortools.sat.CpSolverStatus;
|
|
|
|
|
import com.google.ortools.sat.CpModel;
|
|
|
|
|
import com.google.ortools.sat.CpSolver;
|
|
|
|
|
import com.google.ortools.sat.IntVar;
|
2019-05-06 22:21:53 +02:00
|
|
|
import com.google.ortools.sat.LinearExpr;
|
2018-08-01 17:16:44 -07:00
|
|
|
|
2018-08-28 11:19:49 +02:00
|
|
|
/**
|
|
|
|
|
* In a field of rabbits and pheasants, there are 20 heads and 56 legs. How many rabbits and
|
|
|
|
|
* pheasants are there?
|
|
|
|
|
*/
|
2018-11-28 10:37:45 +01:00
|
|
|
public class RabbitsAndPheasantsSat {
|
2018-08-03 16:42:45 -07:00
|
|
|
public static void main(String[] args) throws Exception {
|
2020-11-03 18:40:54 +01:00
|
|
|
Loader.loadNativeLibraries();
|
2018-08-01 17:16:44 -07:00
|
|
|
// Creates the model.
|
|
|
|
|
CpModel model = new CpModel();
|
|
|
|
|
// Creates the variables.
|
|
|
|
|
IntVar r = model.newIntVar(0, 100, "r");
|
|
|
|
|
IntVar p = model.newIntVar(0, 100, "p");
|
|
|
|
|
// 20 heads.
|
2022-01-04 16:30:04 +01:00
|
|
|
model.addEquality(LinearExpr.newBuilder().add(r).add(p), 20);
|
2018-08-01 17:16:44 -07:00
|
|
|
// 56 legs.
|
2022-01-04 16:30:04 +01:00
|
|
|
model.addEquality(LinearExpr.newBuilder().addTerm(r, 4).addTerm(p, 2), 56);
|
2018-08-01 17:16:44 -07:00
|
|
|
|
|
|
|
|
// Creates a solver and solves the model.
|
|
|
|
|
CpSolver solver = new CpSolver();
|
|
|
|
|
CpSolverStatus status = solver.solve(model);
|
|
|
|
|
|
2020-11-16 17:50:35 +01:00
|
|
|
if (status == CpSolverStatus.OPTIMAL) {
|
2018-08-03 16:42:45 -07:00
|
|
|
System.out.println(solver.value(r) + " rabbits, and " + solver.value(p) + " pheasants");
|
2018-08-01 17:16:44 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-05 15:56:55 +02:00
|
|
|
### C\# code
|
|
|
|
|
|
|
|
|
|
```cs
|
|
|
|
|
using System;
|
|
|
|
|
using Google.OrTools.Sat;
|
|
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
public class RabbitsAndPheasantsSat
|
|
|
|
|
{
|
|
|
|
|
static void Main()
|
2018-06-05 15:56:55 +02:00
|
|
|
{
|
2020-11-03 14:01:06 +01:00
|
|
|
// Creates the model.
|
|
|
|
|
CpModel model = new CpModel();
|
|
|
|
|
// Creates the variables.
|
|
|
|
|
IntVar r = model.NewIntVar(0, 100, "r");
|
|
|
|
|
IntVar p = model.NewIntVar(0, 100, "p");
|
|
|
|
|
// 20 heads.
|
|
|
|
|
model.Add(r + p == 20);
|
|
|
|
|
// 56 legs.
|
|
|
|
|
model.Add(4 * r + 2 * p == 56);
|
|
|
|
|
|
|
|
|
|
// Creates a solver and solves the model.
|
|
|
|
|
CpSolver solver = new CpSolver();
|
|
|
|
|
CpSolverStatus status = solver.Solve(model);
|
|
|
|
|
|
|
|
|
|
if (status == CpSolverStatus.Optimal)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(solver.Value(r) + " rabbits, and " + solver.Value(p) + " pheasants");
|
|
|
|
|
}
|
2018-06-05 15:56:55 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-10 19:29:40 +01:00
|
|
|
|
2018-06-05 15:56:55 +02:00
|
|
|
```
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
## Earliness-Tardiness cost function.
|
|
|
|
|
|
|
|
|
|
Let's encode a useful convex piecewise linear function that often appears in
|
|
|
|
|
scheduling. You want to encourage a delivery to happen during a time window. If
|
|
|
|
|
you deliver early, you pay a linear penalty on waiting time. If you deliver
|
|
|
|
|
late, you pay a linear penalty on the delay.
|
|
|
|
|
|
|
|
|
|
Because the function is convex, you can define all affine functions, and take
|
|
|
|
|
the max of them to define the piecewise linear function.
|
|
|
|
|
|
|
|
|
|
The following samples output:
|
|
|
|
|
|
|
|
|
|
x=0 expr=40
|
|
|
|
|
x=1 expr=32
|
|
|
|
|
x=2 expr=24
|
|
|
|
|
x=3 expr=16
|
|
|
|
|
x=4 expr=8
|
|
|
|
|
x=5 expr=0
|
|
|
|
|
x=6 expr=0
|
|
|
|
|
x=7 expr=0
|
|
|
|
|
x=8 expr=0
|
|
|
|
|
x=9 expr=0
|
|
|
|
|
x=10 expr=0
|
|
|
|
|
x=11 expr=0
|
|
|
|
|
x=12 expr=0
|
|
|
|
|
x=13 expr=0
|
|
|
|
|
x=14 expr=0
|
|
|
|
|
x=15 expr=0
|
|
|
|
|
x=16 expr=12
|
|
|
|
|
x=17 expr=24
|
|
|
|
|
x=18 expr=36
|
|
|
|
|
x=19 expr=48
|
|
|
|
|
x=20 expr=60
|
|
|
|
|
|
|
|
|
|
### Python code
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
"""Encodes an convex piecewise linear function."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from ortools.sat.python import cp_model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):
|
|
|
|
|
"""Print intermediate solutions."""
|
|
|
|
|
|
|
|
|
|
def __init__(self, variables):
|
|
|
|
|
cp_model.CpSolverSolutionCallback.__init__(self)
|
|
|
|
|
self.__variables = variables
|
|
|
|
|
self.__solution_count = 0
|
|
|
|
|
|
|
|
|
|
def on_solution_callback(self):
|
|
|
|
|
self.__solution_count += 1
|
|
|
|
|
for v in self.__variables:
|
|
|
|
|
print('%s=%i' % (v, self.Value(v)), end=' ')
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
def solution_count(self):
|
|
|
|
|
return self.__solution_count
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def earliness_tardiness_cost_sample_sat():
|
|
|
|
|
"""Encode the piecewise linear expression."""
|
|
|
|
|
|
|
|
|
|
earliness_date = 5 # ed.
|
|
|
|
|
earliness_cost = 8
|
|
|
|
|
lateness_date = 15 # ld.
|
|
|
|
|
lateness_cost = 12
|
|
|
|
|
|
|
|
|
|
# Model.
|
|
|
|
|
model = cp_model.CpModel()
|
|
|
|
|
|
|
|
|
|
# Declare our primary variable.
|
|
|
|
|
x = model.NewIntVar(0, 20, 'x')
|
|
|
|
|
|
|
|
|
|
# Create the expression variable and implement the piecewise linear function.
|
|
|
|
|
#
|
|
|
|
|
# \ /
|
|
|
|
|
# \______/
|
|
|
|
|
# ed ld
|
|
|
|
|
#
|
|
|
|
|
large_constant = 1000
|
|
|
|
|
expr = model.NewIntVar(0, large_constant, 'expr')
|
|
|
|
|
|
|
|
|
|
# First segment.
|
|
|
|
|
s1 = model.NewIntVar(-large_constant, large_constant, 's1')
|
|
|
|
|
model.Add(s1 == earliness_cost * (earliness_date - x))
|
|
|
|
|
|
|
|
|
|
# Second segment.
|
|
|
|
|
s2 = 0
|
|
|
|
|
|
|
|
|
|
# Third segment.
|
|
|
|
|
s3 = model.NewIntVar(-large_constant, large_constant, 's3')
|
|
|
|
|
model.Add(s3 == lateness_cost * (x - lateness_date))
|
|
|
|
|
|
|
|
|
|
# Link together expr and x through s1, s2, and s3.
|
|
|
|
|
model.AddMaxEquality(expr, [s1, s2, s3])
|
|
|
|
|
|
|
|
|
|
# Search for x values in increasing order.
|
|
|
|
|
model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST,
|
|
|
|
|
cp_model.SELECT_MIN_VALUE)
|
|
|
|
|
|
|
|
|
|
# Create a solver and solve with a fixed search.
|
|
|
|
|
solver = cp_model.CpSolver()
|
|
|
|
|
|
|
|
|
|
# Force the solver to follow the decision strategy exactly.
|
|
|
|
|
solver.parameters.search_branching = cp_model.FIXED_SEARCH
|
2021-05-03 12:11:39 +02:00
|
|
|
# Enumerate all solutions.
|
|
|
|
|
solver.parameters.enumerate_all_solutions = True
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
# Search and print out all solutions.
|
|
|
|
|
solution_printer = VarArraySolutionPrinter([x, expr])
|
2021-05-03 12:11:39 +02:00
|
|
|
solver.Solve(model, solution_printer)
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
earliness_tardiness_cost_sample_sat()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### C++ code
|
|
|
|
|
|
|
|
|
|
```cpp
|
2022-02-15 18:00:11 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
2021-03-04 18:26:01 +01:00
|
|
|
#include <cstdint>
|
|
|
|
|
|
2022-02-15 18:00:11 +01:00
|
|
|
#include "ortools/base/logging.h"
|
|
|
|
|
#include "absl/types/span.h"
|
2019-03-25 11:26:14 +01:00
|
|
|
#include "ortools/sat/cp_model.h"
|
2022-02-15 18:00:11 +01:00
|
|
|
#include "ortools/sat/cp_model.pb.h"
|
|
|
|
|
#include "ortools/sat/cp_model_solver.h"
|
2019-03-25 11:26:14 +01:00
|
|
|
#include "ortools/sat/model.h"
|
|
|
|
|
#include "ortools/sat/sat_parameters.pb.h"
|
|
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
namespace sat {
|
|
|
|
|
|
|
|
|
|
void EarlinessTardinessCostSampleSat() {
|
2021-03-04 18:26:01 +01:00
|
|
|
const int64_t kEarlinessDate = 5;
|
|
|
|
|
const int64_t kEarlinessCost = 8;
|
|
|
|
|
const int64_t kLatenessDate = 15;
|
|
|
|
|
const int64_t kLatenessCost = 12;
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
// Create the CP-SAT model.
|
|
|
|
|
CpModelBuilder cp_model;
|
|
|
|
|
|
|
|
|
|
// Declare our primary variable.
|
|
|
|
|
const IntVar x = cp_model.NewIntVar({0, 20});
|
|
|
|
|
|
|
|
|
|
// Create the expression variable and implement the piecewise linear function.
|
|
|
|
|
//
|
|
|
|
|
// \ /
|
|
|
|
|
// \______/
|
|
|
|
|
// ed ld
|
|
|
|
|
//
|
2021-03-04 18:26:01 +01:00
|
|
|
const int64_t kLargeConstant = 1000;
|
2019-03-25 11:26:14 +01:00
|
|
|
const IntVar expr = cp_model.NewIntVar({0, kLargeConstant});
|
|
|
|
|
|
2021-12-09 15:29:49 +01:00
|
|
|
// Link together expr and x through the 3 segments.
|
|
|
|
|
cp_model.AddMaxEquality(expr, {(kEarlinessDate - x) * kEarlinessCost, 0,
|
|
|
|
|
(x - kLatenessDate) * kLatenessCost});
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
// Search for x values in increasing order.
|
|
|
|
|
cp_model.AddDecisionStrategy({x}, DecisionStrategyProto::CHOOSE_FIRST,
|
|
|
|
|
DecisionStrategyProto::SELECT_MIN_VALUE);
|
|
|
|
|
|
|
|
|
|
// Create a solver and solve with a fixed search.
|
|
|
|
|
Model model;
|
|
|
|
|
SatParameters parameters;
|
|
|
|
|
parameters.set_search_branching(SatParameters::FIXED_SEARCH);
|
|
|
|
|
parameters.set_enumerate_all_solutions(true);
|
|
|
|
|
model.Add(NewSatParameters(parameters));
|
|
|
|
|
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) {
|
|
|
|
|
LOG(INFO) << "x=" << SolutionIntegerValue(r, x) << " expr"
|
|
|
|
|
<< SolutionIntegerValue(r, expr);
|
|
|
|
|
}));
|
2019-05-16 16:50:11 +02:00
|
|
|
SolveCpModel(cp_model.Build(), &model);
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace sat
|
|
|
|
|
} // namespace operations_research
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
operations_research::sat::EarlinessTardinessCostSampleSat();
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Java code
|
|
|
|
|
|
|
|
|
|
```java
|
2020-06-25 18:31:47 +02:00
|
|
|
package com.google.ortools.sat.samples;
|
|
|
|
|
|
2020-11-03 18:40:54 +01:00
|
|
|
import com.google.ortools.Loader;
|
2019-03-25 11:26:14 +01:00
|
|
|
import com.google.ortools.sat.DecisionStrategyProto;
|
|
|
|
|
import com.google.ortools.sat.SatParameters;
|
|
|
|
|
import com.google.ortools.sat.CpModel;
|
|
|
|
|
import com.google.ortools.sat.CpSolver;
|
|
|
|
|
import com.google.ortools.sat.CpSolverSolutionCallback;
|
|
|
|
|
import com.google.ortools.sat.IntVar;
|
2019-05-06 22:21:53 +02:00
|
|
|
import com.google.ortools.sat.LinearExpr;
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
/** Encode the piecewise linear expression. */
|
|
|
|
|
public class EarlinessTardinessCostSampleSat {
|
|
|
|
|
public static void main(String[] args) throws Exception {
|
2020-11-03 18:40:54 +01:00
|
|
|
Loader.loadNativeLibraries();
|
2019-03-25 11:26:14 +01:00
|
|
|
long earlinessDate = 5;
|
|
|
|
|
long earlinessCost = 8;
|
|
|
|
|
long latenessDate = 15;
|
|
|
|
|
long latenessCost = 12;
|
|
|
|
|
|
|
|
|
|
// Create the CP-SAT model.
|
|
|
|
|
CpModel model = new CpModel();
|
|
|
|
|
|
|
|
|
|
// Declare our primary variable.
|
|
|
|
|
IntVar x = model.newIntVar(0, 20, "x");
|
|
|
|
|
|
|
|
|
|
// Create the expression variable and implement the piecewise linear function.
|
|
|
|
|
//
|
|
|
|
|
// \ /
|
|
|
|
|
// \______/
|
|
|
|
|
// ed ld
|
|
|
|
|
//
|
|
|
|
|
long largeConstant = 1000;
|
|
|
|
|
IntVar expr = model.newIntVar(0, largeConstant, "expr");
|
|
|
|
|
|
2022-01-03 09:43:59 +01:00
|
|
|
// Link together expr and the 3 segment.
|
|
|
|
|
// First segment: y == earlinessCost * (earlinessDate - x).
|
|
|
|
|
// Second segment: y = 0
|
|
|
|
|
// Third segment: y == latenessCost * (x - latenessDate).
|
|
|
|
|
model.addMaxEquality(
|
|
|
|
|
expr,
|
|
|
|
|
new LinearExpr[] {
|
|
|
|
|
LinearExpr.newBuilder()
|
|
|
|
|
.addTerm(x, -earlinessCost)
|
|
|
|
|
.add(earlinessCost * earlinessDate)
|
|
|
|
|
.build(),
|
|
|
|
|
LinearExpr.constant(0),
|
|
|
|
|
LinearExpr.newBuilder().addTerm(x, latenessCost).add(-latenessCost * latenessDate).build()
|
|
|
|
|
});
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
// Search for x values in increasing order.
|
|
|
|
|
model.addDecisionStrategy(
|
|
|
|
|
new IntVar[] {x},
|
|
|
|
|
DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST,
|
|
|
|
|
DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE);
|
|
|
|
|
|
|
|
|
|
// Create the solver.
|
|
|
|
|
CpSolver solver = new CpSolver();
|
|
|
|
|
|
|
|
|
|
// Force the solver to follow the decision strategy exactly.
|
|
|
|
|
solver.getParameters().setSearchBranching(SatParameters.SearchBranching.FIXED_SEARCH);
|
2021-05-03 12:11:39 +02:00
|
|
|
// Tell the solver to enumerate all solutions.
|
|
|
|
|
solver.getParameters().setEnumerateAllSolutions(true);
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
// Solve the problem with the printer callback.
|
2021-05-03 12:11:39 +02:00
|
|
|
solver.solve(
|
2019-03-25 11:26:14 +01:00
|
|
|
model,
|
|
|
|
|
new CpSolverSolutionCallback() {
|
|
|
|
|
public CpSolverSolutionCallback init(IntVar[] variables) {
|
|
|
|
|
variableArray = variables;
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSolutionCallback() {
|
|
|
|
|
for (IntVar v : variableArray) {
|
|
|
|
|
System.out.printf("%s=%d ", v.getName(), value(v));
|
|
|
|
|
}
|
|
|
|
|
System.out.println();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IntVar[] variableArray;
|
|
|
|
|
}.init(new IntVar[] {x, expr}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### C\# code
|
|
|
|
|
|
|
|
|
|
```cs
|
|
|
|
|
using System;
|
|
|
|
|
using Google.OrTools.Sat;
|
2019-05-08 13:43:05 +02:00
|
|
|
using Google.OrTools.Util;
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
public class VarArraySolutionPrinter : CpSolverSolutionCallback
|
|
|
|
|
{
|
|
|
|
|
public VarArraySolutionPrinter(IntVar[] variables)
|
|
|
|
|
{
|
|
|
|
|
variables_ = variables;
|
|
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
public override void OnSolutionCallback()
|
2019-03-25 11:26:14 +01:00
|
|
|
{
|
2020-11-03 14:01:06 +01:00
|
|
|
{
|
|
|
|
|
foreach (IntVar v in variables_)
|
|
|
|
|
{
|
2022-01-10 19:29:40 +01:00
|
|
|
Console.Write(String.Format("{0}={1} ", v.ToString(), Value(v)));
|
2020-11-03 14:01:06 +01:00
|
|
|
}
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
private IntVar[] variables_;
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
public class EarlinessTardinessCostSampleSat
|
|
|
|
|
{
|
|
|
|
|
static void Main()
|
|
|
|
|
{
|
|
|
|
|
long earliness_date = 5;
|
|
|
|
|
long earliness_cost = 8;
|
|
|
|
|
long lateness_date = 15;
|
|
|
|
|
long lateness_cost = 12;
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
// Create the CP-SAT model.
|
|
|
|
|
CpModel model = new CpModel();
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
// Declare our primary variable.
|
|
|
|
|
IntVar x = model.NewIntVar(0, 20, "x");
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
// Create the expression variable and implement the piecewise linear
|
|
|
|
|
// function.
|
|
|
|
|
//
|
|
|
|
|
// \ /
|
|
|
|
|
// \______/
|
|
|
|
|
// ed ld
|
|
|
|
|
//
|
|
|
|
|
long large_constant = 1000;
|
|
|
|
|
IntVar expr = model.NewIntVar(0, large_constant, "expr");
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
// Link together expr and x through s1, s2, and s3.
|
2022-01-10 19:29:40 +01:00
|
|
|
model.AddMaxEquality(expr, new LinearExpr[] { earliness_cost * (earliness_date - x), model.NewConstant(0),
|
|
|
|
|
lateness_cost * (x - lateness_date) });
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
// Search for x values in increasing order.
|
|
|
|
|
model.AddDecisionStrategy(new IntVar[] { x }, DecisionStrategyProto.Types.VariableSelectionStrategy.ChooseFirst,
|
|
|
|
|
DecisionStrategyProto.Types.DomainReductionStrategy.SelectMinValue);
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
// Create the solver.
|
|
|
|
|
CpSolver solver = new CpSolver();
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
// Force solver to follow the decision strategy exactly.
|
2021-05-03 12:11:39 +02:00
|
|
|
// Tell the solver to search for all solutions.
|
|
|
|
|
solver.StringParameters = "search_branching:FIXED_SEARCH, enumerate_all_solutions:true";
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
VarArraySolutionPrinter cb = new VarArraySolutionPrinter(new IntVar[] { x, expr });
|
2021-05-03 12:11:39 +02:00
|
|
|
solver.Solve(model, cb);
|
2020-11-03 14:01:06 +01:00
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Step function.
|
|
|
|
|
|
|
|
|
|
Let's encode a step function. We will use one Boolean variable per step value,
|
|
|
|
|
and filter the admissible domain of the input variable with this variable.
|
|
|
|
|
|
|
|
|
|
The following samples output:
|
|
|
|
|
|
|
|
|
|
x=0 expr=2
|
|
|
|
|
x=1 expr=2
|
|
|
|
|
x=3 expr=2
|
|
|
|
|
x=4 expr=2
|
|
|
|
|
x=5 expr=0
|
|
|
|
|
x=6 expr=0
|
|
|
|
|
x=7 expr=3
|
|
|
|
|
x=8 expr=0
|
|
|
|
|
x=9 expr=0
|
|
|
|
|
x=10 expr=0
|
|
|
|
|
x=11 expr=2
|
|
|
|
|
x=12 expr=2
|
|
|
|
|
x=13 expr=2
|
|
|
|
|
x=14 expr=2
|
|
|
|
|
x=15 expr=2
|
|
|
|
|
x=16 expr=2
|
|
|
|
|
x=17 expr=2
|
|
|
|
|
x=18 expr=2
|
|
|
|
|
x=19 expr=2
|
|
|
|
|
x=20 expr=2
|
|
|
|
|
|
|
|
|
|
### Python code
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
"""Implements a step function."""
|
|
|
|
|
|
|
|
|
|
from ortools.sat.python import cp_model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):
|
2019-05-06 10:12:44 +02:00
|
|
|
"""Print intermediate solutions."""
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
def __init__(self, variables):
|
|
|
|
|
cp_model.CpSolverSolutionCallback.__init__(self)
|
|
|
|
|
self.__variables = variables
|
|
|
|
|
self.__solution_count = 0
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
def on_solution_callback(self):
|
|
|
|
|
self.__solution_count += 1
|
|
|
|
|
for v in self.__variables:
|
|
|
|
|
print('%s=%i' % (v, self.Value(v)), end=' ')
|
|
|
|
|
print()
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
def solution_count(self):
|
|
|
|
|
return self.__solution_count
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def step_function_sample_sat():
|
2019-05-06 10:12:44 +02:00
|
|
|
"""Encode the step function."""
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# Model.
|
|
|
|
|
model = cp_model.CpModel()
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# Declare our primary variable.
|
|
|
|
|
x = model.NewIntVar(0, 20, 'x')
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# Create the expression variable and implement the step function
|
|
|
|
|
# Note it is not defined for x == 2.
|
|
|
|
|
#
|
|
|
|
|
# - 3
|
|
|
|
|
# -- -- --------- 2
|
|
|
|
|
# 1
|
|
|
|
|
# -- --- 0
|
|
|
|
|
# 0 ================ 20
|
|
|
|
|
#
|
|
|
|
|
expr = model.NewIntVar(0, 3, 'expr')
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# expr == 0 on [5, 6] U [8, 10]
|
|
|
|
|
b0 = model.NewBoolVar('b0')
|
|
|
|
|
model.AddLinearExpressionInDomain(
|
|
|
|
|
x, cp_model.Domain.FromIntervals([(5, 6), (8, 10)])).OnlyEnforceIf(b0)
|
|
|
|
|
model.Add(expr == 0).OnlyEnforceIf(b0)
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# expr == 2 on [0, 1] U [3, 4] U [11, 20]
|
|
|
|
|
b2 = model.NewBoolVar('b2')
|
|
|
|
|
model.AddLinearExpressionInDomain(
|
|
|
|
|
x, cp_model.Domain.FromIntervals([(0, 1), (3, 4),
|
|
|
|
|
(11, 20)])).OnlyEnforceIf(b2)
|
|
|
|
|
model.Add(expr == 2).OnlyEnforceIf(b2)
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# expr == 3 when x == 7
|
|
|
|
|
b3 = model.NewBoolVar('b3')
|
|
|
|
|
model.Add(x == 7).OnlyEnforceIf(b3)
|
|
|
|
|
model.Add(expr == 3).OnlyEnforceIf(b3)
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2022-01-01 19:26:39 +01:00
|
|
|
# At least one bi is true. (we could use an exactly one constraint).
|
2022-01-28 15:54:07 +01:00
|
|
|
model.AddBoolOr(b0, b2, b3)
|
2019-05-04 16:56:42 +02:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# Search for x values in increasing order.
|
|
|
|
|
model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST,
|
|
|
|
|
cp_model.SELECT_MIN_VALUE)
|
2019-05-04 16:56:42 +02:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# Create a solver and solve with a fixed search.
|
|
|
|
|
solver = cp_model.CpSolver()
|
2019-05-04 16:56:42 +02:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# Force the solver to follow the decision strategy exactly.
|
|
|
|
|
solver.parameters.search_branching = cp_model.FIXED_SEARCH
|
2021-05-03 12:11:39 +02:00
|
|
|
# Enumerate all solutions.
|
|
|
|
|
solver.parameters.enumerate_all_solutions = True
|
2019-05-04 16:56:42 +02:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
# Search and print out all solutions.
|
|
|
|
|
solution_printer = VarArraySolutionPrinter([x, expr])
|
2021-05-03 12:11:39 +02:00
|
|
|
solver.Solve(model, solution_printer)
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
step_function_sample_sat()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### C++ code
|
|
|
|
|
|
|
|
|
|
```cpp
|
2022-02-15 18:00:11 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#include "ortools/base/integral_types.h"
|
|
|
|
|
#include "ortools/base/logging.h"
|
|
|
|
|
#include "absl/types/span.h"
|
2019-03-25 11:26:14 +01:00
|
|
|
#include "ortools/sat/cp_model.h"
|
2022-02-15 18:00:11 +01:00
|
|
|
#include "ortools/sat/cp_model.pb.h"
|
|
|
|
|
#include "ortools/sat/cp_model_solver.h"
|
2019-03-25 11:26:14 +01:00
|
|
|
#include "ortools/sat/model.h"
|
|
|
|
|
#include "ortools/sat/sat_parameters.pb.h"
|
2022-02-15 18:00:11 +01:00
|
|
|
#include "ortools/util/sorted_interval_list.h"
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
namespace sat {
|
|
|
|
|
|
|
|
|
|
void StepFunctionSampleSat() {
|
|
|
|
|
// Create the CP-SAT model.
|
|
|
|
|
CpModelBuilder cp_model;
|
|
|
|
|
|
|
|
|
|
// Declare our primary variable.
|
|
|
|
|
const IntVar x = cp_model.NewIntVar({0, 20});
|
|
|
|
|
|
|
|
|
|
// Create the expression variable and implement the step function
|
|
|
|
|
// Note it is not defined for var == 2.
|
|
|
|
|
//
|
|
|
|
|
// - 3
|
|
|
|
|
// -- -- --------- 2
|
|
|
|
|
// 1
|
|
|
|
|
// -- --- 0
|
|
|
|
|
// 0 ================ 20
|
|
|
|
|
//
|
|
|
|
|
IntVar expr = cp_model.NewIntVar({0, 3});
|
|
|
|
|
|
|
|
|
|
// expr == 0 on [5, 6] U [8, 10]
|
|
|
|
|
BoolVar b0 = cp_model.NewBoolVar();
|
|
|
|
|
cp_model.AddLinearConstraint(x, Domain::FromValues({5, 6, 8, 9, 10}))
|
|
|
|
|
.OnlyEnforceIf(b0);
|
|
|
|
|
cp_model.AddEquality(expr, 0).OnlyEnforceIf(b0);
|
|
|
|
|
|
|
|
|
|
// expr == 2 on [0, 1] U [3, 4] U [11, 20]
|
|
|
|
|
BoolVar b2 = cp_model.NewBoolVar();
|
|
|
|
|
cp_model
|
|
|
|
|
.AddLinearConstraint(x, Domain::FromIntervals({{0, 1}, {3, 4}, {11, 20}}))
|
|
|
|
|
.OnlyEnforceIf(b2);
|
|
|
|
|
cp_model.AddEquality(expr, 2).OnlyEnforceIf(b2);
|
|
|
|
|
|
|
|
|
|
// expr == 3 when x = 7
|
|
|
|
|
BoolVar b3 = cp_model.NewBoolVar();
|
|
|
|
|
cp_model.AddEquality(x, 7).OnlyEnforceIf(b3);
|
|
|
|
|
cp_model.AddEquality(expr, 3).OnlyEnforceIf(b3);
|
|
|
|
|
|
2022-01-01 19:26:39 +01:00
|
|
|
// At least one bi is true. (we could use an exactly one constraint).
|
2019-03-25 11:26:14 +01:00
|
|
|
cp_model.AddBoolOr({b0, b2, b3});
|
|
|
|
|
|
|
|
|
|
// Search for x values in increasing order.
|
|
|
|
|
cp_model.AddDecisionStrategy({x}, DecisionStrategyProto::CHOOSE_FIRST,
|
|
|
|
|
DecisionStrategyProto::SELECT_MIN_VALUE);
|
|
|
|
|
|
|
|
|
|
// Create a solver and solve with a fixed search.
|
|
|
|
|
Model model;
|
|
|
|
|
SatParameters parameters;
|
|
|
|
|
parameters.set_search_branching(SatParameters::FIXED_SEARCH);
|
|
|
|
|
parameters.set_enumerate_all_solutions(true);
|
|
|
|
|
model.Add(NewSatParameters(parameters));
|
|
|
|
|
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) {
|
|
|
|
|
LOG(INFO) << "x=" << SolutionIntegerValue(r, x) << " expr"
|
|
|
|
|
<< SolutionIntegerValue(r, expr);
|
|
|
|
|
}));
|
2019-05-16 16:50:11 +02:00
|
|
|
SolveCpModel(cp_model.Build(), &model);
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace sat
|
|
|
|
|
} // namespace operations_research
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
operations_research::sat::StepFunctionSampleSat();
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Java code
|
|
|
|
|
|
|
|
|
|
```java
|
2020-06-25 18:31:47 +02:00
|
|
|
package com.google.ortools.sat.samples;
|
|
|
|
|
|
2020-11-03 18:40:54 +01:00
|
|
|
import com.google.ortools.Loader;
|
2019-05-06 10:12:44 +02:00
|
|
|
import com.google.ortools.sat.DecisionStrategyProto;
|
|
|
|
|
import com.google.ortools.sat.SatParameters;
|
2019-03-25 11:26:14 +01:00
|
|
|
import com.google.ortools.sat.CpModel;
|
|
|
|
|
import com.google.ortools.sat.CpSolver;
|
|
|
|
|
import com.google.ortools.sat.CpSolverSolutionCallback;
|
|
|
|
|
import com.google.ortools.sat.IntVar;
|
|
|
|
|
import com.google.ortools.sat.Literal;
|
2019-05-06 22:21:53 +02:00
|
|
|
import com.google.ortools.util.Domain;
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
/** Link integer constraints together. */
|
|
|
|
|
public class StepFunctionSampleSat {
|
|
|
|
|
public static void main(String[] args) throws Exception {
|
2020-11-03 18:40:54 +01:00
|
|
|
Loader.loadNativeLibraries();
|
2019-03-25 11:26:14 +01:00
|
|
|
// Create the CP-SAT model.
|
|
|
|
|
CpModel model = new CpModel();
|
|
|
|
|
|
|
|
|
|
// Declare our primary variable.
|
|
|
|
|
IntVar x = model.newIntVar(0, 20, "x");
|
|
|
|
|
|
|
|
|
|
// Create the expression variable and implement the step function
|
|
|
|
|
// Note it is not defined for var == 2.
|
|
|
|
|
//
|
|
|
|
|
// - 3
|
|
|
|
|
// -- -- --------- 2
|
|
|
|
|
// 1
|
|
|
|
|
// -- --- 0
|
|
|
|
|
// 0 ================ 20
|
|
|
|
|
//
|
|
|
|
|
IntVar expr = model.newIntVar(0, 3, "expr");
|
|
|
|
|
|
|
|
|
|
// expr == 0 on [5, 6] U [8, 10]
|
|
|
|
|
Literal b0 = model.newBoolVar("b0");
|
2019-05-06 10:12:44 +02:00
|
|
|
model
|
2019-05-06 22:21:53 +02:00
|
|
|
.addLinearExpressionInDomain(x, Domain.fromValues(new long[] {5, 6, 8, 9, 10}))
|
2019-05-06 10:12:44 +02:00
|
|
|
.onlyEnforceIf(b0);
|
2019-03-25 11:26:14 +01:00
|
|
|
model.addEquality(expr, 0).onlyEnforceIf(b0);
|
|
|
|
|
|
|
|
|
|
// expr == 2 on [0, 1] U [3, 4] U [11, 20]
|
|
|
|
|
Literal b2 = model.newBoolVar("b2");
|
2019-05-06 10:12:44 +02:00
|
|
|
model
|
2019-05-06 22:21:53 +02:00
|
|
|
.addLinearExpressionInDomain(
|
|
|
|
|
x, Domain.fromIntervals(new long[][] {{0, 1}, {3, 4}, {11, 20}}))
|
2019-05-06 10:12:44 +02:00
|
|
|
.onlyEnforceIf(b2);
|
2019-03-25 11:26:14 +01:00
|
|
|
model.addEquality(expr, 2).onlyEnforceIf(b2);
|
|
|
|
|
|
|
|
|
|
// expr == 3 when x = 7
|
|
|
|
|
Literal b3 = model.newBoolVar("b3");
|
|
|
|
|
model.addEquality(x, 7).onlyEnforceIf(b3);
|
|
|
|
|
model.addEquality(expr, 3).onlyEnforceIf(b3);
|
|
|
|
|
|
|
|
|
|
// At least one bi is true. (we could use a sum == 1).
|
|
|
|
|
model.addBoolOr(new Literal[] {b0, b2, b3});
|
|
|
|
|
|
|
|
|
|
// Search for x values in increasing order.
|
2019-05-06 10:12:44 +02:00
|
|
|
model.addDecisionStrategy(
|
|
|
|
|
new IntVar[] {x},
|
2019-03-25 11:26:14 +01:00
|
|
|
DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST,
|
|
|
|
|
DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE);
|
|
|
|
|
|
|
|
|
|
// Create the solver.
|
|
|
|
|
CpSolver solver = new CpSolver();
|
|
|
|
|
|
|
|
|
|
// Force the solver to follow the decision strategy exactly.
|
|
|
|
|
solver.getParameters().setSearchBranching(SatParameters.SearchBranching.FIXED_SEARCH);
|
2021-05-03 12:11:39 +02:00
|
|
|
// Tell the solver to enumerate all solutions.
|
|
|
|
|
solver.getParameters().setEnumerateAllSolutions(true);
|
2019-03-25 11:26:14 +01:00
|
|
|
|
|
|
|
|
// Solve the problem with the printer callback.
|
2021-05-03 12:11:39 +02:00
|
|
|
solver.solve(
|
2019-05-06 10:12:44 +02:00
|
|
|
model,
|
|
|
|
|
new CpSolverSolutionCallback() {
|
|
|
|
|
public CpSolverSolutionCallback init(IntVar[] variables) {
|
|
|
|
|
variableArray = variables;
|
|
|
|
|
return this;
|
|
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
@Override
|
|
|
|
|
public void onSolutionCallback() {
|
|
|
|
|
for (IntVar v : variableArray) {
|
|
|
|
|
System.out.printf("%s=%d ", v.getName(), value(v));
|
|
|
|
|
}
|
|
|
|
|
System.out.println();
|
|
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2019-05-06 10:12:44 +02:00
|
|
|
private IntVar[] variableArray;
|
|
|
|
|
}.init(new IntVar[] {x, expr}));
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### C\# code
|
|
|
|
|
|
|
|
|
|
```cs
|
|
|
|
|
using System;
|
|
|
|
|
using Google.OrTools.Sat;
|
2019-05-08 13:43:05 +02:00
|
|
|
using Google.OrTools.Util;
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
public class VarArraySolutionPrinter : CpSolverSolutionCallback
|
|
|
|
|
{
|
|
|
|
|
public VarArraySolutionPrinter(IntVar[] variables)
|
|
|
|
|
{
|
|
|
|
|
variables_ = variables;
|
|
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
public override void OnSolutionCallback()
|
2019-03-25 11:26:14 +01:00
|
|
|
{
|
2020-11-03 14:01:06 +01:00
|
|
|
{
|
|
|
|
|
foreach (IntVar v in variables_)
|
|
|
|
|
{
|
2022-01-10 19:29:40 +01:00
|
|
|
Console.Write(String.Format("{0}={1} ", v.ToString(), Value(v)));
|
2020-11-03 14:01:06 +01:00
|
|
|
}
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
private IntVar[] variables_;
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-03 14:01:06 +01:00
|
|
|
public class StepFunctionSampleSat
|
|
|
|
|
{
|
|
|
|
|
static void Main()
|
|
|
|
|
{
|
|
|
|
|
// Create the CP-SAT model.
|
|
|
|
|
CpModel model = new CpModel();
|
|
|
|
|
|
|
|
|
|
// Declare our primary variable.
|
|
|
|
|
IntVar x = model.NewIntVar(0, 20, "x");
|
|
|
|
|
|
|
|
|
|
// Create the expression variable and implement the step function
|
|
|
|
|
// Note it is not defined for var == 2.
|
|
|
|
|
//
|
|
|
|
|
// - 3
|
|
|
|
|
// -- -- --------- 2
|
|
|
|
|
// 1
|
|
|
|
|
// -- --- 0
|
|
|
|
|
// 0 ================ 20
|
|
|
|
|
//
|
|
|
|
|
IntVar expr = model.NewIntVar(0, 3, "expr");
|
|
|
|
|
|
|
|
|
|
// expr == 0 on [5, 6] U [8, 10]
|
|
|
|
|
ILiteral b0 = model.NewBoolVar("b0");
|
|
|
|
|
model.AddLinearExpressionInDomain(x, Domain.FromValues(new long[] { 5, 6, 8, 9, 10 })).OnlyEnforceIf(b0);
|
|
|
|
|
model.Add(expr == 0).OnlyEnforceIf(b0);
|
|
|
|
|
|
|
|
|
|
// expr == 2 on [0, 1] U [3, 4] U [11, 20]
|
|
|
|
|
ILiteral b2 = model.NewBoolVar("b2");
|
|
|
|
|
model
|
|
|
|
|
.AddLinearExpressionInDomain(
|
|
|
|
|
x,
|
|
|
|
|
Domain.FromIntervals(new long[][] { new long[] { 0, 1 }, new long[] { 3, 4 }, new long[] { 11, 20 } }))
|
|
|
|
|
.OnlyEnforceIf(b2);
|
|
|
|
|
model.Add(expr == 2).OnlyEnforceIf(b2);
|
|
|
|
|
|
|
|
|
|
// expr == 3 when x == 7
|
|
|
|
|
ILiteral b3 = model.NewBoolVar("b3");
|
|
|
|
|
model.Add(x == 7).OnlyEnforceIf(b3);
|
|
|
|
|
model.Add(expr == 3).OnlyEnforceIf(b3);
|
|
|
|
|
|
|
|
|
|
// At least one bi is true. (we could use a sum == 1).
|
|
|
|
|
model.AddBoolOr(new ILiteral[] { b0, b2, b3 });
|
|
|
|
|
|
|
|
|
|
// Search for x values in increasing order.
|
|
|
|
|
model.AddDecisionStrategy(new IntVar[] { x }, DecisionStrategyProto.Types.VariableSelectionStrategy.ChooseFirst,
|
|
|
|
|
DecisionStrategyProto.Types.DomainReductionStrategy.SelectMinValue);
|
|
|
|
|
|
|
|
|
|
// Create the solver.
|
|
|
|
|
CpSolver solver = new CpSolver();
|
|
|
|
|
|
|
|
|
|
// Force solver to follow the decision strategy exactly.
|
2021-05-03 12:11:39 +02:00
|
|
|
// Tells the solver to enumerate all solutions.
|
|
|
|
|
solver.StringParameters = "search_branching:FIXED_SEARCH, enumerate_all_solutions:true";
|
2020-11-03 14:01:06 +01:00
|
|
|
|
|
|
|
|
VarArraySolutionPrinter cb = new VarArraySolutionPrinter(new IntVar[] { x, expr });
|
2021-05-03 12:11:39 +02:00
|
|
|
solver.Solve(model, cb);
|
2020-11-03 14:01:06 +01:00
|
|
|
}
|
2019-03-25 11:26:14 +01:00
|
|
|
}
|
|
|
|
|
```
|