From 4036cde59e629ada3ff99f9977a78ffc6bdfe12f Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Mon, 10 Jan 2022 18:44:12 +0100 Subject: [PATCH] C# CP-SAT: test new expression; reformat examples/tests/*cs --- examples/tests/ConstraintSolverTests.cs | 1819 +++++++++++----------- examples/tests/InitTests.cs | 60 +- examples/tests/LinearSolverTests.cs | 774 ++++----- examples/tests/RoutingSolverTests.cs | 361 +++-- examples/tests/SatSolverTests.cs | 843 +++++----- examples/tests/issue18.cs | 62 +- examples/tests/issue22.cs | 100 +- examples/tests/issue33.cs | 1138 +++++++------- ortools/sat/csharp/IntegerExpressions.cs | 24 +- 9 files changed, 2619 insertions(+), 2562 deletions(-) diff --git a/examples/tests/ConstraintSolverTests.cs b/examples/tests/ConstraintSolverTests.cs index 4143b99e28..e59436853a 100644 --- a/examples/tests/ConstraintSolverTests.cs +++ b/examples/tests/ConstraintSolverTests.cs @@ -18,914 +18,917 @@ using static Google.OrTools.ConstraintSolver.operations_research_constraint_solv namespace Google.OrTools.Tests { - public class ConstraintSolverTest +public class ConstraintSolverTest +{ + [Fact] + public void IntVectorToInt64Vector() { - [Fact] - public void IntVectorToInt64Vector() - { - int[] input = { 5, 11, 17 }; - long[] output = ToInt64Vector(input); - Assert.Equal(3, output.Length); - Assert.Equal(5, output[0]); - Assert.Equal(11, output[1]); - Assert.Equal(17, output[2]); - } - - [Fact] - public void IntVarConstructor() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 7, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(7, x.Max()); - Assert.Equal("x(3..7)", x.ToString()); - } - - [Fact] - public void ConstraintConstructor() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 7, "x"); - Assert.Equal("x(3..7)", x.ToString()); - - // Unary operator - Constraint c0 = (x == 5); - Assert.Equal("(x(3..7) == 5)", c0.ToString()); - IntExpr e1 = -c0; - Assert.Equal("-(Watch(0 .. 1))", e1.ToString()); - IntExpr e2 = c0.Abs(); - Assert.Equal("Watch(0 .. 1)", e2.ToString()); - IntExpr e3 = c0.Square(); - Assert.Equal("IntSquare(Watch(0 .. 1))", e3.ToString()); - - // Relational operator with a scalar - Constraint c1 = x == 5; - Assert.Equal("(x(3..7) == 5)", c1.ToString()); - Constraint c2 = x >= 5; - Assert.Equal("(x(3..7) >= 5)", c2.ToString()); - Constraint c3 = x > 5; - Assert.Equal("(x(3..7) >= 6)", c3.ToString()); - Constraint c4 = x <= 5; - Assert.Equal("(x(3..7) <= 5)", c4.ToString()); - Constraint c5 = x < 5; - Assert.Equal("(x(3..7) <= 4)", c5.ToString()); - Constraint c6 = x != 5; - Assert.Equal("(x(3..7) != 5)", c6.ToString()); - Constraint c7 = x == 2; - Assert.Equal("FalseConstraint()", c7.ToString()); - } - - [Fact] - public void IntExprConstructor() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - // Unary Operator - IntExpr e1 = -(x == 7); - Assert.Equal("-(Watch(0 .. 1))", e1.ToString()); - IntExpr e2 = (x == 7).Abs(); - Assert.Equal("Watch(0 .. 1)", e2.ToString()); - IntExpr e3 = (x == 7).Square(); - Assert.Equal("IntSquare(Watch(0 .. 1))", e3.ToString()); - } - - [Fact] - public void GreaterIntExprConstructor() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - // Unary Operator - IntExpr e1 = -(x >= 7); - Assert.Equal("-(Watch= 7>(0 .. 1))", e1.ToString()); - IntExpr e2 = (x >= 7).Abs(); - Assert.Equal("Watch= 7>(0 .. 1)", e2.ToString()); - IntExpr e3 = (x >= 7).Square(); - Assert.Equal("IntSquare(Watch= 7>(0 .. 1))", e3.ToString()); - } - - [Fact] - public void ConstraintAndScalar() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - Constraint c1 = (x == 7); - Assert.Equal("(x(3..13) == 7)", c1.ToString()); - - // Arithmetic operator with a scalar - IntExpr e2a = c1 + 1; - Assert.Equal("(Watch(0 .. 1) + 1)", e2a.ToString()); - IntExpr e2b = 1 + c1; - Assert.Equal("(Watch(0 .. 1) + 1)", e2b.ToString()); - - IntExpr e2c = c1 - 1; - Assert.Equal("(Watch(0 .. 1) + -1)", e2c.ToString()); - IntExpr e2d = 1 - c1; - Assert.Equal("Not(Watch(0 .. 1))", e2d.ToString()); - - IntExpr e2e = c1 * 2; - Assert.Equal("(Watch(0 .. 1) * 2)", e2e.ToString()); - IntExpr e2f = 2 * c1; - Assert.Equal("(Watch(0 .. 1) * 2)", e2f.ToString()); - - IntExpr e2g = c1 / 4; - Assert.Equal("(Watch(0 .. 1) div 4)", e2g.ToString()); - - // Relational operator with a scalar - Constraint c8a = c1 == 1; - Assert.Equal("(Watch(0 .. 1) == 1)", c8a.ToString()); - Constraint c8b = 1 == c1; - Assert.Equal("(Watch(0 .. 1) == 1)", c8b.ToString()); - - Constraint c8c = c1 != 1; - Assert.Equal("(Watch(0 .. 1) != 1)", c8c.ToString()); - Constraint c8d = 1 != c1; - Assert.Equal("(Watch(0 .. 1) != 1)", c8d.ToString()); - - Constraint c8e = c1 >= 1; - Assert.Equal("(Watch(0 .. 1) >= 1)", c8e.ToString()); - Constraint c8f = 1 >= c1; - Assert.Equal("TrueConstraint()", c8f.ToString()); - - Constraint c8g = c1 > 1; - Assert.Equal("FalseConstraint()", c8g.ToString()); - Constraint c8h = 1 > c1; - Assert.Equal("(Watch(0 .. 1) <= 0)", c8h.ToString()); - - Constraint c8i = c1 <= 1; - Assert.Equal("TrueConstraint()", c8i.ToString()); - Constraint c8j = 1 <= c1; - Assert.Equal("(Watch(0 .. 1) >= 1)", c8j.ToString()); - - Constraint c8k = c1 < 1; - Assert.Equal("(Watch(0 .. 1) <= 0)", c8k.ToString()); - Constraint c8l = 1 < c1; - Assert.Equal("FalseConstraint()", c8l.ToString()); - } - - [Fact] - public void ConstraintAndIntVar() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - Constraint c1 = x == 7; - Assert.Equal("(x(3..13) == 7)", c1.ToString()); - - IntVar y = solver.MakeIntVar(5, 17, "y"); - Assert.Equal(5, y.Min()); - Assert.Equal(17, y.Max()); - - // Arithmetic operator with IntVar - IntExpr e3a = c1 + y; - Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3a.ToString()); - IntExpr e3b = y + c1; - Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3b.ToString()); - - IntExpr e3c = c1 - y; - Assert.Equal("(Watch(0 .. 1) - y(5..17))", e3c.ToString()); - IntExpr e3d = y - c1; - Assert.Equal("(y(5..17) - Watch(0 .. 1))", e3d.ToString()); - - IntExpr e3e = c1 * y; - Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3e.ToString()); - IntExpr e3f = y * c1; - Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3f.ToString()); - - // Relational operator with an IntVar - Constraint c9a = c1 == y; - Assert.Equal("Watch(0 .. 1) == y(5..17)", c9a.ToString()); - Constraint c9b = y == c1; - Assert.Equal("y(5..17) == Watch(0 .. 1)", c9b.ToString()); - - Constraint c9c = c1 != y; - Assert.Equal("Watch(0 .. 1) != y(5..17)", c9c.ToString()); - Constraint c9d = y != c1; - Assert.Equal("y(5..17) != Watch(0 .. 1)", c9d.ToString()); - - Constraint c9e = c1 >= y; - Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9e.ToString()); - Constraint c9f = y >= c1; - Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9f.ToString()); - - Constraint c9g = c1 > y; - Assert.Equal("y(5..17) < Watch(0 .. 1)", c9g.ToString()); - Constraint c9h = y > c1; - Assert.Equal("Watch(0 .. 1) < y(5..17)", c9h.ToString()); - - Constraint c9i = c1 <= y; - Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9i.ToString()); - Constraint c9j = y <= c1; - Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9j.ToString()); - - Constraint c9k = c1 < y; - Assert.Equal("Watch(0 .. 1) < y(5..17)", c9k.ToString()); - Constraint c9l = y < c1; - Assert.Equal("y(5..17) < Watch(0 .. 1)", c9l.ToString()); - } - - [Fact] - public void ConstraintAndIntExpr() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - Constraint c1 = x == 7; - Assert.Equal("(x(3..13) == 7)", c1.ToString()); - - IntVar y = solver.MakeIntVar(5, 17, "y"); - Assert.Equal(5, y.Min()); - Assert.Equal(17, y.Max()); - - // Arithmetic operator with an IntExpr - IntExpr e11a = c1 + (y == 11); - Assert.Equal("(Watch(0 .. 1) + Watch(0 .. 1))", e11a.ToString()); - IntExpr e11b = (y == 11) + c1; - Assert.Equal("(Watch(0 .. 1) + Watch(0 .. 1))", e11b.ToString()); - IntExpr e11c = c1 - (y == 11); - Assert.Equal("(Watch(0 .. 1) - Watch(0 .. 1))", e11c.ToString()); - IntExpr e11d = (y == 11) - c1; - Assert.Equal("(Watch(0 .. 1) - Watch(0 .. 1))", e11d.ToString()); - IntExpr e11e = c1 * (y == 11); - Assert.Equal("(Watch(0 .. 1) * Watch(0 .. 1))", e11e.ToString()); - IntExpr e11f = (y == 11) * c1; - Assert.Equal("(Watch(0 .. 1) * Watch(0 .. 1))", e11f.ToString()); - - // Relational operator with an IntExpr - Constraint c12a = c1 == (y == 11); - Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c12a.ToString()); - Constraint c12b = (y == 11) == c1; - Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c12b.ToString()); - Constraint c12c = c1 != (y == 11); - Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c12c.ToString()); - Constraint c12d = (y == 11) != c1; - Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c12d.ToString()); - Constraint c12e = c1 >= (y == 11); - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12e.ToString()); - Constraint c12f = (y == 11) >= c1; - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12f.ToString()); - Constraint c12g = c1 > (y == 11); - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12g.ToString()); - Constraint c12h = (y == 11) > c1; - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12h.ToString()); - Constraint c12i = c1 <= (y == 11); - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12i.ToString()); - Constraint c12j = (y == 11) <= c1; - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12j.ToString()); - Constraint c12k = c1 < (y == 11); - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12k.ToString()); - Constraint c12l = (y == 11) < c1; - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12l.ToString()); - } - - [Fact] - public void ConstraintAndConstraint() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - Constraint c1 = x == 7; - Assert.Equal("(x(3..13) == 7)", c1.ToString()); - - IntVar y = solver.MakeIntVar(5, 17, "y"); - Assert.Equal(5, y.Min()); - Assert.Equal(17, y.Max()); - Constraint c2 = y == 11; - Assert.Equal("(y(5..17) == 11)", c2.ToString()); - - // Relational operator with a Constraint - Constraint c10a = c1 == c2; - Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c10a.ToString()); - - Constraint c10b = c1 != c2; - Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c10b.ToString()); - - Constraint c10c = c1 <= c2; - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10c.ToString()); - Constraint c10d = c1 >= c2; - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10d.ToString()); - - Constraint c10e = c1 > c2; - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10e.ToString()); - Constraint c10f = c1 < c2; - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10f.ToString()); - } - - [Fact] - public void IntExprAndScalar() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - // Arithmetic operator with a scalar - IntExpr e2a = (x == 7) + 1; - Assert.Equal("(Watch(0 .. 1) + 1)", e2a.ToString()); - IntExpr e2b = 1 + (x == 7); - Assert.Equal("(Watch(0 .. 1) + 1)", e2b.ToString()); - - IntExpr e2c = (x == 7) - 1; - Assert.Equal("(Watch(0 .. 1) + -1)", e2c.ToString()); - IntExpr e2d = 1 - (x == 7); - Assert.Equal("Not(Watch(0 .. 1))", e2d.ToString()); - - IntExpr e2e = (x == 7) * 2; - Assert.Equal("(Watch(0 .. 1) * 2)", e2e.ToString()); - IntExpr e2f = 2 * (x == 7); - Assert.Equal("(Watch(0 .. 1) * 2)", e2f.ToString()); - - IntExpr e2g = (x == 7) / 4; - Assert.Equal("(Watch(0 .. 1) div 4)", e2g.ToString()); - - // Relational operator with a scalar - Constraint c8a = (x == 7) == 1; - Assert.Equal("(Watch(0 .. 1) == 1)", c8a.ToString()); - Constraint c8b = 1 == (x == 7); - Assert.Equal("(Watch(0 .. 1) == 1)", c8b.ToString()); - - Constraint c8c = (x == 7) != 1; - Assert.Equal("(Watch(0 .. 1) != 1)", c8c.ToString()); - Constraint c8d = 1 != (x == 7); - Assert.Equal("(Watch(0 .. 1) != 1)", c8d.ToString()); - - Constraint c8e = (x == 7) >= 1; - Assert.Equal("(Watch(0 .. 1) >= 1)", c8e.ToString()); - Constraint c8f = 1 >= (x == 7); - Assert.Equal("TrueConstraint()", c8f.ToString()); - - Constraint c8g = (x == 7) > 1; - Assert.Equal("FalseConstraint()", c8g.ToString()); - Constraint c8h = 1 > (x == 7); - Assert.Equal("(Watch(0 .. 1) <= 0)", c8h.ToString()); - - Constraint c8i = (x == 7) <= 1; - Assert.Equal("TrueConstraint()", c8i.ToString()); - Constraint c8j = 1 <= (x == 7); - Assert.Equal("(Watch(0 .. 1) >= 1)", c8j.ToString()); - - Constraint c8k = (x == 7) < 1; - Assert.Equal("(Watch(0 .. 1) <= 0)", c8k.ToString()); - Constraint c8l = 1 < (x == 7); - Assert.Equal("FalseConstraint()", c8l.ToString()); - } - - [Fact] - public void IntExprAndIntVar() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - IntVar y = solver.MakeIntVar(5, 17, "y"); - Assert.Equal(5, y.Min()); - Assert.Equal(17, y.Max()); - - // Arithmetic operator with IntVar - IntExpr e3a = (x == 7) + y; - Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3a.ToString()); - IntExpr e3b = y + (x == 7); - Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3b.ToString()); - - IntExpr e3c = (x == 7) - y; - Assert.Equal("(Watch(0 .. 1) - y(5..17))", e3c.ToString()); - IntExpr e3d = y - (x == 7); - Assert.Equal("(y(5..17) - Watch(0 .. 1))", e3d.ToString()); - - IntExpr e3e = (x == 7) * y; - Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3e.ToString()); - IntExpr e3f = y * (x == 7); - Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3f.ToString()); - - // Relational operator with an IntVar - Constraint c9a = (x == 7) == y; - Assert.Equal("Watch(0 .. 1) == y(5..17)", c9a.ToString()); - Constraint c9b = y == (x == 7); - Assert.Equal("y(5..17) == Watch(0 .. 1)", c9b.ToString()); - - Constraint c9c = (x == 7) != y; - Assert.Equal("Watch(0 .. 1) != y(5..17)", c9c.ToString()); - Constraint c9d = y != (x == 7); - Assert.Equal("y(5..17) != Watch(0 .. 1)", c9d.ToString()); - - Constraint c9e = (x == 7) >= y; - Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9e.ToString()); - Constraint c9f = y >= (x == 7); - Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9f.ToString()); - - Constraint c9g = (x == 7) > y; - Assert.Equal("y(5..17) < Watch(0 .. 1)", c9g.ToString()); - Constraint c9h = y > (x == 7); - Assert.Equal("Watch(0 .. 1) < y(5..17)", c9h.ToString()); - - Constraint c9i = (x == 7) <= y; - Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9i.ToString()); - Constraint c9j = y <= (x == 7); - Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9j.ToString()); - - Constraint c9k = (x == 7) < y; - Assert.Equal("Watch(0 .. 1) < y(5..17)", c9k.ToString()); - Constraint c9l = y < (x == 7); - Assert.Equal("y(5..17) < Watch(0 .. 1)", c9l.ToString()); - } - - [Fact] - public void IntExprAndIntExpr() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - IntVar y = solver.MakeIntVar(5, 17, "y"); - Assert.Equal(5, y.Min()); - Assert.Equal(17, y.Max()); - - // Relational operator between IntExpr - Constraint c10a = (x == 7) == (y == 11); - Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c10a.ToString()); - - Constraint c10b = (x == 7) != (y == 11); - Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c10b.ToString()); - - Constraint c10c = (x == 7) <= (y == 11); - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10c.ToString()); - Constraint c10d = (x == 7) >= (y == 11); - Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10d.ToString()); - - Constraint c10e = (x == 7) > (y == 11); - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10e.ToString()); - Constraint c10f = (x == 7) < (y == 11); - Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10f.ToString()); - } - - [Fact] - public void GreaterIntExprAndScalar() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - // Arithmetic operator with a scalar - IntExpr e2a = (x >= 7) + 1; - Assert.Equal("(Watch= 7>(0 .. 1) + 1)", e2a.ToString()); - IntExpr e2b = 1 + (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) + 1)", e2b.ToString()); - - IntExpr e2c = (x >= 7) - 1; - Assert.Equal("(Watch= 7>(0 .. 1) + -1)", e2c.ToString()); - IntExpr e2d = 1 - (x >= 7); - Assert.Equal("Not(Watch= 7>(0 .. 1))", e2d.ToString()); - - IntExpr e2e = (x >= 7) * 2; - Assert.Equal("(Watch= 7>(0 .. 1) * 2)", e2e.ToString()); - IntExpr e2f = 2 * (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) * 2)", e2f.ToString()); - - // Relational operator with a scalar - Constraint c8a = (x >= 7) == 1; - Assert.Equal("(Watch= 7>(0 .. 1) == 1)", c8a.ToString()); - Constraint c8b = 1 == (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) == 1)", c8b.ToString()); - - Constraint c8c = (x >= 7) != 1; - Assert.Equal("(Watch= 7>(0 .. 1) != 1)", c8c.ToString()); - Constraint c8d = 1 != (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) != 1)", c8d.ToString()); - - Constraint c8e = (x >= 7) >= 1; - Assert.Equal("(Watch= 7>(0 .. 1) >= 1)", c8e.ToString()); - Constraint c8f = 1 >= (x >= 7); - Assert.Equal("TrueConstraint()", c8f.ToString()); - - Constraint c8g = (x >= 7) > 1; - Assert.Equal("FalseConstraint()", c8g.ToString()); - Constraint c8h = 1 > (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) <= 0)", c8h.ToString()); - - Constraint c8i = (x >= 7) <= 1; - Assert.Equal("TrueConstraint()", c8i.ToString()); - Constraint c8j = 1 <= (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) >= 1)", c8j.ToString()); - - Constraint c8k = (x >= 7) < 1; - Assert.Equal("(Watch= 7>(0 .. 1) <= 0)", c8k.ToString()); - Constraint c8l = 1 < (x >= 7); - Assert.Equal("FalseConstraint()", c8l.ToString()); - } - - [Fact] - public void GreaterIntExprAndIntVar() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - IntVar y = solver.MakeIntVar(5, 17, "y"); - Assert.Equal(5, y.Min()); - Assert.Equal(17, y.Max()); - - // Arithmetic operator with IntVar - IntExpr e3a = (x >= 7) + y; - Assert.Equal("(Watch= 7>(0 .. 1) + y(5..17))", e3a.ToString()); - IntExpr e3b = y + (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) + y(5..17))", e3b.ToString()); - - IntExpr e3c = (x >= 7) - y; - Assert.Equal("(Watch= 7>(0 .. 1) - y(5..17))", e3c.ToString()); - IntExpr e3d = y - (x >= 7); - Assert.Equal("(y(5..17) - Watch= 7>(0 .. 1))", e3d.ToString()); - - IntExpr e3e = (x >= 7) * y; - Assert.Equal("(Watch= 7>(0 .. 1) * y(5..17))", e3e.ToString()); - IntExpr e3f = y * (x >= 7); - Assert.Equal("(Watch= 7>(0 .. 1) * y(5..17))", e3f.ToString()); - - // Relational operator with an IntVar - Constraint c9a = (x >= 7) == y; - Assert.Equal("Watch= 7>(0 .. 1) == y(5..17)", c9a.ToString()); - Constraint c9b = y == (x >= 7); - Assert.Equal("y(5..17) == Watch= 7>(0 .. 1)", c9b.ToString()); - - Constraint c9c = (x >= 7) != y; - Assert.Equal("Watch= 7>(0 .. 1) != y(5..17)", c9c.ToString()); - Constraint c9d = y != (x >= 7); - Assert.Equal("y(5..17) != Watch= 7>(0 .. 1)", c9d.ToString()); - - Constraint c9e = (x >= 7) >= y; - Assert.Equal("y(5..17) <= Watch= 7>(0 .. 1)", c9e.ToString()); - Constraint c9f = y >= (x >= 7); - Assert.Equal("Watch= 7>(0 .. 1) <= y(5..17)", c9f.ToString()); - - Constraint c9g = (x >= 7) > y; - Assert.Equal("y(5..17) < Watch= 7>(0 .. 1)", c9g.ToString()); - Constraint c9h = y > (x >= 7); - Assert.Equal("Watch= 7>(0 .. 1) < y(5..17)", c9h.ToString()); - - Constraint c9i = (x >= 7) <= y; - Assert.Equal("Watch= 7>(0 .. 1) <= y(5..17)", c9i.ToString()); - Constraint c9j = y <= (x >= 7); - Assert.Equal("y(5..17) <= Watch= 7>(0 .. 1)", c9j.ToString()); - - Constraint c9k = (x >= 7) < y; - Assert.Equal("Watch= 7>(0 .. 1) < y(5..17)", c9k.ToString()); - Constraint c9l = y < (x >= 7); - Assert.Equal("y(5..17) < Watch= 7>(0 .. 1)", c9l.ToString()); - } - - [Fact] - public void GreaterIntExprAndGreaterIntExpr() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(3, 13, "x"); - Assert.Equal(3, x.Min()); - Assert.Equal(13, x.Max()); - - IntVar y = solver.MakeIntVar(5, 17, "y"); - Assert.Equal(5, y.Min()); - Assert.Equal(17, y.Max()); - - // Relational operator between IntExpr - Constraint c10a = (x >= 7) == (y >= 11); - Assert.Equal("Watch= 7>(0 .. 1) == Watch= 11>(0 .. 1)", c10a.ToString()); - - Constraint c10b = (x >= 7) != (y >= 11); - Assert.Equal("Watch= 7>(0 .. 1) != Watch= 11>(0 .. 1)", c10b.ToString()); - - Constraint c10c = (x >= 7) <= (y >= 11); - Assert.Equal("Watch= 7>(0 .. 1) <= Watch= 11>(0 .. 1)", c10c.ToString()); - Constraint c10d = (x >= 7) >= (y >= 11); - Assert.Equal("Watch= 11>(0 .. 1) <= Watch= 7>(0 .. 1)", c10d.ToString()); - - Constraint c10e = (x >= 7) > (y >= 11); - Assert.Equal("Watch= 11>(0 .. 1) < Watch= 7>(0 .. 1)", c10e.ToString()); - Constraint c10f = (x >= 7) < (y >= 11); - Assert.Equal("Watch= 7>(0 .. 1) < Watch= 11>(0 .. 1)", c10f.ToString()); - } - - [Fact] - public void Downcast() - { - Solver solver = new Solver("Solver"); - IntVar x = solver.MakeIntVar(2, 17, "x"); - IntExpr e = x + 5; - IntVar y = e.Var(); - Assert.Equal("(x(2..17) + 5)", y.ToString()); - } - - [Fact] - public void Sequence() - { - Solver solver = new Solver("Solver"); - IntervalVar[] intervals = solver.MakeFixedDurationIntervalVarArray(10, 0, 10, 5, false, "task"); - DisjunctiveConstraint disjunctive = intervals.Disjunctive("Sequence"); - SequenceVar var = disjunctive.SequenceVar(); - Assignment ass = solver.MakeAssignment(); - ass.Add(var); - ass.SetForwardSequence(var, new int[] { 1, 3, 5 }); - int[] seq = ass.ForwardSequence(var); - Assert.Equal(3, seq.Length); - Assert.Equal(1, seq[0]); - Assert.Equal(3, seq[1]); - Assert.Equal(5, seq[2]); - } - - // A simple demon that simply sets the maximum of a fixed IntVar to 10 when - // it's being called. - class SetMaxDemon : NetDemon - { - public SetMaxDemon(IntVar x) - { - x_ = x; - } - public override void Run(Solver s) - { - x_.SetMax(10); - } - private IntVar x_; - } - - [Fact] - public void Demon() - { - Solver solver = new Solver("DemonTest"); - IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); - NetDemon demon = new SetMaxDemon(x); - Assert.Equal(11, x.Max()); - demon.Run(solver); - Assert.Equal(10, x.Max()); - } - - // This constraint has a single target variable x. It enforces x >= 5 upon - // InitialPropagate() and invokes the SetMaxDemon when x changes its range. - class SetMinAndMaxConstraint : NetConstraint - { - public SetMinAndMaxConstraint(Solver solver, IntVar x) : base(solver) - { - x_ = x; - } - public override void Post() - { - // Always store the demon in the constraint to avoid it being reclaimed - // by the GC. - demon_ = new SetMaxDemon(x_); - x_.WhenBound(demon_); - } - public override void InitialPropagate() - { - x_.SetMin(5); - } - private IntVar x_; - private Demon demon_; - } - - [Fact] - public void MinAndMaxConstraint() - { - Solver solver = new Solver("TestConstraint"); - IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); - Constraint ct = new SetMinAndMaxConstraint(solver, x); - solver.Add(ct); - DecisionBuilder db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - solver.NewSearch(db); - Assert.Equal(-1, x.Min()); - Assert.Equal(11, x.Max()); - - Assert.True(solver.NextSolution()); - Assert.Equal(6, x.Min()); - Assert.Equal(6, x.Max()); - - Assert.True(solver.NextSolution()); - Assert.Equal(10, x.Min()); - Assert.Equal(10, x.Max()); - - Assert.False(solver.NextSolution()); - solver.EndSearch(); - } - - // // This constraint has a single target variable x. It enforces x >= 5, - // but only at the leaf of the search tree: it doesn't change x's bounds, - // and simply fails if x is bound and is < 5. - class DumbGreaterOrEqualToFive : NetConstraint - { - public DumbGreaterOrEqualToFive(Solver solver, IntVar x) : base(solver) - { - x_ = x; - } - public override void Post() - { - demon_ = solver().MakeConstraintInitialPropagateCallback(this); - x_.WhenRange(demon_); - } - public override void InitialPropagate() - { - if (x_.Bound()) - { - if (x_.Value() < 5) - { - solver().Fail(); - } - } - } - private IntVar x_; - private Demon demon_; - } - - [Fact] - public void FailingConstraint() - { - Solver solver = new Solver("TestConstraint"); - IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); - Constraint ct = new DumbGreaterOrEqualToFive(solver, x); - solver.Add(ct); - DecisionBuilder db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - solver.NewSearch(db); - Assert.True(solver.NextSolution()); - Assert.Equal(6, x.Min()); - solver.EndSearch(); - } - - [Fact] - public void DomainIterator() - { - Solver solver = new Solver("TestConstraint"); - IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); - ulong count = 0; - foreach (long value in x.GetDomain()) - { - count++; - } - Assert.Equal(count, x.Size()); - } - - class CountHoles : NetDemon - { - public CountHoles(IntVar x) - { - x_ = x; - count_ = 0; - } - public override void Run(Solver s) - { - foreach (long removed in x_.GetHoles()) - { - count_++; - } - } - public int count() - { - return count_; - } - private IntVar x_; - private int count_; - } - - class RemoveThreeValues : NetConstraint - { - public RemoveThreeValues(Solver solver, IntVar x) : base(solver) - { - x_ = x; - } - public override void Post() - { - demon_ = new CountHoles(x_); - x_.WhenDomain(demon_); - } - public override void InitialPropagate() - { - x_.RemoveValues(new long[] { 3, 5, 7 }); - } - public int count() - { - return demon_.count(); - } - private IntVar x_; - private CountHoles demon_; - } - - [Fact] - public void HoleIteratorTest() - { - Solver solver = new Solver("TestConstraint"); - IntVar x = solver.MakeIntVar(0, 10, "x"); - RemoveThreeValues ct = new RemoveThreeValues(solver, x); - solver.Add(ct); - DecisionBuilder db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - solver.Solve(db); - Assert.Equal(3, ct.count()); - } - - // TODO(user): Improve search log tests; currently only tests coverage. - void RunSearchLog(in SearchMonitor searchlog) - { - searchlog.EnterSearch(); - searchlog.ExitSearch(); - searchlog.AcceptSolution(); - searchlog.AtSolution(); - searchlog.BeginFail(); - searchlog.NoMoreSolutions(); - searchlog.BeginInitialPropagation(); - searchlog.EndInitialPropagation(); - } - - [Fact] - public void SearchLog() - { - Solver solver = new Solver("TestSearchLog"); - IntVar var = solver.MakeIntVar(1, 1, "Variable"); - OptimizeVar objective = solver.MakeMinimize(var, 1); - SearchMonitor searchlog = solver.MakeSearchLog(0); - RunSearchLog(in searchlog); - GC.KeepAlive(solver); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void SearchLogWithCallback(bool callGC) - { - Solver solver = new Solver("TestSearchLog"); - IntVar var = solver.MakeIntVar(1, 1, "Variable"); - OptimizeVar objective = solver.MakeMinimize(var, 1); - int count = 0; - SearchMonitor searchlog = solver.MakeSearchLog(0, // branch period - () => { - count++; - return "display callback..."; - }); - if (callGC) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - RunSearchLog(in searchlog); - GC.KeepAlive(solver); - Assert.Equal(1, count); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void SearchLogWithObjectiveAndCallback(bool callGC) - { - Solver solver = new Solver("TestSearchLog"); - IntVar var = solver.MakeIntVar(1, 1, "Variable"); - OptimizeVar objective = solver.MakeMinimize(var, 1); - int count = 0; - SearchMonitor searchlog = solver.MakeSearchLog(0, // branch period - objective, // objective var to monitor - () => { - count++; - return "OptimizeVar display callback"; - }); - if (callGC) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - RunSearchLog(in searchlog); - GC.KeepAlive(solver); - Assert.Equal(1, count); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void SearchLogWithIntVarAndCallback(bool callGC) - { - Solver solver = new Solver("TestSearchLog"); - IntVar var = solver.MakeIntVar(1, 1, "Variable"); - OptimizeVar objective = solver.MakeMinimize(var, 1); - int count = 0; - SearchMonitor searchlog = solver.MakeSearchLog(0, // branch period - var, // int var to monitor - () => { - count++; - return "IntVar display callback"; - }); - if (callGC) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - RunSearchLog(in searchlog); - GC.KeepAlive(solver); - Assert.Equal(1, count); - } + int[] input = { 5, 11, 17 }; + long[] output = ToInt64Vector(input); + Assert.Equal(3, output.Length); + Assert.Equal(5, output[0]); + Assert.Equal(11, output[1]); + Assert.Equal(17, output[2]); } + + [Fact] + public void IntVarConstructor() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 7, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(7, x.Max()); + Assert.Equal("x(3..7)", x.ToString()); + } + + [Fact] + public void ConstraintConstructor() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 7, "x"); + Assert.Equal("x(3..7)", x.ToString()); + + // Unary operator + Constraint c0 = (x == 5); + Assert.Equal("(x(3..7) == 5)", c0.ToString()); + IntExpr e1 = -c0; + Assert.Equal("-(Watch(0 .. 1))", e1.ToString()); + IntExpr e2 = c0.Abs(); + Assert.Equal("Watch(0 .. 1)", e2.ToString()); + IntExpr e3 = c0.Square(); + Assert.Equal("IntSquare(Watch(0 .. 1))", e3.ToString()); + + // Relational operator with a scalar + Constraint c1 = x == 5; + Assert.Equal("(x(3..7) == 5)", c1.ToString()); + Constraint c2 = x >= 5; + Assert.Equal("(x(3..7) >= 5)", c2.ToString()); + Constraint c3 = x > 5; + Assert.Equal("(x(3..7) >= 6)", c3.ToString()); + Constraint c4 = x <= 5; + Assert.Equal("(x(3..7) <= 5)", c4.ToString()); + Constraint c5 = x < 5; + Assert.Equal("(x(3..7) <= 4)", c5.ToString()); + Constraint c6 = x != 5; + Assert.Equal("(x(3..7) != 5)", c6.ToString()); + Constraint c7 = x == 2; + Assert.Equal("FalseConstraint()", c7.ToString()); + } + + [Fact] + public void IntExprConstructor() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + // Unary Operator + IntExpr e1 = -(x == 7); + Assert.Equal("-(Watch(0 .. 1))", e1.ToString()); + IntExpr e2 = (x == 7).Abs(); + Assert.Equal("Watch(0 .. 1)", e2.ToString()); + IntExpr e3 = (x == 7).Square(); + Assert.Equal("IntSquare(Watch(0 .. 1))", e3.ToString()); + } + + [Fact] + public void GreaterIntExprConstructor() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + // Unary Operator + IntExpr e1 = -(x >= 7); + Assert.Equal("-(Watch= 7>(0 .. 1))", e1.ToString()); + IntExpr e2 = (x >= 7).Abs(); + Assert.Equal("Watch= 7>(0 .. 1)", e2.ToString()); + IntExpr e3 = (x >= 7).Square(); + Assert.Equal("IntSquare(Watch= 7>(0 .. 1))", e3.ToString()); + } + + [Fact] + public void ConstraintAndScalar() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + Constraint c1 = (x == 7); + Assert.Equal("(x(3..13) == 7)", c1.ToString()); + + // Arithmetic operator with a scalar + IntExpr e2a = c1 + 1; + Assert.Equal("(Watch(0 .. 1) + 1)", e2a.ToString()); + IntExpr e2b = 1 + c1; + Assert.Equal("(Watch(0 .. 1) + 1)", e2b.ToString()); + + IntExpr e2c = c1 - 1; + Assert.Equal("(Watch(0 .. 1) + -1)", e2c.ToString()); + IntExpr e2d = 1 - c1; + Assert.Equal("Not(Watch(0 .. 1))", e2d.ToString()); + + IntExpr e2e = c1 * 2; + Assert.Equal("(Watch(0 .. 1) * 2)", e2e.ToString()); + IntExpr e2f = 2 * c1; + Assert.Equal("(Watch(0 .. 1) * 2)", e2f.ToString()); + + IntExpr e2g = c1 / 4; + Assert.Equal("(Watch(0 .. 1) div 4)", e2g.ToString()); + + // Relational operator with a scalar + Constraint c8a = c1 == 1; + Assert.Equal("(Watch(0 .. 1) == 1)", c8a.ToString()); + Constraint c8b = 1 == c1; + Assert.Equal("(Watch(0 .. 1) == 1)", c8b.ToString()); + + Constraint c8c = c1 != 1; + Assert.Equal("(Watch(0 .. 1) != 1)", c8c.ToString()); + Constraint c8d = 1 != c1; + Assert.Equal("(Watch(0 .. 1) != 1)", c8d.ToString()); + + Constraint c8e = c1 >= 1; + Assert.Equal("(Watch(0 .. 1) >= 1)", c8e.ToString()); + Constraint c8f = 1 >= c1; + Assert.Equal("TrueConstraint()", c8f.ToString()); + + Constraint c8g = c1 > 1; + Assert.Equal("FalseConstraint()", c8g.ToString()); + Constraint c8h = 1 > c1; + Assert.Equal("(Watch(0 .. 1) <= 0)", c8h.ToString()); + + Constraint c8i = c1 <= 1; + Assert.Equal("TrueConstraint()", c8i.ToString()); + Constraint c8j = 1 <= c1; + Assert.Equal("(Watch(0 .. 1) >= 1)", c8j.ToString()); + + Constraint c8k = c1 < 1; + Assert.Equal("(Watch(0 .. 1) <= 0)", c8k.ToString()); + Constraint c8l = 1 < c1; + Assert.Equal("FalseConstraint()", c8l.ToString()); + } + + [Fact] + public void ConstraintAndIntVar() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + Constraint c1 = x == 7; + Assert.Equal("(x(3..13) == 7)", c1.ToString()); + + IntVar y = solver.MakeIntVar(5, 17, "y"); + Assert.Equal(5, y.Min()); + Assert.Equal(17, y.Max()); + + // Arithmetic operator with IntVar + IntExpr e3a = c1 + y; + Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3a.ToString()); + IntExpr e3b = y + c1; + Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3b.ToString()); + + IntExpr e3c = c1 - y; + Assert.Equal("(Watch(0 .. 1) - y(5..17))", e3c.ToString()); + IntExpr e3d = y - c1; + Assert.Equal("(y(5..17) - Watch(0 .. 1))", e3d.ToString()); + + IntExpr e3e = c1 * y; + Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3e.ToString()); + IntExpr e3f = y * c1; + Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3f.ToString()); + + // Relational operator with an IntVar + Constraint c9a = c1 == y; + Assert.Equal("Watch(0 .. 1) == y(5..17)", c9a.ToString()); + Constraint c9b = y == c1; + Assert.Equal("y(5..17) == Watch(0 .. 1)", c9b.ToString()); + + Constraint c9c = c1 != y; + Assert.Equal("Watch(0 .. 1) != y(5..17)", c9c.ToString()); + Constraint c9d = y != c1; + Assert.Equal("y(5..17) != Watch(0 .. 1)", c9d.ToString()); + + Constraint c9e = c1 >= y; + Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9e.ToString()); + Constraint c9f = y >= c1; + Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9f.ToString()); + + Constraint c9g = c1 > y; + Assert.Equal("y(5..17) < Watch(0 .. 1)", c9g.ToString()); + Constraint c9h = y > c1; + Assert.Equal("Watch(0 .. 1) < y(5..17)", c9h.ToString()); + + Constraint c9i = c1 <= y; + Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9i.ToString()); + Constraint c9j = y <= c1; + Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9j.ToString()); + + Constraint c9k = c1 < y; + Assert.Equal("Watch(0 .. 1) < y(5..17)", c9k.ToString()); + Constraint c9l = y < c1; + Assert.Equal("y(5..17) < Watch(0 .. 1)", c9l.ToString()); + } + + [Fact] + public void ConstraintAndIntExpr() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + Constraint c1 = x == 7; + Assert.Equal("(x(3..13) == 7)", c1.ToString()); + + IntVar y = solver.MakeIntVar(5, 17, "y"); + Assert.Equal(5, y.Min()); + Assert.Equal(17, y.Max()); + + // Arithmetic operator with an IntExpr + IntExpr e11a = c1 + (y == 11); + Assert.Equal("(Watch(0 .. 1) + Watch(0 .. 1))", e11a.ToString()); + IntExpr e11b = (y == 11) + c1; + Assert.Equal("(Watch(0 .. 1) + Watch(0 .. 1))", e11b.ToString()); + IntExpr e11c = c1 - (y == 11); + Assert.Equal("(Watch(0 .. 1) - Watch(0 .. 1))", e11c.ToString()); + IntExpr e11d = (y == 11) - c1; + Assert.Equal("(Watch(0 .. 1) - Watch(0 .. 1))", e11d.ToString()); + IntExpr e11e = c1 * (y == 11); + Assert.Equal("(Watch(0 .. 1) * Watch(0 .. 1))", e11e.ToString()); + IntExpr e11f = (y == 11) * c1; + Assert.Equal("(Watch(0 .. 1) * Watch(0 .. 1))", e11f.ToString()); + + // Relational operator with an IntExpr + Constraint c12a = c1 == (y == 11); + Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c12a.ToString()); + Constraint c12b = (y == 11) == c1; + Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c12b.ToString()); + Constraint c12c = c1 != (y == 11); + Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c12c.ToString()); + Constraint c12d = (y == 11) != c1; + Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c12d.ToString()); + Constraint c12e = c1 >= (y == 11); + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12e.ToString()); + Constraint c12f = (y == 11) >= c1; + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12f.ToString()); + Constraint c12g = c1 > (y == 11); + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12g.ToString()); + Constraint c12h = (y == 11) > c1; + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12h.ToString()); + Constraint c12i = c1 <= (y == 11); + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12i.ToString()); + Constraint c12j = (y == 11) <= c1; + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c12j.ToString()); + Constraint c12k = c1 < (y == 11); + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12k.ToString()); + Constraint c12l = (y == 11) < c1; + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c12l.ToString()); + } + + [Fact] + public void ConstraintAndConstraint() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + Constraint c1 = x == 7; + Assert.Equal("(x(3..13) == 7)", c1.ToString()); + + IntVar y = solver.MakeIntVar(5, 17, "y"); + Assert.Equal(5, y.Min()); + Assert.Equal(17, y.Max()); + Constraint c2 = y == 11; + Assert.Equal("(y(5..17) == 11)", c2.ToString()); + + // Relational operator with a Constraint + Constraint c10a = c1 == c2; + Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c10a.ToString()); + + Constraint c10b = c1 != c2; + Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c10b.ToString()); + + Constraint c10c = c1 <= c2; + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10c.ToString()); + Constraint c10d = c1 >= c2; + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10d.ToString()); + + Constraint c10e = c1 > c2; + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10e.ToString()); + Constraint c10f = c1 < c2; + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10f.ToString()); + } + + [Fact] + public void IntExprAndScalar() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + // Arithmetic operator with a scalar + IntExpr e2a = (x == 7) + 1; + Assert.Equal("(Watch(0 .. 1) + 1)", e2a.ToString()); + IntExpr e2b = 1 + (x == 7); + Assert.Equal("(Watch(0 .. 1) + 1)", e2b.ToString()); + + IntExpr e2c = (x == 7) - 1; + Assert.Equal("(Watch(0 .. 1) + -1)", e2c.ToString()); + IntExpr e2d = 1 - (x == 7); + Assert.Equal("Not(Watch(0 .. 1))", e2d.ToString()); + + IntExpr e2e = (x == 7) * 2; + Assert.Equal("(Watch(0 .. 1) * 2)", e2e.ToString()); + IntExpr e2f = 2 * (x == 7); + Assert.Equal("(Watch(0 .. 1) * 2)", e2f.ToString()); + + IntExpr e2g = (x == 7) / 4; + Assert.Equal("(Watch(0 .. 1) div 4)", e2g.ToString()); + + // Relational operator with a scalar + Constraint c8a = (x == 7) == 1; + Assert.Equal("(Watch(0 .. 1) == 1)", c8a.ToString()); + Constraint c8b = 1 == (x == 7); + Assert.Equal("(Watch(0 .. 1) == 1)", c8b.ToString()); + + Constraint c8c = (x == 7) != 1; + Assert.Equal("(Watch(0 .. 1) != 1)", c8c.ToString()); + Constraint c8d = 1 != (x == 7); + Assert.Equal("(Watch(0 .. 1) != 1)", c8d.ToString()); + + Constraint c8e = (x == 7) >= 1; + Assert.Equal("(Watch(0 .. 1) >= 1)", c8e.ToString()); + Constraint c8f = 1 >= (x == 7); + Assert.Equal("TrueConstraint()", c8f.ToString()); + + Constraint c8g = (x == 7) > 1; + Assert.Equal("FalseConstraint()", c8g.ToString()); + Constraint c8h = 1 > (x == 7); + Assert.Equal("(Watch(0 .. 1) <= 0)", c8h.ToString()); + + Constraint c8i = (x == 7) <= 1; + Assert.Equal("TrueConstraint()", c8i.ToString()); + Constraint c8j = 1 <= (x == 7); + Assert.Equal("(Watch(0 .. 1) >= 1)", c8j.ToString()); + + Constraint c8k = (x == 7) < 1; + Assert.Equal("(Watch(0 .. 1) <= 0)", c8k.ToString()); + Constraint c8l = 1 < (x == 7); + Assert.Equal("FalseConstraint()", c8l.ToString()); + } + + [Fact] + public void IntExprAndIntVar() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + IntVar y = solver.MakeIntVar(5, 17, "y"); + Assert.Equal(5, y.Min()); + Assert.Equal(17, y.Max()); + + // Arithmetic operator with IntVar + IntExpr e3a = (x == 7) + y; + Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3a.ToString()); + IntExpr e3b = y + (x == 7); + Assert.Equal("(Watch(0 .. 1) + y(5..17))", e3b.ToString()); + + IntExpr e3c = (x == 7) - y; + Assert.Equal("(Watch(0 .. 1) - y(5..17))", e3c.ToString()); + IntExpr e3d = y - (x == 7); + Assert.Equal("(y(5..17) - Watch(0 .. 1))", e3d.ToString()); + + IntExpr e3e = (x == 7) * y; + Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3e.ToString()); + IntExpr e3f = y * (x == 7); + Assert.Equal("(Watch(0 .. 1) * y(5..17))", e3f.ToString()); + + // Relational operator with an IntVar + Constraint c9a = (x == 7) == y; + Assert.Equal("Watch(0 .. 1) == y(5..17)", c9a.ToString()); + Constraint c9b = y == (x == 7); + Assert.Equal("y(5..17) == Watch(0 .. 1)", c9b.ToString()); + + Constraint c9c = (x == 7) != y; + Assert.Equal("Watch(0 .. 1) != y(5..17)", c9c.ToString()); + Constraint c9d = y != (x == 7); + Assert.Equal("y(5..17) != Watch(0 .. 1)", c9d.ToString()); + + Constraint c9e = (x == 7) >= y; + Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9e.ToString()); + Constraint c9f = y >= (x == 7); + Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9f.ToString()); + + Constraint c9g = (x == 7) > y; + Assert.Equal("y(5..17) < Watch(0 .. 1)", c9g.ToString()); + Constraint c9h = y > (x == 7); + Assert.Equal("Watch(0 .. 1) < y(5..17)", c9h.ToString()); + + Constraint c9i = (x == 7) <= y; + Assert.Equal("Watch(0 .. 1) <= y(5..17)", c9i.ToString()); + Constraint c9j = y <= (x == 7); + Assert.Equal("y(5..17) <= Watch(0 .. 1)", c9j.ToString()); + + Constraint c9k = (x == 7) < y; + Assert.Equal("Watch(0 .. 1) < y(5..17)", c9k.ToString()); + Constraint c9l = y < (x == 7); + Assert.Equal("y(5..17) < Watch(0 .. 1)", c9l.ToString()); + } + + [Fact] + public void IntExprAndIntExpr() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + IntVar y = solver.MakeIntVar(5, 17, "y"); + Assert.Equal(5, y.Min()); + Assert.Equal(17, y.Max()); + + // Relational operator between IntExpr + Constraint c10a = (x == 7) == (y == 11); + Assert.Equal("Watch(0 .. 1) == Watch(0 .. 1)", c10a.ToString()); + + Constraint c10b = (x == 7) != (y == 11); + Assert.Equal("Watch(0 .. 1) != Watch(0 .. 1)", c10b.ToString()); + + Constraint c10c = (x == 7) <= (y == 11); + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10c.ToString()); + Constraint c10d = (x == 7) >= (y == 11); + Assert.Equal("Watch(0 .. 1) <= Watch(0 .. 1)", c10d.ToString()); + + Constraint c10e = (x == 7) > (y == 11); + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10e.ToString()); + Constraint c10f = (x == 7) < (y == 11); + Assert.Equal("Watch(0 .. 1) < Watch(0 .. 1)", c10f.ToString()); + } + + [Fact] + public void GreaterIntExprAndScalar() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + // Arithmetic operator with a scalar + IntExpr e2a = (x >= 7) + 1; + Assert.Equal("(Watch= 7>(0 .. 1) + 1)", e2a.ToString()); + IntExpr e2b = 1 + (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) + 1)", e2b.ToString()); + + IntExpr e2c = (x >= 7) - 1; + Assert.Equal("(Watch= 7>(0 .. 1) + -1)", e2c.ToString()); + IntExpr e2d = 1 - (x >= 7); + Assert.Equal("Not(Watch= 7>(0 .. 1))", e2d.ToString()); + + IntExpr e2e = (x >= 7) * 2; + Assert.Equal("(Watch= 7>(0 .. 1) * 2)", e2e.ToString()); + IntExpr e2f = 2 * (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) * 2)", e2f.ToString()); + + // Relational operator with a scalar + Constraint c8a = (x >= 7) == 1; + Assert.Equal("(Watch= 7>(0 .. 1) == 1)", c8a.ToString()); + Constraint c8b = 1 == (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) == 1)", c8b.ToString()); + + Constraint c8c = (x >= 7) != 1; + Assert.Equal("(Watch= 7>(0 .. 1) != 1)", c8c.ToString()); + Constraint c8d = 1 != (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) != 1)", c8d.ToString()); + + Constraint c8e = (x >= 7) >= 1; + Assert.Equal("(Watch= 7>(0 .. 1) >= 1)", c8e.ToString()); + Constraint c8f = 1 >= (x >= 7); + Assert.Equal("TrueConstraint()", c8f.ToString()); + + Constraint c8g = (x >= 7) > 1; + Assert.Equal("FalseConstraint()", c8g.ToString()); + Constraint c8h = 1 > (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) <= 0)", c8h.ToString()); + + Constraint c8i = (x >= 7) <= 1; + Assert.Equal("TrueConstraint()", c8i.ToString()); + Constraint c8j = 1 <= (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) >= 1)", c8j.ToString()); + + Constraint c8k = (x >= 7) < 1; + Assert.Equal("(Watch= 7>(0 .. 1) <= 0)", c8k.ToString()); + Constraint c8l = 1 < (x >= 7); + Assert.Equal("FalseConstraint()", c8l.ToString()); + } + + [Fact] + public void GreaterIntExprAndIntVar() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + IntVar y = solver.MakeIntVar(5, 17, "y"); + Assert.Equal(5, y.Min()); + Assert.Equal(17, y.Max()); + + // Arithmetic operator with IntVar + IntExpr e3a = (x >= 7) + y; + Assert.Equal("(Watch= 7>(0 .. 1) + y(5..17))", e3a.ToString()); + IntExpr e3b = y + (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) + y(5..17))", e3b.ToString()); + + IntExpr e3c = (x >= 7) - y; + Assert.Equal("(Watch= 7>(0 .. 1) - y(5..17))", e3c.ToString()); + IntExpr e3d = y - (x >= 7); + Assert.Equal("(y(5..17) - Watch= 7>(0 .. 1))", e3d.ToString()); + + IntExpr e3e = (x >= 7) * y; + Assert.Equal("(Watch= 7>(0 .. 1) * y(5..17))", e3e.ToString()); + IntExpr e3f = y * (x >= 7); + Assert.Equal("(Watch= 7>(0 .. 1) * y(5..17))", e3f.ToString()); + + // Relational operator with an IntVar + Constraint c9a = (x >= 7) == y; + Assert.Equal("Watch= 7>(0 .. 1) == y(5..17)", c9a.ToString()); + Constraint c9b = y == (x >= 7); + Assert.Equal("y(5..17) == Watch= 7>(0 .. 1)", c9b.ToString()); + + Constraint c9c = (x >= 7) != y; + Assert.Equal("Watch= 7>(0 .. 1) != y(5..17)", c9c.ToString()); + Constraint c9d = y != (x >= 7); + Assert.Equal("y(5..17) != Watch= 7>(0 .. 1)", c9d.ToString()); + + Constraint c9e = (x >= 7) >= y; + Assert.Equal("y(5..17) <= Watch= 7>(0 .. 1)", c9e.ToString()); + Constraint c9f = y >= (x >= 7); + Assert.Equal("Watch= 7>(0 .. 1) <= y(5..17)", c9f.ToString()); + + Constraint c9g = (x >= 7) > y; + Assert.Equal("y(5..17) < Watch= 7>(0 .. 1)", c9g.ToString()); + Constraint c9h = y > (x >= 7); + Assert.Equal("Watch= 7>(0 .. 1) < y(5..17)", c9h.ToString()); + + Constraint c9i = (x >= 7) <= y; + Assert.Equal("Watch= 7>(0 .. 1) <= y(5..17)", c9i.ToString()); + Constraint c9j = y <= (x >= 7); + Assert.Equal("y(5..17) <= Watch= 7>(0 .. 1)", c9j.ToString()); + + Constraint c9k = (x >= 7) < y; + Assert.Equal("Watch= 7>(0 .. 1) < y(5..17)", c9k.ToString()); + Constraint c9l = y < (x >= 7); + Assert.Equal("y(5..17) < Watch= 7>(0 .. 1)", c9l.ToString()); + } + + [Fact] + public void GreaterIntExprAndGreaterIntExpr() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(3, 13, "x"); + Assert.Equal(3, x.Min()); + Assert.Equal(13, x.Max()); + + IntVar y = solver.MakeIntVar(5, 17, "y"); + Assert.Equal(5, y.Min()); + Assert.Equal(17, y.Max()); + + // Relational operator between IntExpr + Constraint c10a = (x >= 7) == (y >= 11); + Assert.Equal("Watch= 7>(0 .. 1) == Watch= 11>(0 .. 1)", c10a.ToString()); + + Constraint c10b = (x >= 7) != (y >= 11); + Assert.Equal("Watch= 7>(0 .. 1) != Watch= 11>(0 .. 1)", c10b.ToString()); + + Constraint c10c = (x >= 7) <= (y >= 11); + Assert.Equal("Watch= 7>(0 .. 1) <= Watch= 11>(0 .. 1)", c10c.ToString()); + Constraint c10d = (x >= 7) >= (y >= 11); + Assert.Equal("Watch= 11>(0 .. 1) <= Watch= 7>(0 .. 1)", c10d.ToString()); + + Constraint c10e = (x >= 7) > (y >= 11); + Assert.Equal("Watch= 11>(0 .. 1) < Watch= 7>(0 .. 1)", c10e.ToString()); + Constraint c10f = (x >= 7) < (y >= 11); + Assert.Equal("Watch= 7>(0 .. 1) < Watch= 11>(0 .. 1)", c10f.ToString()); + } + + [Fact] + public void Downcast() + { + Solver solver = new Solver("Solver"); + IntVar x = solver.MakeIntVar(2, 17, "x"); + IntExpr e = x + 5; + IntVar y = e.Var(); + Assert.Equal("(x(2..17) + 5)", y.ToString()); + } + + [Fact] + public void Sequence() + { + Solver solver = new Solver("Solver"); + IntervalVar[] intervals = solver.MakeFixedDurationIntervalVarArray(10, 0, 10, 5, false, "task"); + DisjunctiveConstraint disjunctive = intervals.Disjunctive("Sequence"); + SequenceVar var = disjunctive.SequenceVar(); + Assignment ass = solver.MakeAssignment(); + ass.Add(var); + ass.SetForwardSequence(var, new int[] { 1, 3, 5 }); + int[] seq = ass.ForwardSequence(var); + Assert.Equal(3, seq.Length); + Assert.Equal(1, seq[0]); + Assert.Equal(3, seq[1]); + Assert.Equal(5, seq[2]); + } + + // A simple demon that simply sets the maximum of a fixed IntVar to 10 when + // it's being called. + class SetMaxDemon : NetDemon + { + public SetMaxDemon(IntVar x) + { + x_ = x; + } + public override void Run(Solver s) + { + x_.SetMax(10); + } + private IntVar x_; + } + + [Fact] + public void Demon() + { + Solver solver = new Solver("DemonTest"); + IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); + NetDemon demon = new SetMaxDemon(x); + Assert.Equal(11, x.Max()); + demon.Run(solver); + Assert.Equal(10, x.Max()); + } + + // This constraint has a single target variable x. It enforces x >= 5 upon + // InitialPropagate() and invokes the SetMaxDemon when x changes its range. + class SetMinAndMaxConstraint : NetConstraint + { + public SetMinAndMaxConstraint(Solver solver, IntVar x) : base(solver) + { + x_ = x; + } + public override void Post() + { + // Always store the demon in the constraint to avoid it being reclaimed + // by the GC. + demon_ = new SetMaxDemon(x_); + x_.WhenBound(demon_); + } + public override void InitialPropagate() + { + x_.SetMin(5); + } + private IntVar x_; + private Demon demon_; + } + + [Fact] + public void MinAndMaxConstraint() + { + Solver solver = new Solver("TestConstraint"); + IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); + Constraint ct = new SetMinAndMaxConstraint(solver, x); + solver.Add(ct); + DecisionBuilder db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + solver.NewSearch(db); + Assert.Equal(-1, x.Min()); + Assert.Equal(11, x.Max()); + + Assert.True(solver.NextSolution()); + Assert.Equal(6, x.Min()); + Assert.Equal(6, x.Max()); + + Assert.True(solver.NextSolution()); + Assert.Equal(10, x.Min()); + Assert.Equal(10, x.Max()); + + Assert.False(solver.NextSolution()); + solver.EndSearch(); + } + + // // This constraint has a single target variable x. It enforces x >= 5, + // but only at the leaf of the search tree: it doesn't change x's bounds, + // and simply fails if x is bound and is < 5. + class DumbGreaterOrEqualToFive : NetConstraint + { + public DumbGreaterOrEqualToFive(Solver solver, IntVar x) : base(solver) + { + x_ = x; + } + public override void Post() + { + demon_ = solver().MakeConstraintInitialPropagateCallback(this); + x_.WhenRange(demon_); + } + public override void InitialPropagate() + { + if (x_.Bound()) + { + if (x_.Value() < 5) + { + solver().Fail(); + } + } + } + private IntVar x_; + private Demon demon_; + } + + [Fact] + public void FailingConstraint() + { + Solver solver = new Solver("TestConstraint"); + IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); + Constraint ct = new DumbGreaterOrEqualToFive(solver, x); + solver.Add(ct); + DecisionBuilder db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + solver.NewSearch(db); + Assert.True(solver.NextSolution()); + Assert.Equal(6, x.Min()); + solver.EndSearch(); + } + + [Fact] + public void DomainIterator() + { + Solver solver = new Solver("TestConstraint"); + IntVar x = solver.MakeIntVar(new int[] { 2, 4, -1, 6, 11, 10 }, "x"); + ulong count = 0; + foreach (long value in x.GetDomain()) + { + count++; + } + Assert.Equal(count, x.Size()); + } + + class CountHoles : NetDemon + { + public CountHoles(IntVar x) + { + x_ = x; + count_ = 0; + } + public override void Run(Solver s) + { + foreach (long removed in x_.GetHoles()) + { + count_++; + } + } + public int count() + { + return count_; + } + private IntVar x_; + private int count_; + } + + class RemoveThreeValues : NetConstraint + { + public RemoveThreeValues(Solver solver, IntVar x) : base(solver) + { + x_ = x; + } + public override void Post() + { + demon_ = new CountHoles(x_); + x_.WhenDomain(demon_); + } + public override void InitialPropagate() + { + x_.RemoveValues(new long[] { 3, 5, 7 }); + } + public int count() + { + return demon_.count(); + } + private IntVar x_; + private CountHoles demon_; + } + + [Fact] + public void HoleIteratorTest() + { + Solver solver = new Solver("TestConstraint"); + IntVar x = solver.MakeIntVar(0, 10, "x"); + RemoveThreeValues ct = new RemoveThreeValues(solver, x); + solver.Add(ct); + DecisionBuilder db = solver.MakePhase(x, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + solver.Solve(db); + Assert.Equal(3, ct.count()); + } + + // TODO(user): Improve search log tests; currently only tests coverage. + void RunSearchLog(in SearchMonitor searchlog) + { + searchlog.EnterSearch(); + searchlog.ExitSearch(); + searchlog.AcceptSolution(); + searchlog.AtSolution(); + searchlog.BeginFail(); + searchlog.NoMoreSolutions(); + searchlog.BeginInitialPropagation(); + searchlog.EndInitialPropagation(); + } + + [Fact] + public void SearchLog() + { + Solver solver = new Solver("TestSearchLog"); + IntVar var = solver.MakeIntVar(1, 1, "Variable"); + OptimizeVar objective = solver.MakeMinimize(var, 1); + SearchMonitor searchlog = solver.MakeSearchLog(0); + RunSearchLog(in searchlog); + GC.KeepAlive(solver); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SearchLogWithCallback(bool callGC) + { + Solver solver = new Solver("TestSearchLog"); + IntVar var = solver.MakeIntVar(1, 1, "Variable"); + OptimizeVar objective = solver.MakeMinimize(var, 1); + int count = 0; + SearchMonitor searchlog = solver.MakeSearchLog(0, // branch period + () => + { + count++; + return "display callback..."; + }); + if (callGC) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + RunSearchLog(in searchlog); + GC.KeepAlive(solver); + Assert.Equal(1, count); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SearchLogWithObjectiveAndCallback(bool callGC) + { + Solver solver = new Solver("TestSearchLog"); + IntVar var = solver.MakeIntVar(1, 1, "Variable"); + OptimizeVar objective = solver.MakeMinimize(var, 1); + int count = 0; + SearchMonitor searchlog = solver.MakeSearchLog(0, // branch period + objective, // objective var to monitor + () => + { + count++; + return "OptimizeVar display callback"; + }); + if (callGC) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + RunSearchLog(in searchlog); + GC.KeepAlive(solver); + Assert.Equal(1, count); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SearchLogWithIntVarAndCallback(bool callGC) + { + Solver solver = new Solver("TestSearchLog"); + IntVar var = solver.MakeIntVar(1, 1, "Variable"); + OptimizeVar objective = solver.MakeMinimize(var, 1); + int count = 0; + SearchMonitor searchlog = solver.MakeSearchLog(0, // branch period + var, // int var to monitor + () => + { + count++; + return "IntVar display callback"; + }); + if (callGC) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + RunSearchLog(in searchlog); + GC.KeepAlive(solver); + Assert.Equal(1, count); + } +} } // namespace Google.OrTools.Tests diff --git a/examples/tests/InitTests.cs b/examples/tests/InitTests.cs index 70037ee03e..aa2bd28bab 100644 --- a/examples/tests/InitTests.cs +++ b/examples/tests/InitTests.cs @@ -18,36 +18,36 @@ using static Google.OrTools.Init.operations_research_init; namespace Google.OrTools.Tests { - public class InitTest +public class InitTest +{ + [Fact] + public void CheckLogging() { - [Fact] - public void CheckLogging() - { - Init.CppBridge.InitLogging("init"); - Init.CppBridge.ShutdownLogging(); - } - - [Fact] - public void CheckFlags() - { - Init.CppFlags cpp_flags = new Init.CppFlags(); - cpp_flags.logtostderr = true; - cpp_flags.log_prefix = true; - cpp_flags.cp_model_dump_prefix = "init"; - cpp_flags.cp_model_dump_models = true; - cpp_flags.cp_model_dump_lns = true; - cpp_flags.cp_model_dump_response = true; - Init.CppBridge.SetFlags(cpp_flags); - } - - [Fact] - public void CheckOrToolsVersion() - { - int major = OrToolsVersion.MajorNumber(); - int minor = OrToolsVersion.MinorNumber(); - int patch = OrToolsVersion.PatchNumber(); - string version = OrToolsVersion.VersionString(); - Assert.Equal($"{major}.{minor}.{patch}", version); - } + Init.CppBridge.InitLogging("init"); + Init.CppBridge.ShutdownLogging(); } + + [Fact] + public void CheckFlags() + { + Init.CppFlags cpp_flags = new Init.CppFlags(); + cpp_flags.logtostderr = true; + cpp_flags.log_prefix = true; + cpp_flags.cp_model_dump_prefix = "init"; + cpp_flags.cp_model_dump_models = true; + cpp_flags.cp_model_dump_lns = true; + cpp_flags.cp_model_dump_response = true; + Init.CppBridge.SetFlags(cpp_flags); + } + + [Fact] + public void CheckOrToolsVersion() + { + int major = OrToolsVersion.MajorNumber(); + int minor = OrToolsVersion.MinorNumber(); + int patch = OrToolsVersion.PatchNumber(); + string version = OrToolsVersion.VersionString(); + Assert.Equal($"{major}.{minor}.{patch}", version); + } +} } // namespace Google.OrTools.Tests diff --git a/examples/tests/LinearSolverTests.cs b/examples/tests/LinearSolverTests.cs index d8ea5ece93..1ea51350dc 100644 --- a/examples/tests/LinearSolverTests.cs +++ b/examples/tests/LinearSolverTests.cs @@ -17,402 +17,402 @@ using Google.OrTools.LinearSolver; namespace Google.OrTools.Tests { - public class LinearSolverTest +public class LinearSolverTest +{ + [Fact] + public void VarOperator() { - [Fact] - public void VarOperator() + Solver solver = Solver.CreateSolver("CLP"); + Variable x = solver.MakeNumVar(0.0, 100.0, "x"); + Assert.Equal(0.0, x.Lb()); + Assert.Equal(100.0, x.Ub()); + + Constraint ct1 = solver.Add(x >= 1); + Assert.Equal(1.0, ct1.GetCoefficient(x)); + Assert.Equal(1.0, ct1.Lb()); + Assert.Equal(double.PositiveInfinity, ct1.Ub()); + + Constraint ct2 = solver.Add(x <= 1); + Assert.Equal(1.0, ct2.GetCoefficient(x)); + Assert.Equal(double.NegativeInfinity, ct2.Lb()); + Assert.Equal(1.0, ct2.Ub()); + + Constraint ct3 = solver.Add(x == 1); + Assert.Equal(1.0, ct3.GetCoefficient(x)); + Assert.Equal(1.0, ct3.Lb()); + Assert.Equal(1.0, ct3.Ub()); + + Constraint ct4 = solver.Add(1 >= x); + Assert.Equal(1.0, ct4.GetCoefficient(x)); + Assert.Equal(double.NegativeInfinity, ct4.Lb()); + Assert.Equal(1.0, ct4.Ub()); + + Constraint ct5 = solver.Add(1 <= x); + Assert.Equal(1.0, ct5.GetCoefficient(x)); + Assert.Equal(1.0, ct5.Lb()); + Assert.Equal(double.PositiveInfinity, ct5.Ub()); + + Constraint ct6 = solver.Add(1 == x); + Assert.Equal(1.0, ct6.GetCoefficient(x)); + Assert.Equal(1.0, ct6.Lb()); + Assert.Equal(1.0, ct6.Ub()); + } + + [Fact] + public void VarAddition() + { + Solver solver = Solver.CreateSolver("CLP"); + Variable x = solver.MakeNumVar(0.0, 100.0, "x"); + Assert.Equal(0.0, x.Lb()); + Assert.Equal(100.0, x.Ub()); + + Variable y = solver.MakeNumVar(0.0, 100.0, "y"); + Assert.Equal(0.0, y.Lb()); + Assert.Equal(100.0, y.Ub()); + + Constraint ct1 = solver.Add(x + y == 1); + Assert.Equal(1.0, ct1.GetCoefficient(x)); + Assert.Equal(1.0, ct1.GetCoefficient(y)); + + Constraint ct2 = solver.Add(x + x == 1); + Assert.Equal(2.0, ct2.GetCoefficient(x)); + + Constraint ct3 = solver.Add(x + (y + x) == 1); + Assert.Equal(2.0, ct3.GetCoefficient(x)); + Assert.Equal(1.0, ct3.GetCoefficient(y)); + + Constraint ct4 = solver.Add(x + (y + x + 3) == 1); + Assert.Equal(2.0, ct4.GetCoefficient(x)); + Assert.Equal(1.0, ct4.GetCoefficient(y)); + Assert.Equal(-2.0, ct4.Lb()); + Assert.Equal(-2.0, ct4.Ub()); + } + + [Fact] + public void VarMultiplication() + { + Solver solver = Solver.CreateSolver("CLP"); + Variable x = solver.MakeNumVar(0.0, 100.0, "x"); + Assert.Equal(0.0, x.Lb()); + Assert.Equal(100.0, x.Ub()); + + Variable y = solver.MakeNumVar(0.0, 100.0, "y"); + Assert.Equal(0.0, y.Lb()); + Assert.Equal(100.0, y.Ub()); + + Constraint ct1 = solver.Add(3 * x == 1); + Assert.Equal(3.0, ct1.GetCoefficient(x)); + + Constraint ct2 = solver.Add(x * 3 == 1); + Assert.Equal(3.0, ct2.GetCoefficient(x)); + + Constraint ct3 = solver.Add(x + (2 * y + 3 * x) == 1); + Assert.Equal(4.0, ct3.GetCoefficient(x)); + Assert.Equal(2.0, ct3.GetCoefficient(y)); + + Constraint ct4 = solver.Add(x + 5 * (y + x + 3) == 1); + Assert.Equal(6.0, ct4.GetCoefficient(x)); + Assert.Equal(5.0, ct4.GetCoefficient(y)); + Assert.Equal(-14.0, ct4.Lb()); + Assert.Equal(-14.0, ct4.Ub()); + + Constraint ct5 = solver.Add(x + (2 * y + x + 3) * 3 == 1); + Assert.Equal(4.0, ct5.GetCoefficient(x)); + Assert.Equal(6.0, ct5.GetCoefficient(y)); + Assert.Equal(-8.0, ct5.Lb()); + Assert.Equal(-8.0, ct5.Ub()); + } + + [Fact] + public void BinaryOperator() + { + Solver solver = Solver.CreateSolver("CLP"); + Variable x = solver.MakeNumVar(0.0, 100.0, "x"); + Assert.Equal(0.0, x.Lb()); + Assert.Equal(100.0, x.Ub()); + + Variable y = solver.MakeNumVar(0.0, 100.0, "y"); + Assert.Equal(0.0, y.Lb()); + Assert.Equal(100.0, y.Ub()); + + Constraint ct1 = solver.Add(x == y); + Assert.Equal(1.0, ct1.GetCoefficient(x)); + Assert.Equal(-1.0, ct1.GetCoefficient(y)); + + Constraint ct2 = solver.Add(x == 3 * y + 5); + Assert.Equal(1.0, ct2.GetCoefficient(x)); + Assert.Equal(-3.0, ct2.GetCoefficient(y)); + Assert.Equal(5.0, ct2.Lb()); + Assert.Equal(5.0, ct2.Ub()); + + Constraint ct3 = solver.Add(2 * x - 9 == y); + Assert.Equal(2.0, ct3.GetCoefficient(x)); + Assert.Equal(-1.0, ct3.GetCoefficient(y)); + Assert.Equal(9.0, ct3.Lb()); + Assert.Equal(9.0, ct3.Ub()); + + Assert.True(x == x); + Assert.True(!(x != x)); + Assert.True((x != y)); + Assert.True(!(x == y)); + } + + [Fact] + public void Inequalities() + { + Solver solver = Solver.CreateSolver("CLP"); + Variable x = solver.MakeNumVar(0.0, 100.0, "x"); + Assert.Equal(0.0, x.Lb()); + Assert.Equal(100.0, x.Ub()); + + Variable y = solver.MakeNumVar(0.0, 100.0, "y"); + Assert.Equal(0.0, y.Lb()); + Assert.Equal(100.0, y.Ub()); + + Constraint ct1 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) >= 3); + Assert.Equal(7.0, ct1.GetCoefficient(x)); + Assert.Equal(5.0, ct1.GetCoefficient(y)); + Assert.Equal(2.0, ct1.Lb()); + Assert.Equal(double.PositiveInfinity, ct1.Ub()); + + Constraint ct2 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) <= 3); + Assert.Equal(7.0, ct2.GetCoefficient(x)); + Assert.Equal(5.0, ct2.GetCoefficient(y)); + Assert.Equal(double.NegativeInfinity, ct2.Lb()); + Assert.Equal(2.0, ct2.Ub()); + + Constraint ct3 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) >= 3 - x - y); + Assert.Equal(8.0, ct3.GetCoefficient(x)); + Assert.Equal(6.0, ct3.GetCoefficient(y)); + Assert.Equal(2.0, ct3.Lb()); + Assert.Equal(double.PositiveInfinity, ct3.Ub()); + + Constraint ct4 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) <= -x - y + 3); + Assert.Equal(8.0, ct4.GetCoefficient(x)); + Assert.Equal(6.0, ct4.GetCoefficient(y)); + Assert.Equal(double.NegativeInfinity, ct4.Lb()); + Assert.Equal(2.0, ct4.Ub()); + } + + [Fact] + public void SumArray() + { + Solver solver = Solver.CreateSolver("CLP"); + + Variable[] x = solver.MakeBoolVarArray(10, "x"); + Constraint ct1 = solver.Add(x.Sum() == 3); + Assert.Equal(1.0, ct1.GetCoefficient(x[0])); + + Constraint ct2 = solver.Add(-2 * x.Sum() == 3); + Assert.Equal(-2.0, ct2.GetCoefficient(x[0])); + + LinearExpr[] array = new LinearExpr[] { x[0] + 2.0, x[0] + 3, x[0] + 4 }; + Constraint ct3 = solver.Add(array.Sum() == 1); + Assert.Equal(3.0, ct3.GetCoefficient(x[0])); + Assert.Equal(-8.0, ct3.Lb()); + Assert.Equal(-8.0, ct3.Ub()); + } + + [Fact] + public void Objective() + { + Solver solver = Solver.CreateSolver("CLP"); + Variable x = solver.MakeNumVar(0.0, 100.0, "x"); + Assert.Equal(0.0, x.Lb()); + Assert.Equal(100.0, x.Ub()); + + Variable y = solver.MakeNumVar(0.0, 100.0, "y"); + Assert.Equal(0.0, y.Lb()); + Assert.Equal(100.0, y.Ub()); + + solver.Maximize(x); + Assert.Equal(0.0, solver.Objective().Offset()); + Assert.Equal(1.0, solver.Objective().GetCoefficient(x)); + Assert.True(solver.Objective().Maximization()); + + solver.Minimize(-x - 2 * y + 3); + Assert.Equal(3.0, solver.Objective().Offset()); + Assert.Equal(-1.0, solver.Objective().GetCoefficient(x)); + Assert.Equal(-2.0, solver.Objective().GetCoefficient(y)); + Assert.True(solver.Objective().Minimization()); + } + + void SolveAndPrint(in Solver solver, in Variable[] variables, in Constraint[] constraints) + { + Console.WriteLine($"Number of variables = {solver.NumVariables()}"); + Console.WriteLine($"Number of constraints = {solver.NumConstraints()}"); + + Solver.ResultStatus resultStatus = solver.Solve(); + // Check that the problem has an optimal solution. + if (resultStatus != Solver.ResultStatus.OPTIMAL) { - Solver solver = Solver.CreateSolver("CLP"); - Variable x = solver.MakeNumVar(0.0, 100.0, "x"); - Assert.Equal(0.0, x.Lb()); - Assert.Equal(100.0, x.Ub()); - - Constraint ct1 = solver.Add(x >= 1); - Assert.Equal(1.0, ct1.GetCoefficient(x)); - Assert.Equal(1.0, ct1.Lb()); - Assert.Equal(double.PositiveInfinity, ct1.Ub()); - - Constraint ct2 = solver.Add(x <= 1); - Assert.Equal(1.0, ct2.GetCoefficient(x)); - Assert.Equal(double.NegativeInfinity, ct2.Lb()); - Assert.Equal(1.0, ct2.Ub()); - - Constraint ct3 = solver.Add(x == 1); - Assert.Equal(1.0, ct3.GetCoefficient(x)); - Assert.Equal(1.0, ct3.Lb()); - Assert.Equal(1.0, ct3.Ub()); - - Constraint ct4 = solver.Add(1 >= x); - Assert.Equal(1.0, ct4.GetCoefficient(x)); - Assert.Equal(double.NegativeInfinity, ct4.Lb()); - Assert.Equal(1.0, ct4.Ub()); - - Constraint ct5 = solver.Add(1 <= x); - Assert.Equal(1.0, ct5.GetCoefficient(x)); - Assert.Equal(1.0, ct5.Lb()); - Assert.Equal(double.PositiveInfinity, ct5.Ub()); - - Constraint ct6 = solver.Add(1 == x); - Assert.Equal(1.0, ct6.GetCoefficient(x)); - Assert.Equal(1.0, ct6.Lb()); - Assert.Equal(1.0, ct6.Ub()); + Console.WriteLine("The problem does not have an optimal solution!"); } - - [Fact] - public void VarAddition() + else { - Solver solver = Solver.CreateSolver("CLP"); - Variable x = solver.MakeNumVar(0.0, 100.0, "x"); - Assert.Equal(0.0, x.Lb()); - Assert.Equal(100.0, x.Ub()); - - Variable y = solver.MakeNumVar(0.0, 100.0, "y"); - Assert.Equal(0.0, y.Lb()); - Assert.Equal(100.0, y.Ub()); - - Constraint ct1 = solver.Add(x + y == 1); - Assert.Equal(1.0, ct1.GetCoefficient(x)); - Assert.Equal(1.0, ct1.GetCoefficient(y)); - - Constraint ct2 = solver.Add(x + x == 1); - Assert.Equal(2.0, ct2.GetCoefficient(x)); - - Constraint ct3 = solver.Add(x + (y + x) == 1); - Assert.Equal(2.0, ct3.GetCoefficient(x)); - Assert.Equal(1.0, ct3.GetCoefficient(y)); - - Constraint ct4 = solver.Add(x + (y + x + 3) == 1); - Assert.Equal(2.0, ct4.GetCoefficient(x)); - Assert.Equal(1.0, ct4.GetCoefficient(y)); - Assert.Equal(-2.0, ct4.Lb()); - Assert.Equal(-2.0, ct4.Ub()); - } - - [Fact] - public void VarMultiplication() - { - Solver solver = Solver.CreateSolver("CLP"); - Variable x = solver.MakeNumVar(0.0, 100.0, "x"); - Assert.Equal(0.0, x.Lb()); - Assert.Equal(100.0, x.Ub()); - - Variable y = solver.MakeNumVar(0.0, 100.0, "y"); - Assert.Equal(0.0, y.Lb()); - Assert.Equal(100.0, y.Ub()); - - Constraint ct1 = solver.Add(3 * x == 1); - Assert.Equal(3.0, ct1.GetCoefficient(x)); - - Constraint ct2 = solver.Add(x * 3 == 1); - Assert.Equal(3.0, ct2.GetCoefficient(x)); - - Constraint ct3 = solver.Add(x + (2 * y + 3 * x) == 1); - Assert.Equal(4.0, ct3.GetCoefficient(x)); - Assert.Equal(2.0, ct3.GetCoefficient(y)); - - Constraint ct4 = solver.Add(x + 5 * (y + x + 3) == 1); - Assert.Equal(6.0, ct4.GetCoefficient(x)); - Assert.Equal(5.0, ct4.GetCoefficient(y)); - Assert.Equal(-14.0, ct4.Lb()); - Assert.Equal(-14.0, ct4.Ub()); - - Constraint ct5 = solver.Add(x + (2 * y + x + 3) * 3 == 1); - Assert.Equal(4.0, ct5.GetCoefficient(x)); - Assert.Equal(6.0, ct5.GetCoefficient(y)); - Assert.Equal(-8.0, ct5.Lb()); - Assert.Equal(-8.0, ct5.Ub()); - } - - [Fact] - public void BinaryOperator() - { - Solver solver = Solver.CreateSolver("CLP"); - Variable x = solver.MakeNumVar(0.0, 100.0, "x"); - Assert.Equal(0.0, x.Lb()); - Assert.Equal(100.0, x.Ub()); - - Variable y = solver.MakeNumVar(0.0, 100.0, "y"); - Assert.Equal(0.0, y.Lb()); - Assert.Equal(100.0, y.Ub()); - - Constraint ct1 = solver.Add(x == y); - Assert.Equal(1.0, ct1.GetCoefficient(x)); - Assert.Equal(-1.0, ct1.GetCoefficient(y)); - - Constraint ct2 = solver.Add(x == 3 * y + 5); - Assert.Equal(1.0, ct2.GetCoefficient(x)); - Assert.Equal(-3.0, ct2.GetCoefficient(y)); - Assert.Equal(5.0, ct2.Lb()); - Assert.Equal(5.0, ct2.Ub()); - - Constraint ct3 = solver.Add(2 * x - 9 == y); - Assert.Equal(2.0, ct3.GetCoefficient(x)); - Assert.Equal(-1.0, ct3.GetCoefficient(y)); - Assert.Equal(9.0, ct3.Lb()); - Assert.Equal(9.0, ct3.Ub()); - - Assert.True(x == x); - Assert.True(!(x != x)); - Assert.True((x != y)); - Assert.True(!(x == y)); - } - - [Fact] - public void Inequalities() - { - Solver solver = Solver.CreateSolver("CLP"); - Variable x = solver.MakeNumVar(0.0, 100.0, "x"); - Assert.Equal(0.0, x.Lb()); - Assert.Equal(100.0, x.Ub()); - - Variable y = solver.MakeNumVar(0.0, 100.0, "y"); - Assert.Equal(0.0, y.Lb()); - Assert.Equal(100.0, y.Ub()); - - Constraint ct1 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) >= 3); - Assert.Equal(7.0, ct1.GetCoefficient(x)); - Assert.Equal(5.0, ct1.GetCoefficient(y)); - Assert.Equal(2.0, ct1.Lb()); - Assert.Equal(double.PositiveInfinity, ct1.Ub()); - - Constraint ct2 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) <= 3); - Assert.Equal(7.0, ct2.GetCoefficient(x)); - Assert.Equal(5.0, ct2.GetCoefficient(y)); - Assert.Equal(double.NegativeInfinity, ct2.Lb()); - Assert.Equal(2.0, ct2.Ub()); - - Constraint ct3 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) >= 3 - x - y); - Assert.Equal(8.0, ct3.GetCoefficient(x)); - Assert.Equal(6.0, ct3.GetCoefficient(y)); - Assert.Equal(2.0, ct3.Lb()); - Assert.Equal(double.PositiveInfinity, ct3.Ub()); - - Constraint ct4 = solver.Add(2 * (x + 3) + 5 * (y + x - 1) <= -x - y + 3); - Assert.Equal(8.0, ct4.GetCoefficient(x)); - Assert.Equal(6.0, ct4.GetCoefficient(y)); - Assert.Equal(double.NegativeInfinity, ct4.Lb()); - Assert.Equal(2.0, ct4.Ub()); - } - - [Fact] - public void SumArray() - { - Solver solver = Solver.CreateSolver("CLP"); - - Variable[] x = solver.MakeBoolVarArray(10, "x"); - Constraint ct1 = solver.Add(x.Sum() == 3); - Assert.Equal(1.0, ct1.GetCoefficient(x[0])); - - Constraint ct2 = solver.Add(-2 * x.Sum() == 3); - Assert.Equal(-2.0, ct2.GetCoefficient(x[0])); - - LinearExpr[] array = new LinearExpr[] { x[0] + 2.0, x[0] + 3, x[0] + 4 }; - Constraint ct3 = solver.Add(array.Sum() == 1); - Assert.Equal(3.0, ct3.GetCoefficient(x[0])); - Assert.Equal(-8.0, ct3.Lb()); - Assert.Equal(-8.0, ct3.Ub()); - } - - [Fact] - public void Objective() - { - Solver solver = Solver.CreateSolver("CLP"); - Variable x = solver.MakeNumVar(0.0, 100.0, "x"); - Assert.Equal(0.0, x.Lb()); - Assert.Equal(100.0, x.Ub()); - - Variable y = solver.MakeNumVar(0.0, 100.0, "y"); - Assert.Equal(0.0, y.Lb()); - Assert.Equal(100.0, y.Ub()); - - solver.Maximize(x); - Assert.Equal(0.0, solver.Objective().Offset()); - Assert.Equal(1.0, solver.Objective().GetCoefficient(x)); - Assert.True(solver.Objective().Maximization()); - - solver.Minimize(-x - 2 * y + 3); - Assert.Equal(3.0, solver.Objective().Offset()); - Assert.Equal(-1.0, solver.Objective().GetCoefficient(x)); - Assert.Equal(-2.0, solver.Objective().GetCoefficient(y)); - Assert.True(solver.Objective().Minimization()); - } - - void SolveAndPrint(in Solver solver, in Variable[] variables, in Constraint[] constraints) - { - Console.WriteLine($"Number of variables = {solver.NumVariables()}"); - Console.WriteLine($"Number of constraints = {solver.NumConstraints()}"); - - Solver.ResultStatus resultStatus = solver.Solve(); - // Check that the problem has an optimal solution. - if (resultStatus != Solver.ResultStatus.OPTIMAL) + Console.WriteLine("Solution:"); + foreach (Variable var in variables) { - Console.WriteLine("The problem does not have an optimal solution!"); + Console.WriteLine($"{var.Name()} = {var.SolutionValue()}"); } - else + Console.WriteLine($"Optimal objective value = {solver.Objective().Value()}"); + Console.WriteLine(""); + Console.WriteLine("Advanced usage:"); + Console.WriteLine($"Problem solved in {solver.WallTime()} milliseconds"); + Console.WriteLine($"Problem solved in {solver.Iterations()} iterations"); + foreach (Variable var in variables) { - Console.WriteLine("Solution:"); - foreach (Variable var in variables) - { - Console.WriteLine($"{var.Name()} = {var.SolutionValue()}"); - } - Console.WriteLine($"Optimal objective value = {solver.Objective().Value()}"); - Console.WriteLine(""); - Console.WriteLine("Advanced usage:"); - Console.WriteLine($"Problem solved in {solver.WallTime()} milliseconds"); - Console.WriteLine($"Problem solved in {solver.Iterations()} iterations"); - foreach (Variable var in variables) - { - Console.WriteLine($"{var.Name()}: reduced cost {var.ReducedCost()}"); - } - - double[] activities = solver.ComputeConstraintActivities(); - foreach (Constraint ct in constraints) - { - Console.WriteLine($"{ct.Name()}: dual value = {ct.DualValue()}", - $" activity = {activities[ct.Index()]}"); - } + Console.WriteLine($"{var.Name()}: reduced cost {var.ReducedCost()}"); } - } - void RunLinearProgrammingExample(in String problemType) - { - Console.WriteLine($"------ Linear programming example with {problemType} ------"); - - Solver solver = Solver.CreateSolver(problemType); - if (solver == null) - return; - - // x and y are continuous non-negative variables. - Variable x = solver.MakeNumVar(0.0, double.PositiveInfinity, "x"); - Variable y = solver.MakeNumVar(0.0, double.PositiveInfinity, "y"); - - // Objectif function: Maximize 3x + 4y. - Objective objective = solver.Objective(); - objective.SetCoefficient(x, 3); - objective.SetCoefficient(y, 4); - objective.SetMaximization(); - - // x + 2y <= 14. - Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 14.0, "c0"); - c0.SetCoefficient(x, 1); - c0.SetCoefficient(y, 2); - - // 3x - y >= 0. - Constraint c1 = solver.MakeConstraint(0.0, double.PositiveInfinity, "c1"); - c1.SetCoefficient(x, 3); - c1.SetCoefficient(y, -1); - - // x - y <= 2. - Constraint c2 = solver.MakeConstraint(double.NegativeInfinity, 2.0, "c2"); - c2.SetCoefficient(x, 1); - c2.SetCoefficient(y, -1); - - SolveAndPrint(solver, new Variable[] { x, y }, new Constraint[] { c0, c1, c2 }); - } - void RunMixedIntegerProgrammingExample(in String problemType) - { - Console.WriteLine($"------ Mixed integer programming example with {problemType} ------"); - - Solver solver = Solver.CreateSolver(problemType); - if (solver == null) - return; - - // x and y are integers non-negative variables. - Variable x = solver.MakeIntVar(0.0, double.PositiveInfinity, "x"); - Variable y = solver.MakeIntVar(0.0, double.PositiveInfinity, "y"); - - // Objectif function: Maximize x + 10 * y. - Objective objective = solver.Objective(); - objective.SetCoefficient(x, 1); - objective.SetCoefficient(y, 10); - objective.SetMaximization(); - - // x + 7 * y <= 17.5. - Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 17.5, "c0"); - c0.SetCoefficient(x, 1); - c0.SetCoefficient(y, 7); - - // x <= 3.5. - Constraint c1 = solver.MakeConstraint(double.NegativeInfinity, 3.5, "c1"); - c1.SetCoefficient(x, 1); - c1.SetCoefficient(y, 0); - - SolveAndPrint(solver, new Variable[] { x, y }, new Constraint[] { c0, c1 }); - } - void RunBooleanProgrammingExample(in String problemType) - { - Console.WriteLine($"------ Boolean programming example with {problemType} ------"); - - Solver solver = Solver.CreateSolver(problemType); - if (solver == null) - return; - - // x and y are boolean variables. - Variable x = solver.MakeBoolVar("x"); - Variable y = solver.MakeBoolVar("y"); - - // Objectif function: Maximize 2 * x + y. - Objective objective = solver.Objective(); - objective.SetCoefficient(x, 2); - objective.SetCoefficient(y, 1); - objective.SetMinimization(); - - // 1 <= x + 2 * y <= 3. - Constraint c0 = solver.MakeConstraint(1, 3, "c0"); - c0.SetCoefficient(x, 1); - c0.SetCoefficient(y, 2); - - SolveAndPrint(solver, new Variable[] { x, y }, new Constraint[] { c0 }); - } - - [Fact] - public void OptimizationProblemType() - { - RunLinearProgrammingExample("GLOP"); - RunLinearProgrammingExample("GLPK_LP"); - RunLinearProgrammingExample("CLP"); - RunLinearProgrammingExample("GUROBI_LP"); - - RunMixedIntegerProgrammingExample("GLPK"); - RunMixedIntegerProgrammingExample("CBC"); - RunMixedIntegerProgrammingExample("SCIP"); - RunMixedIntegerProgrammingExample("SAT"); - - RunBooleanProgrammingExample("SAT"); - RunBooleanProgrammingExample("BOP"); - } - - [Fact] - static void testSetHintAndSolverGetters() - { - Console.WriteLine("testSetHintAndSolverGetters"); - Solver solver = Solver.CreateSolver("glop"); - // x and y are continuous non-negative variables. - Variable x = solver.MakeIntVar(0.0, double.PositiveInfinity, "x"); - Variable y = solver.MakeIntVar(0.0, double.PositiveInfinity, "y"); - - // Objectif function: Maximize x + 10 * y. - Objective objective = solver.Objective(); - objective.SetCoefficient(x, 1); - objective.SetCoefficient(y, 10); - objective.SetMaximization(); - - // x + 7 * y <= 17.5. - Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 17.5, "c0"); - c0.SetCoefficient(x, 1); - c0.SetCoefficient(y, 7); - - // x <= 3.5. - Constraint c1 = solver.MakeConstraint(double.NegativeInfinity, 3.5, "c1"); - c1.SetCoefficient(x, 1); - c1.SetCoefficient(y, 0); - - Constraint[] constraints = solver.constraints(); - Assert.Equal(constraints.Length, 2); - Variable[] variables = solver.variables(); - Assert.Equal(variables.Length, 2); - - solver.SetHint(new Variable[] { x, y }, new double[] { 2.0, 3.0 }); + double[] activities = solver.ComputeConstraintActivities(); + foreach (Constraint ct in constraints) + { + Console.WriteLine($"{ct.Name()}: dual value = {ct.DualValue()}", + $" activity = {activities[ct.Index()]}"); + } } } + + void RunLinearProgrammingExample(in String problemType) + { + Console.WriteLine($"------ Linear programming example with {problemType} ------"); + + Solver solver = Solver.CreateSolver(problemType); + if (solver == null) + return; + + // x and y are continuous non-negative variables. + Variable x = solver.MakeNumVar(0.0, double.PositiveInfinity, "x"); + Variable y = solver.MakeNumVar(0.0, double.PositiveInfinity, "y"); + + // Objectif function: Maximize 3x + 4y. + Objective objective = solver.Objective(); + objective.SetCoefficient(x, 3); + objective.SetCoefficient(y, 4); + objective.SetMaximization(); + + // x + 2y <= 14. + Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 14.0, "c0"); + c0.SetCoefficient(x, 1); + c0.SetCoefficient(y, 2); + + // 3x - y >= 0. + Constraint c1 = solver.MakeConstraint(0.0, double.PositiveInfinity, "c1"); + c1.SetCoefficient(x, 3); + c1.SetCoefficient(y, -1); + + // x - y <= 2. + Constraint c2 = solver.MakeConstraint(double.NegativeInfinity, 2.0, "c2"); + c2.SetCoefficient(x, 1); + c2.SetCoefficient(y, -1); + + SolveAndPrint(solver, new Variable[] { x, y }, new Constraint[] { c0, c1, c2 }); + } + void RunMixedIntegerProgrammingExample(in String problemType) + { + Console.WriteLine($"------ Mixed integer programming example with {problemType} ------"); + + Solver solver = Solver.CreateSolver(problemType); + if (solver == null) + return; + + // x and y are integers non-negative variables. + Variable x = solver.MakeIntVar(0.0, double.PositiveInfinity, "x"); + Variable y = solver.MakeIntVar(0.0, double.PositiveInfinity, "y"); + + // Objectif function: Maximize x + 10 * y. + Objective objective = solver.Objective(); + objective.SetCoefficient(x, 1); + objective.SetCoefficient(y, 10); + objective.SetMaximization(); + + // x + 7 * y <= 17.5. + Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 17.5, "c0"); + c0.SetCoefficient(x, 1); + c0.SetCoefficient(y, 7); + + // x <= 3.5. + Constraint c1 = solver.MakeConstraint(double.NegativeInfinity, 3.5, "c1"); + c1.SetCoefficient(x, 1); + c1.SetCoefficient(y, 0); + + SolveAndPrint(solver, new Variable[] { x, y }, new Constraint[] { c0, c1 }); + } + void RunBooleanProgrammingExample(in String problemType) + { + Console.WriteLine($"------ Boolean programming example with {problemType} ------"); + + Solver solver = Solver.CreateSolver(problemType); + if (solver == null) + return; + + // x and y are boolean variables. + Variable x = solver.MakeBoolVar("x"); + Variable y = solver.MakeBoolVar("y"); + + // Objectif function: Maximize 2 * x + y. + Objective objective = solver.Objective(); + objective.SetCoefficient(x, 2); + objective.SetCoefficient(y, 1); + objective.SetMinimization(); + + // 1 <= x + 2 * y <= 3. + Constraint c0 = solver.MakeConstraint(1, 3, "c0"); + c0.SetCoefficient(x, 1); + c0.SetCoefficient(y, 2); + + SolveAndPrint(solver, new Variable[] { x, y }, new Constraint[] { c0 }); + } + + [Fact] + public void OptimizationProblemType() + { + RunLinearProgrammingExample("GLOP"); + RunLinearProgrammingExample("GLPK_LP"); + RunLinearProgrammingExample("CLP"); + RunLinearProgrammingExample("GUROBI_LP"); + + RunMixedIntegerProgrammingExample("GLPK"); + RunMixedIntegerProgrammingExample("CBC"); + RunMixedIntegerProgrammingExample("SCIP"); + RunMixedIntegerProgrammingExample("SAT"); + + RunBooleanProgrammingExample("SAT"); + RunBooleanProgrammingExample("BOP"); + } + + [Fact] + static void testSetHintAndSolverGetters() + { + Console.WriteLine("testSetHintAndSolverGetters"); + Solver solver = Solver.CreateSolver("glop"); + // x and y are continuous non-negative variables. + Variable x = solver.MakeIntVar(0.0, double.PositiveInfinity, "x"); + Variable y = solver.MakeIntVar(0.0, double.PositiveInfinity, "y"); + + // Objectif function: Maximize x + 10 * y. + Objective objective = solver.Objective(); + objective.SetCoefficient(x, 1); + objective.SetCoefficient(y, 10); + objective.SetMaximization(); + + // x + 7 * y <= 17.5. + Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 17.5, "c0"); + c0.SetCoefficient(x, 1); + c0.SetCoefficient(y, 7); + + // x <= 3.5. + Constraint c1 = solver.MakeConstraint(double.NegativeInfinity, 3.5, "c1"); + c1.SetCoefficient(x, 1); + c1.SetCoefficient(y, 0); + + Constraint[] constraints = solver.constraints(); + Assert.Equal(constraints.Length, 2); + Variable[] variables = solver.variables(); + Assert.Equal(variables.Length, 2); + + solver.SetHint(new Variable[] { x, y }, new double[] { 2.0, 3.0 }); + } +} } // namespace Google.OrTools.Tests diff --git a/examples/tests/RoutingSolverTests.cs b/examples/tests/RoutingSolverTests.cs index 90a5b4413f..548ac2be19 100644 --- a/examples/tests/RoutingSolverTests.cs +++ b/examples/tests/RoutingSolverTests.cs @@ -17,191 +17,186 @@ using Google.OrTools.ConstraintSolver; namespace Google.OrTools.Tests { - public class RoutingSolverTest +public class RoutingSolverTest +{ + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SimpleLambdaCallback(bool callGC) { - [Theory] - [InlineData(false)] - [InlineData(true)] - public void SimpleLambdaCallback(bool callGC) + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); + // Create a distance callback. + int transitCallbackIndex = routing.RegisterTransitCallback((long fromIndex, long toIndex) => + { + // Convert from routing variable Index to + // distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return Math.Abs(toNode - fromNode); + }); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + if (callGC) { - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); - // Create a distance callback. - int transitCallbackIndex = routing.RegisterTransitCallback((long fromIndex, long toIndex) => { - // Convert from routing variable Index to distance matrix NodeIndex. - var fromNode = manager.IndexToNode(fromIndex); - var toNode = manager.IndexToNode(toIndex); - return Math.Abs(toNode - fromNode); - }); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); - if (callGC) - { - GC.Collect(); - } - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - Assignment solution = routing.SolveWithParameters(searchParameters); - // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+4)-> 0 := +8 - Assert.Equal(8, solution.ObjectiveValue()); - } - - [Fact] - public void TestTransitMatrix() - { - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); - // Create a distance callback. - long[][] matrix = new long[][] { - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - }; - int transitCallbackIndex = routing.RegisterTransitMatrix(matrix); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - Assignment solution = routing.SolveWithParameters(searchParameters); - // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 - Assert.Equal(5, solution.ObjectiveValue()); - } - - [Fact] - public void TestTransitCallback() - { - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); - // Create a distance callback. - int transitCallbackIndex = routing.RegisterTransitCallback( - (long fromIndex, long toIndex) => { - // Convert from routing variable Index to distance matrix NodeIndex. - var fromNode = manager.IndexToNode(fromIndex); - var toNode = manager.IndexToNode(toIndex); - return Math.Abs(toNode - fromNode); - }); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - Assignment solution = routing.SolveWithParameters(searchParameters); - Assert.Equal(8, solution.ObjectiveValue()); - } - - [Fact] - public void TestMatrixDimension() - { - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); - // Create a distance callback. - long[][] matrix = new long[][] { - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - new long[] {1, 1, 1, 1, 1}, - }; - IntBoolPair result = routing.AddMatrixDimension( - matrix, - /*capacity=*/10, - /*fix_start_cumul_to_zero=*/true, - "Dimension"); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(result.first); - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - Assignment solution = routing.SolveWithParameters(searchParameters); - // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 - Assert.Equal(5, solution.ObjectiveValue()); - } - - [Fact] - public void TestUnaryTransitVector() - { - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); - // Create a distance callback. - long[] vector = {1, 1, 1, 1, 1}; - int transitCallbackIndex = routing.RegisterUnaryTransitVector(vector); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - Assignment solution = routing.SolveWithParameters(searchParameters); - // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 - Assert.Equal(5, solution.ObjectiveValue()); - } - - [Fact] - public void TestUnaryTransitCallback() - { - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); - // Create a distance callback. - int transitCallbackIndex = routing.RegisterUnaryTransitCallback( - (long fromIndex) => { - // Convert from routing variable Index to distance matrix NodeIndex. - var fromNode = manager.IndexToNode(fromIndex); - return fromNode + 1; - }); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - Assignment solution = routing.SolveWithParameters(searchParameters); - // 0 --(+1)-> 1 --(+2)-> 2 --(+3)-> 3 --(+4)-> 4 --(+5)-> 0 := +15 - Assert.Equal(15, solution.ObjectiveValue()); - } - - [Fact] - public void TestVectorDimension() - { - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); - // Create a distance callback. - long[] vector = new long[] {1, 1, 1, 1, 1}; - IntBoolPair result = routing.AddVectorDimension( - vector, - /*capacity=*/10, - /*fix_start_cumul_to_zero=*/true, - "Dimension"); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(result.first); - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - Assignment solution = routing.SolveWithParameters(searchParameters); - // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 - Assert.Equal(5, solution.ObjectiveValue()); + GC.Collect(); } + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + Assignment solution = routing.SolveWithParameters(searchParameters); + // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+4)-> 0 := +8 + Assert.Equal(8, solution.ObjectiveValue()); } + + [Fact] + public void TestTransitMatrix() + { + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); + // Create a distance callback. + long[][] matrix = new long[][] { + new long[] { 1, 1, 1, 1, 1 }, new long[] { 1, 1, 1, 1, 1 }, new long[] { 1, 1, 1, 1, 1 }, + new long[] { 1, 1, 1, 1, 1 }, new long[] { 1, 1, 1, 1, 1 }, + }; + int transitCallbackIndex = routing.RegisterTransitMatrix(matrix); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + Assignment solution = routing.SolveWithParameters(searchParameters); + // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 + Assert.Equal(5, solution.ObjectiveValue()); + } + + [Fact] + public void TestTransitCallback() + { + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); + // Create a distance callback. + int transitCallbackIndex = routing.RegisterTransitCallback((long fromIndex, long toIndex) => + { + // Convert from routing variable Index to + // distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return Math.Abs(toNode - fromNode); + }); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + Assignment solution = routing.SolveWithParameters(searchParameters); + Assert.Equal(8, solution.ObjectiveValue()); + } + + [Fact] + public void TestMatrixDimension() + { + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); + // Create a distance callback. + long[][] matrix = new long[][] { + new long[] { 1, 1, 1, 1, 1 }, new long[] { 1, 1, 1, 1, 1 }, new long[] { 1, 1, 1, 1, 1 }, + new long[] { 1, 1, 1, 1, 1 }, new long[] { 1, 1, 1, 1, 1 }, + }; + IntBoolPair result = routing.AddMatrixDimension(matrix, + /*capacity=*/10, + /*fix_start_cumul_to_zero=*/true, "Dimension"); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(result.first); + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + Assignment solution = routing.SolveWithParameters(searchParameters); + // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 + Assert.Equal(5, solution.ObjectiveValue()); + } + + [Fact] + public void TestUnaryTransitVector() + { + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); + // Create a distance callback. + long[] vector = { 1, 1, 1, 1, 1 }; + int transitCallbackIndex = routing.RegisterUnaryTransitVector(vector); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + Assignment solution = routing.SolveWithParameters(searchParameters); + // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 + Assert.Equal(5, solution.ObjectiveValue()); + } + + [Fact] + public void TestUnaryTransitCallback() + { + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); + // Create a distance callback. + int transitCallbackIndex = routing.RegisterUnaryTransitCallback((long fromIndex) => + { + // Convert from routing variable Index to + // distance matrix NodeIndex. + var fromNode = + manager.IndexToNode(fromIndex); + return fromNode + 1; + }); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + Assignment solution = routing.SolveWithParameters(searchParameters); + // 0 --(+1)-> 1 --(+2)-> 2 --(+3)-> 3 --(+4)-> 4 --(+5)-> 0 := +15 + Assert.Equal(15, solution.ObjectiveValue()); + } + + [Fact] + public void TestVectorDimension() + { + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); + // Create a distance callback. + long[] vector = new long[] { 1, 1, 1, 1, 1 }; + IntBoolPair result = routing.AddVectorDimension(vector, + /*capacity=*/10, + /*fix_start_cumul_to_zero=*/true, "Dimension"); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(result.first); + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + Assignment solution = routing.SolveWithParameters(searchParameters); + // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5 + Assert.Equal(5, solution.ObjectiveValue()); + } +} } // namespace Google.OrTools.Tests diff --git a/examples/tests/SatSolverTests.cs b/examples/tests/SatSolverTests.cs index c9f092d4b8..bbc8908960 100644 --- a/examples/tests/SatSolverTests.cs +++ b/examples/tests/SatSolverTests.cs @@ -18,376 +18,437 @@ using Google.OrTools.Sat; namespace Google.OrTools.Tests { - public class SatSolverTest +public class SatSolverTest +{ + static IntegerVariableProto NewIntegerVariable(long lb, long ub) { - static IntegerVariableProto NewIntegerVariable(long lb, long ub) + IntegerVariableProto var = new IntegerVariableProto(); + var.Domain.Add(lb); + var.Domain.Add(ub); + return var; + } + + static ConstraintProto NewLinear2(int v1, int v2, long c1, long c2, long lb, long ub) + { + LinearConstraintProto linear = new LinearConstraintProto(); + linear.Vars.Add(v1); + linear.Vars.Add(v2); + linear.Coeffs.Add(c1); + linear.Coeffs.Add(c2); + linear.Domain.Add(lb); + linear.Domain.Add(ub); + ConstraintProto ct = new ConstraintProto(); + ct.Linear = linear; + return ct; + } + + static ConstraintProto NewLinear3(int v1, int v2, int v3, long c1, long c2, long c3, long lb, long ub) + { + LinearConstraintProto linear = new LinearConstraintProto(); + linear.Vars.Add(v1); + linear.Vars.Add(v2); + linear.Vars.Add(v3); + linear.Coeffs.Add(c1); + linear.Coeffs.Add(c2); + linear.Coeffs.Add(c3); + linear.Domain.Add(lb); + linear.Domain.Add(ub); + ConstraintProto ct = new ConstraintProto(); + ct.Linear = linear; + return ct; + } + + static CpObjectiveProto NewMinimize1(int v1, long c1) + { + CpObjectiveProto obj = new CpObjectiveProto(); + obj.Vars.Add(v1); + obj.Coeffs.Add(c1); + return obj; + } + + static CpObjectiveProto NewMaximize1(int v1, long c1) + { + CpObjectiveProto obj = new CpObjectiveProto(); + obj.Vars.Add(-v1 - 1); + obj.Coeffs.Add(c1); + obj.ScalingFactor = -1; + return obj; + } + + static CpObjectiveProto NewMaximize2(int v1, int v2, long c1, long c2) + { + CpObjectiveProto obj = new CpObjectiveProto(); + obj.Vars.Add(-v1 - 1); + obj.Vars.Add(-v2 - 1); + obj.Coeffs.Add(c1); + obj.Coeffs.Add(c2); + obj.ScalingFactor = -1; + return obj; + } + + // CpModelProto + [Fact] + public void SimpleLinearModelProto() + { + CpModelProto model = new CpModelProto(); + model.Variables.Add(NewIntegerVariable(-10, 10)); + model.Variables.Add(NewIntegerVariable(-10, 10)); + model.Variables.Add(NewIntegerVariable(-1000000, 1000000)); + model.Constraints.Add(NewLinear2(0, 1, 1, 1, -1000000, 100000)); + model.Constraints.Add(NewLinear3(0, 1, 2, 1, 2, -1, 0, 100000)); + model.Objective = NewMaximize1(2, 1); + // Console.WriteLine("model = " + model.ToString()); + SolveWrapper solve_wrapper = new SolveWrapper(); + CpSolverResponse response = solve_wrapper.Solve(model); + Assert.Equal(CpSolverStatus.Optimal, response.Status); + Assert.Equal(30, response.ObjectiveValue); + Assert.Equal(new long[] { 10, 10, 30 }, response.Solution); + // Console.WriteLine("response = " + response.ToString()); + } + + [Fact] + public void SimpleLinearModelProto2() + { + CpModelProto model = new CpModelProto(); + model.Variables.Add(NewIntegerVariable(-10, 10)); + model.Variables.Add(NewIntegerVariable(-10, 10)); + model.Constraints.Add(NewLinear2(0, 1, 1, 1, -1000000, 100000)); + model.Objective = NewMaximize2(0, 1, 1, -2); + // Console.WriteLine("model = " + model.ToString()); + + SolveWrapper solve_wrapper = new SolveWrapper(); + CpSolverResponse response = solve_wrapper.Solve(model); + Assert.Equal(CpSolverStatus.Optimal, response.Status); + Assert.Equal(30, response.ObjectiveValue); + Assert.Equal(new long[] { 10, -10 }, response.Solution); + // Console.WriteLine("response = " + response.ToString()); + } + + // CpModel + [Fact] + public void SimpleLinearModel() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(-10, 10, "v1"); + IntVar v2 = model.NewIntVar(-10, 10, "v2"); + IntVar v3 = model.NewIntVar(-100000, 100000, "v3"); + model.AddLinearConstraint(v1 + v2, -1000000, 100000); + model.AddLinearConstraint(v1 + 2 * v2 - v3, 0, 100000); + model.Maximize(v3); + Assert.Equal(v1.Domain.FlattenedIntervals(), new long[] { -10, 10 }); + // Console.WriteLine("model = " + model.Model.ToString()); + + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Assert.Equal(CpSolverStatus.Optimal, status); + + CpSolverResponse response = solver.Response; + Assert.Equal(30, response.ObjectiveValue); + Assert.Equal(new long[] { 10, 10, 30 }, response.Solution); + // Console.WriteLine("response = " + reponse.ToString()); + } + + [Fact] + public void SimpleLinearModel2() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(-10, 10, "v1"); + IntVar v2 = model.NewIntVar(-10, 10, "v2"); + model.AddLinearConstraint(v1 + v2, -1000000, 100000); + model.Maximize(v1 - 2 * v2); + // Console.WriteLine("model = " + model.Model.ToString()); + + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Assert.Equal(CpSolverStatus.Optimal, status); + + CpSolverResponse response = solver.Response; + Assert.Equal(30, response.ObjectiveValue); + Assert.Equal(new long[] { 10, -10 }, response.Solution); + // Console.WriteLine("response = " + reponse.ToString()); + } + + [Fact] + public void SimpleLinearModel3() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(-10, 10, "v1"); + IntVar v2 = model.NewIntVar(-10, 10, "v2"); + model.Add(-100000 <= v1 + 2 * v2 <= 100000); + model.Minimize(v1 - 2 * v2); + // Console.WriteLine("model = " + model.Model.ToString()); + + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Assert.Equal(CpSolverStatus.Optimal, status); + + CpSolverResponse response = solver.Response; + Assert.Equal(-10, solver.Value(v1)); + Assert.Equal(10, solver.Value(v2)); + Assert.Equal(new long[] { -10, 10 }, response.Solution); + Assert.Equal(-30, solver.Value(v1 - 2 * v2)); + Assert.Equal(-30, response.ObjectiveValue); + // Console.WriteLine("response = " + reponse.ToString()); + } + + [Fact] + public void NegativeIntVar() + { + CpModel model = new CpModel(); + IntVar boolvar = model.NewBoolVar("boolvar"); + IntVar x = model.NewIntVar(0, 10, "x"); + IntVar delta = model.NewIntVar(-5, 5, "delta"); + IntVar squaredDelta = model.NewIntVar(0, 25, "squaredDelta"); + model.Add(x == boolvar * 4); + model.Add(delta == x - 5); + model.AddMultiplicationEquality(squaredDelta, new IntVar[] { delta, delta }); + model.Minimize(squaredDelta); + // Console.WriteLine("model = " + model.Model.ToString()); + + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + CpSolverResponse response = solver.Response; + Console.WriteLine("response = " + response.ToString()); + + Assert.Equal(CpSolverStatus.Optimal, status); + + Assert.Equal(1, solver.Value(boolvar)); + Assert.Equal(4, solver.Value(x)); + Assert.Equal(-1, solver.Value(delta)); + Assert.Equal(1, solver.Value(squaredDelta)); + Assert.Equal(new long[] { 1, 4, -1, 1 }, response.Solution); + Assert.Equal(1.0, response.ObjectiveValue, 5); + } + + [Fact] + public void NegativeSquareVar() + { + CpModel model = new CpModel(); + BoolVar boolvar = model.NewBoolVar("boolvar"); + IntVar x = model.NewIntVar(0, 10, "x"); + IntVar delta = model.NewIntVar(-5, 5, "delta"); + IntVar squaredDelta = model.NewIntVar(0, 25, "squaredDelta"); + model.Add(x == 4).OnlyEnforceIf(boolvar); + model.Add(x == 0).OnlyEnforceIf(boolvar.Not()); + model.Add(delta == x - 5); + long[,] tuples = { { -5, 25 }, { -4, 16 }, { -3, 9 }, { -2, 4 }, { -1, 1 }, { 0, 0 }, + { 1, 1 }, { 2, 4 }, { 3, 9 }, { 4, 16 }, { 5, 25 } }; + model.AddAllowedAssignments(new IntVar[] { delta, squaredDelta }, tuples); + model.Minimize(squaredDelta); + + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + + CpSolverResponse response = solver.Response; + Assert.Equal(1, solver.Value(boolvar)); + Assert.Equal(4, solver.Value(x)); + Assert.Equal(-1, solver.Value(delta)); + Assert.Equal(1, solver.Value(squaredDelta)); + Assert.Equal(new long[] { 1, 4, -1, 1 }, response.Solution); + Assert.Equal(1.0, response.ObjectiveValue, 6); + } + + [Fact] + public void Division() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(0, 10, "v1"); + IntVar v2 = model.NewIntVar(1, 10, "v2"); + model.AddDivisionEquality(3, v1, v2); + // Console.WriteLine(model.Model); + + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Assert.Equal(CpSolverStatus.Optimal, status); + + CpSolverResponse response = solver.Response; + Assert.Equal(3, solver.Value(v1)); + Assert.Equal(1, solver.Value(v2)); + Assert.Equal(new long[] { 3, 1 }, response.Solution); + Assert.Equal(0, response.ObjectiveValue); + // Console.WriteLine("response = " + reponse.ToString()); + } + + [Fact] + public void Modulo() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(1, 10, "v1"); + IntVar v2 = model.NewIntVar(1, 10, "v2"); + model.AddModuloEquality(3, v1, v2); + // Console.WriteLine(model.Model); + + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Assert.Equal(CpSolverStatus.Optimal, status); + + CpSolverResponse response = solver.Response; + Assert.Equal(3, solver.Value(v1)); + Assert.Equal(4, solver.Value(v2)); + Assert.Equal(new long[] { 3, 4 }, response.Solution); + Assert.Equal(0, response.ObjectiveValue); + // Console.WriteLine("response = " + reponse.ToString()); + } + + [Fact] + public void LargeWeightedSumLong() + { + CpModel model = new CpModel(); + List vars = new List(); + List coeffs = new List(); + + for (int i = 0; i < 100000; ++i) { - IntegerVariableProto var = new IntegerVariableProto(); - var.Domain.Add(lb); - var.Domain.Add(ub); - return var; + vars.Add(model.NewBoolVar("")); + coeffs.Add(i + 1); } - static ConstraintProto NewLinear2(int v1, int v2, long c1, long c2, long lb, long ub) + var watch = System.Diagnostics.Stopwatch.StartNew(); + model.Minimize(LinearExpr.WeightedSum(vars, coeffs)); + watch.Stop(); + var elapsedMs = watch.ElapsedMilliseconds; + Console.WriteLine($"Long: Elapsed time {elapsedMs}"); + } + + [Fact] + public void LargeWeightedSumInt() + { + CpModel model = new CpModel(); + List vars = new List(); + List coeffs = new List(); + + for (int i = 0; i < 100000; ++i) { - LinearConstraintProto linear = new LinearConstraintProto(); - linear.Vars.Add(v1); - linear.Vars.Add(v2); - linear.Coeffs.Add(c1); - linear.Coeffs.Add(c2); - linear.Domain.Add(lb); - linear.Domain.Add(ub); - ConstraintProto ct = new ConstraintProto(); - ct.Linear = linear; - return ct; + vars.Add(model.NewBoolVar("")); + coeffs.Add(i); } - static ConstraintProto NewLinear3(int v1, int v2, int v3, long c1, long c2, long c3, long lb, long ub) + var watch = System.Diagnostics.Stopwatch.StartNew(); + model.Minimize(LinearExpr.WeightedSum(vars, coeffs)); + watch.Stop(); + var elapsedMs = watch.ElapsedMilliseconds; + Console.WriteLine($"Int: Elapsed time {elapsedMs}"); + } + + [Fact] + public void LargeWeightedSumExpr() + { + CpModel model = new CpModel(); + List exprs = new List(); + + for (int i = 0; i < 100000; ++i) { - LinearConstraintProto linear = new LinearConstraintProto(); - linear.Vars.Add(v1); - linear.Vars.Add(v2); - linear.Vars.Add(v3); - linear.Coeffs.Add(c1); - linear.Coeffs.Add(c2); - linear.Coeffs.Add(c3); - linear.Domain.Add(lb); - linear.Domain.Add(ub); - ConstraintProto ct = new ConstraintProto(); - ct.Linear = linear; - return ct; + exprs.Add(model.NewBoolVar("") * i); } - static CpObjectiveProto NewMinimize1(int v1, long c1) + var watch = System.Diagnostics.Stopwatch.StartNew(); + model.Minimize(LinearExpr.Sum(exprs)); + watch.Stop(); + var elapsedMs = watch.ElapsedMilliseconds; + Console.WriteLine($"Exprs: Elapsed time {elapsedMs}"); + } + + [Fact] + public void LargeWeightedSumBuilder() + { + CpModel model = new CpModel(); + List vars = new List(); + List coeffs = new List(); + + for (int i = 0; i < 100000; ++i) { - CpObjectiveProto obj = new CpObjectiveProto(); - obj.Vars.Add(v1); - obj.Coeffs.Add(c1); - return obj; + vars.Add(model.NewBoolVar("")); + coeffs.Add(i + 1); } - static CpObjectiveProto NewMaximize1(int v1, long c1) + var watch = System.Diagnostics.Stopwatch.StartNew(); + LinearExprBuilder obj = LinearExpr.NewBuilder(); + for (int i = 0; i < 100000; ++i) { - CpObjectiveProto obj = new CpObjectiveProto(); - obj.Vars.Add(-v1 - 1); - obj.Coeffs.Add(c1); - obj.ScalingFactor = -1; - return obj; + obj.AddTerm(vars[i], coeffs[i]); } + model.Minimize(obj); + watch.Stop(); + var elapsedMs = watch.ElapsedMilliseconds; + Console.WriteLine($"Proto: Elapsed time {elapsedMs}"); + } - static CpObjectiveProto NewMaximize2(int v1, int v2, long c1, long c2) - { - CpObjectiveProto obj = new CpObjectiveProto(); - obj.Vars.Add(-v1 - 1); - obj.Vars.Add(-v2 - 1); - obj.Coeffs.Add(c1); - obj.Coeffs.Add(c2); - obj.ScalingFactor = -1; - return obj; - } + [Fact] + public void LinearExprStaticCompileTest() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(-10, 10, "v1"); + IntVar v2 = model.NewIntVar(-10, 10, "v2"); + BoolVar b1 = model.NewBoolVar("b1"); + BoolVar b2 = model.NewBoolVar("b2"); + long[] c1 = new long[] { 2L, 4L }; + int[] c2 = new int[] { 2, 4 }; + LinearExpr e1 = LinearExpr.Sum(new IntVar[] { v1, v2 }); + Console.WriteLine(e1.ToString()); + LinearExpr e2 = LinearExpr.Sum(new ILiteral[] { b1, b2 }); + Console.WriteLine(e2.ToString()); + LinearExpr e3 = LinearExpr.Sum(new BoolVar[] { b1, b2 }); + Console.WriteLine(e3.ToString()); + LinearExpr e4 = LinearExpr.WeightedSum(new IntVar[] { v1, v2 }, c1); + Console.WriteLine(e4.ToString()); + LinearExpr e5 = LinearExpr.WeightedSum(new ILiteral[] { b1, b2 }, c1); + Console.WriteLine(e5.ToString()); + LinearExpr e6 = LinearExpr.WeightedSum(new BoolVar[] { b1, b2 }, c1); + Console.WriteLine(e6.ToString()); + LinearExpr e7 = LinearExpr.WeightedSum(new IntVar[] { v1, v2 }, c2); + Console.WriteLine(e7.ToString()); + LinearExpr e8 = LinearExpr.WeightedSum(new ILiteral[] { b1, b2 }, c2); + Console.WriteLine(e8.ToString()); + LinearExpr e9 = LinearExpr.WeightedSum(new BoolVar[] { b1, b2 }, c2); + Console.WriteLine(e9.ToString()); + } - // CpModelProto - [Fact] - public void SimpleLinearModelProto() - { - CpModelProto model = new CpModelProto(); - model.Variables.Add(NewIntegerVariable(-10, 10)); - model.Variables.Add(NewIntegerVariable(-10, 10)); - model.Variables.Add(NewIntegerVariable(-1000000, 1000000)); - model.Constraints.Add(NewLinear2(0, 1, 1, 1, -1000000, 100000)); - model.Constraints.Add(NewLinear3(0, 1, 2, 1, 2, -1, 0, 100000)); - model.Objective = NewMaximize1(2, 1); - // Console.WriteLine("model = " + model.ToString()); - SolveWrapper solve_wrapper = new SolveWrapper(); - CpSolverResponse response = solve_wrapper.Solve(model); - Assert.Equal(CpSolverStatus.Optimal, response.Status); - Assert.Equal(30, response.ObjectiveValue); - Assert.Equal(new long[] { 10, 10, 30 }, response.Solution); - // Console.WriteLine("response = " + response.ToString()); - } + [Fact] + public void LinearExprBuilderCompileTest() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(-10, 10, "v1"); + IntVar v2 = model.NewIntVar(-10, 10, "v2"); + BoolVar b1 = model.NewBoolVar("b1"); + BoolVar b2 = model.NewBoolVar("b2"); + long[] c1 = new long[] { 2L, 4L }; + int[] c2 = new int[] { 2, 4 }; + LinearExpr e1 = LinearExpr.NewBuilder().AddSum(new IntVar[] { v1, v2 }); + Console.WriteLine(e1.ToString()); + LinearExpr e2 = LinearExpr.NewBuilder().AddSum(new ILiteral[] { b1, b2 }); + Console.WriteLine(e2.ToString()); + LinearExpr e3 = LinearExpr.NewBuilder().AddSum(new BoolVar[] { b1, b2 }); + Console.WriteLine(e3.ToString()); + LinearExpr e4 = LinearExpr.NewBuilder().AddWeightedSum(new IntVar[] { v1, v2 }, c1); + Console.WriteLine(e4.ToString()); + LinearExpr e5 = LinearExpr.NewBuilder().AddWeightedSum(new ILiteral[] { b1, b2 }, c1); + Console.WriteLine(e5.ToString()); + LinearExpr e6 = LinearExpr.NewBuilder().AddWeightedSum(new BoolVar[] { b1, b2 }, c1); + Console.WriteLine(e6.ToString()); + LinearExpr e7 = LinearExpr.NewBuilder().AddWeightedSum(new IntVar[] { v1, v2 }, c2); + Console.WriteLine(e7.ToString()); + LinearExpr e8 = LinearExpr.NewBuilder().AddWeightedSum(new ILiteral[] { b1, b2 }, c2); + Console.WriteLine(e8.ToString()); + LinearExpr e9 = LinearExpr.NewBuilder().AddWeightedSum(new BoolVar[] { b1, b2 }, c2); + Console.WriteLine(e9.ToString()); + } - [Fact] - public void SimpleLinearModelProto2() - { - CpModelProto model = new CpModelProto(); - model.Variables.Add(NewIntegerVariable(-10, 10)); - model.Variables.Add(NewIntegerVariable(-10, 10)); - model.Constraints.Add(NewLinear2(0, 1, 1, 1, -1000000, 100000)); - model.Objective = NewMaximize2(0, 1, 1, -2); - // Console.WriteLine("model = " + model.ToString()); + [Fact] + public void ExportModel() + { + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(-10, 10, "v1"); + IntVar v2 = model.NewIntVar(-10, 10, "v2"); + model.Add(-100000 <= v1 + 2 * v2 <= 100000); + model.Minimize(v1 - 2 * v2); + Assert.True(model.ExportToFile("test_model_dotnet.pbtxt")); + Console.WriteLine("Model written to file"); + } - SolveWrapper solve_wrapper = new SolveWrapper(); - CpSolverResponse response = solve_wrapper.Solve(model); - Assert.Equal(CpSolverStatus.Optimal, response.Status); - Assert.Equal(30, response.ObjectiveValue); - Assert.Equal(new long[] { 10, -10 }, response.Solution); - // Console.WriteLine("response = " + response.ToString()); - } - - // CpModel - [Fact] - public void SimpleLinearModel() - { - CpModel model = new CpModel(); - IntVar v1 = model.NewIntVar(-10, 10, "v1"); - IntVar v2 = model.NewIntVar(-10, 10, "v2"); - IntVar v3 = model.NewIntVar(-100000, 100000, "v3"); - model.AddLinearConstraint(v1 + v2, -1000000, 100000); - model.AddLinearConstraint(v1 + 2 * v2 - v3, 0, 100000); - model.Maximize(v3); - Assert.Equal(v1.Domain.FlattenedIntervals(), new long[] { -10, 10 }); - // Console.WriteLine("model = " + model.Model.ToString()); - - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - Assert.Equal(CpSolverStatus.Optimal, status); - - CpSolverResponse response = solver.Response; - Assert.Equal(30, response.ObjectiveValue); - Assert.Equal(new long[] { 10, 10, 30 }, response.Solution); - // Console.WriteLine("response = " + reponse.ToString()); - } - - [Fact] - public void SimpleLinearModel2() - { - CpModel model = new CpModel(); - IntVar v1 = model.NewIntVar(-10, 10, "v1"); - IntVar v2 = model.NewIntVar(-10, 10, "v2"); - model.AddLinearConstraint(v1 + v2, -1000000, 100000); - model.Maximize(v1 - 2 * v2); - // Console.WriteLine("model = " + model.Model.ToString()); - - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - Assert.Equal(CpSolverStatus.Optimal, status); - - CpSolverResponse response = solver.Response; - Assert.Equal(30, response.ObjectiveValue); - Assert.Equal(new long[] { 10, -10 }, response.Solution); - // Console.WriteLine("response = " + reponse.ToString()); - } - - [Fact] - public void SimpleLinearModel3() - { - CpModel model = new CpModel(); - IntVar v1 = model.NewIntVar(-10, 10, "v1"); - IntVar v2 = model.NewIntVar(-10, 10, "v2"); - model.Add(-100000 <= v1 + 2 * v2 <= 100000); - model.Minimize(v1 - 2 * v2); - // Console.WriteLine("model = " + model.Model.ToString()); - - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - Assert.Equal(CpSolverStatus.Optimal, status); - - CpSolverResponse response = solver.Response; - Assert.Equal(-10, solver.Value(v1)); - Assert.Equal(10, solver.Value(v2)); - Assert.Equal(new long[] { -10, 10 }, response.Solution); - Assert.Equal(-30, solver.Value(v1 - 2 * v2)); - Assert.Equal(-30, response.ObjectiveValue); - // Console.WriteLine("response = " + reponse.ToString()); - } - - [Fact] - public void NegativeIntVar() - { - CpModel model = new CpModel(); - IntVar boolvar = model.NewBoolVar("boolvar"); - IntVar x = model.NewIntVar(0, 10, "x"); - IntVar delta = model.NewIntVar(-5, 5, "delta"); - IntVar squaredDelta = model.NewIntVar(0, 25, "squaredDelta"); - model.Add(x == boolvar * 4); - model.Add(delta == x - 5); - model.AddMultiplicationEquality(squaredDelta, new IntVar[] { delta, delta }); - model.Minimize(squaredDelta); - // Console.WriteLine("model = " + model.Model.ToString()); - - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - CpSolverResponse response = solver.Response; - Console.WriteLine("response = " + response.ToString()); - - Assert.Equal(CpSolverStatus.Optimal, status); - - Assert.Equal(1, solver.Value(boolvar)); - Assert.Equal(4, solver.Value(x)); - Assert.Equal(-1, solver.Value(delta)); - Assert.Equal(1, solver.Value(squaredDelta)); - Assert.Equal(new long[] { 1, 4, -1, 1 }, response.Solution); - Assert.Equal(1.0, response.ObjectiveValue, 5); - } - - [Fact] - public void NegativeSquareVar() - { - CpModel model = new CpModel(); - IntVar boolvar = model.NewBoolVar("boolvar"); - IntVar x = model.NewIntVar(0, 10, "x"); - IntVar delta = model.NewIntVar(-5, 5, "delta"); - IntVar squaredDelta = model.NewIntVar(0, 25, "squaredDelta"); - model.Add(x == 4).OnlyEnforceIf(boolvar); - model.Add(x == 0).OnlyEnforceIf(boolvar.Not()); - model.Add(delta == x - 5); - long[,] tuples = { { -5, 25 }, { -4, 16 }, { -3, 9 }, { -2, 4 }, { -1, 1 }, { 0, 0 }, - { 1, 1 }, { 2, 4 }, { 3, 9 }, { 4, 16 }, { 5, 25 } }; - model.AddAllowedAssignments(new IntVar[] { delta, squaredDelta }, tuples); - model.Minimize(squaredDelta); - - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - - CpSolverResponse response = solver.Response; - Assert.Equal(1, solver.Value(boolvar)); - Assert.Equal(4, solver.Value(x)); - Assert.Equal(-1, solver.Value(delta)); - Assert.Equal(1, solver.Value(squaredDelta)); - Assert.Equal(new long[] { 1, 4, -1, 1 }, response.Solution); - Assert.Equal(1.0, response.ObjectiveValue, 6); - } - - [Fact] - public void Division() - { - CpModel model = new CpModel(); - IntVar v1 = model.NewIntVar(0, 10, "v1"); - IntVar v2 = model.NewIntVar(1, 10, "v2"); - model.AddDivisionEquality(3, v1, v2); - // Console.WriteLine(model.Model); - - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - Assert.Equal(CpSolverStatus.Optimal, status); - - CpSolverResponse response = solver.Response; - Assert.Equal(3, solver.Value(v1)); - Assert.Equal(1, solver.Value(v2)); - Assert.Equal(new long[] { 3, 1 }, response.Solution); - Assert.Equal(0, response.ObjectiveValue); - // Console.WriteLine("response = " + reponse.ToString()); - } - - [Fact] - public void Modulo() - { - CpModel model = new CpModel(); - IntVar v1 = model.NewIntVar(1, 10, "v1"); - IntVar v2 = model.NewIntVar(1, 10, "v2"); - model.AddModuloEquality(3, v1, v2); - // Console.WriteLine(model.Model); - - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - Assert.Equal(CpSolverStatus.Optimal, status); - - CpSolverResponse response = solver.Response; - Assert.Equal(3, solver.Value(v1)); - Assert.Equal(4, solver.Value(v2)); - Assert.Equal(new long[] { 3, 4 }, response.Solution); - Assert.Equal(0, response.ObjectiveValue); - // Console.WriteLine("response = " + reponse.ToString()); - } - - [Fact] - public void LargeWeightedSumLong() - { - CpModel model = new CpModel(); - List vars = new List(); - List coeffs = new List(); - - for (int i = 0; i < 100000; ++i) - { - vars.Add(model.NewBoolVar("")); - coeffs.Add(i + 1); - } - - var watch = System.Diagnostics.Stopwatch.StartNew(); - model.Minimize(LinearExpr.WeightedSum(vars, coeffs)); - watch.Stop(); - var elapsedMs = watch.ElapsedMilliseconds; - Console.WriteLine($"Long: Elapsed time {elapsedMs}"); - } - - [Fact] - public void LargeWeightedSumInt() - { - CpModel model = new CpModel(); - List vars = new List(); - List coeffs = new List(); - - for (int i = 0; i < 100000; ++i) - { - vars.Add(model.NewBoolVar("")); - coeffs.Add(i); - } - - var watch = System.Diagnostics.Stopwatch.StartNew(); - model.Minimize(LinearExpr.WeightedSum(vars, coeffs)); - watch.Stop(); - var elapsedMs = watch.ElapsedMilliseconds; - Console.WriteLine($"Int: Elapsed time {elapsedMs}"); - } - - [Fact] - public void LargeWeightedSumExpr() - { - CpModel model = new CpModel(); - List exprs = new List(); - - for (int i = 0; i < 100000; ++i) - { - exprs.Add(model.NewBoolVar("") * i); - } - - var watch = System.Diagnostics.Stopwatch.StartNew(); - model.Minimize(LinearExpr.Sum(exprs)); - watch.Stop(); - var elapsedMs = watch.ElapsedMilliseconds; - Console.WriteLine($"Exprs: Elapsed time {elapsedMs}"); - } - - [Fact] - public void LargeWeightedSumProto() - { - CpModel model = new CpModel(); - List vars = new List(); - List coeffs = new List(); - - for (int i = 0; i < 100000; ++i) - { - vars.Add(model.NewBoolVar("")); - coeffs.Add(i + 1); - } - - var watch = System.Diagnostics.Stopwatch.StartNew(); - model.Minimize(); - for (int i = 0; i < 100000; ++i) - { - model.AddTermToObjective(vars[i], coeffs[i]); - } - watch.Stop(); - var elapsedMs = watch.ElapsedMilliseconds; - Console.WriteLine($"Proto: Elapsed time {elapsedMs}"); - } - - [Fact] - public void ExportModel() - { - CpModel model = new CpModel(); - IntVar v1 = model.NewIntVar(-10, 10, "v1"); - IntVar v2 = model.NewIntVar(-10, 10, "v2"); - model.Add(-100000 <= v1 + 2 * v2 <= 100000); - model.Minimize(v1 - 2 * v2); - Assert.True(model.ExportToFile("test_model_dotnet.pbtxt")); - Console.WriteLine("Model written to file"); - } - - [Fact] - public void SolveFromString() - { - string model_str = @" + [Fact] + public void SolveFromString() + { + string model_str = @" { ""variables"": [ { ""name"": ""C"", ""domain"": [ ""1"", ""9"" ] }, @@ -416,45 +477,45 @@ namespace Google.OrTools.Tests { ""linear"": { ""vars"": [ 6, 5, 9, 4, 3, 7, 8, 2, 0, 1 ], ""coeffs"": [ ""1"", ""0"", ""-1"", ""100"", ""1"", ""-1000"", ""-100"", ""10"", ""10"", ""1"" ], ""domain"": [ ""0"", ""0"" ] } } ] }"; - CpModelProto model = Google.Protobuf.JsonParser.Default.Parse(model_str); - SolveWrapper solve_wrapper = new SolveWrapper(); - CpSolverResponse response = solve_wrapper.Solve(model); - Console.WriteLine(response); - } - - [Fact] - public void CaptureLog() - { - Console.WriteLine("CaptureLog test"); - CpModel model = new CpModel(); - IntVar v1 = model.NewIntVar(-10, 10, "v1"); - IntVar v2 = model.NewIntVar(-10, 10, "v2"); - IntVar v3 = model.NewIntVar(-100000, 100000, "v3"); - model.AddLinearConstraint(v1 + v2, -1000000, 100000); - model.AddLinearConstraint(v1 + 2 * v2 - v3, 0, 100000); - model.Maximize(v3); - Assert.Equal(v1.Domain.FlattenedIntervals(), new long[] { -10, 10 }); - // Console.WriteLine("model = " + model.Model.ToString()); - - CpSolver solver = new CpSolver(); - solver.StringParameters = "log_search_progress:true log_to_stdout:false"; - string log = ""; - solver.SetLogCallback(message => log += message + "\n"); - solver.Solve(model); - Assert.NotEmpty(log); - Assert.Contains("OPTIMAL", log); - } - - [Fact] - public void TestInterval() - { - Console.WriteLine("TestInterval test"); - CpModel model = new CpModel(); - IntVar v = model.NewIntVar(-10, 10, "v"); - IntervalVar i = model.NewFixedSizeIntervalVar(v, 3, "i"); - Assert.Equal("v", i.StartExpr().ShortString()); - Assert.Equal("3", i.SizeExpr().ShortString()); - Assert.Equal("(v + 3)", i.EndExpr().ShortString()); - } + CpModelProto model = Google.Protobuf.JsonParser.Default.Parse(model_str); + SolveWrapper solve_wrapper = new SolveWrapper(); + CpSolverResponse response = solve_wrapper.Solve(model); + Console.WriteLine(response); } + + [Fact] + public void CaptureLog() + { + Console.WriteLine("CaptureLog test"); + CpModel model = new CpModel(); + IntVar v1 = model.NewIntVar(-10, 10, "v1"); + IntVar v2 = model.NewIntVar(-10, 10, "v2"); + IntVar v3 = model.NewIntVar(-100000, 100000, "v3"); + model.AddLinearConstraint(v1 + v2, -1000000, 100000); + model.AddLinearConstraint(v1 + 2 * v2 - v3, 0, 100000); + model.Maximize(v3); + Assert.Equal(v1.Domain.FlattenedIntervals(), new long[] { -10, 10 }); + // Console.WriteLine("model = " + model.Model.ToString()); + + CpSolver solver = new CpSolver(); + solver.StringParameters = "log_search_progress:true log_to_stdout:false"; + string log = ""; + solver.SetLogCallback(message => log += message + "\n"); + solver.Solve(model); + Assert.NotEmpty(log); + Assert.Contains("OPTIMAL", log); + } + + [Fact] + public void TestInterval() + { + Console.WriteLine("TestInterval test"); + CpModel model = new CpModel(); + IntVar v = model.NewIntVar(-10, 10, "v"); + IntervalVar i = model.NewFixedSizeIntervalVar(v, 3, "i"); + Assert.Equal("v", i.StartExpr().ToString()); + Assert.Equal("3", i.SizeExpr().ToString()); + Assert.Equal("v + 3", i.EndExpr().ToString()); + } +} } // namespace Google.OrTools.Tests diff --git a/examples/tests/issue18.cs b/examples/tests/issue18.cs index 5847846ac6..815235bb13 100644 --- a/examples/tests/issue18.cs +++ b/examples/tests/issue18.cs @@ -18,39 +18,39 @@ using Google.OrTools.ConstraintSolver; namespace Google.OrTools.Tests { - public class Issue18Test +public class Issue18Test +{ + [Fact] + public void NewSearchTest() { - [Fact] - public void NewSearchTest() + Solver solver = new Google.OrTools.ConstraintSolver.Solver("p"); + + // creating dummy variables + List vars = new List(); + for (int i = 0; i < 100000; i++) { - Solver solver = new Google.OrTools.ConstraintSolver.Solver("p"); - - // creating dummy variables - List vars = new List(); - for (int i = 0; i < 100000; i++) - { - vars.Add(solver.MakeIntVar(0, 1)); - } - - IntExpr globalSum = solver.MakeSum(vars.ToArray()); - - DecisionBuilder db = solver.MakePhase(vars.ToArray(), Google.OrTools.ConstraintSolver.Solver.INT_VAR_SIMPLE, - Google.OrTools.ConstraintSolver.Solver.INT_VALUE_SIMPLE); - - solver.NewSearch(db, new OptimizeVar(solver, true, globalSum.Var(), 100)); - - // force Garbage Collector - GC.Collect(); - GC.WaitForPendingFinalizers(); - - // Try to read all solutions - int count = 0; - while (solver.NextSolution()) - { - count++; - // Console.WriteLine("solution " + globalSum.Var().Value()); - } - Console.WriteLine("Solutions: " + count); + vars.Add(solver.MakeIntVar(0, 1)); } + + IntExpr globalSum = solver.MakeSum(vars.ToArray()); + + DecisionBuilder db = solver.MakePhase(vars.ToArray(), Google.OrTools.ConstraintSolver.Solver.INT_VAR_SIMPLE, + Google.OrTools.ConstraintSolver.Solver.INT_VALUE_SIMPLE); + + solver.NewSearch(db, new OptimizeVar(solver, true, globalSum.Var(), 100)); + + // force Garbage Collector + GC.Collect(); + GC.WaitForPendingFinalizers(); + + // Try to read all solutions + int count = 0; + while (solver.NextSolution()) + { + count++; + // Console.WriteLine("solution " + globalSum.Var().Value()); + } + Console.WriteLine("Solutions: " + count); } +} } // namespace Google.OrTools.Tests diff --git a/examples/tests/issue22.cs b/examples/tests/issue22.cs index 9d29337e8d..dbccff945a 100644 --- a/examples/tests/issue22.cs +++ b/examples/tests/issue22.cs @@ -21,64 +21,64 @@ using Google.OrTools.ConstraintSolver; namespace Google.OrTools.Tests { - public class Issue22Test +public class Issue22Test +{ + private long Solve(long num_buses_check = 0) { - private long Solve(long num_buses_check = 0) + ConstraintSolverParameters sPrm = Solver.DefaultSolverParameters(); + sPrm.CompressTrail = 0; + Solver solver = new Solver("OrTools", sPrm); + + // this works + // IntVar[,] x = solver.MakeIntVarMatrix(2,2, new int[] {-2,0,1,2}, "x"); + + // this doesn't work + IntVar[,] x = solver.MakeIntVarMatrix(2, 2, new int[] { 0, 1, 2 }, "x"); + + for (int w = 0; w < 2; w++) { - ConstraintSolverParameters sPrm = Solver.DefaultSolverParameters(); - sPrm.CompressTrail = 0; - Solver solver = new Solver("OrTools", sPrm); - - // this works - // IntVar[,] x = solver.MakeIntVarMatrix(2,2, new int[] {-2,0,1,2}, "x"); - - // this doesn't work - IntVar[,] x = solver.MakeIntVarMatrix(2, 2, new int[] { 0, 1, 2 }, "x"); - - for (int w = 0; w < 2; w++) + IntVar[] b = new IntVar[2]; + for (int i = 0; i < 2; i++) { - IntVar[] b = new IntVar[2]; + b[i] = solver.MakeIsEqualCstVar(x[w, i], 0); + } + solver.Add(solver.MakeSumGreaterOrEqual(b, 2)); + } + + IntVar[] x_flat = x.Flatten(); + DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + solver.NewSearch(db); + while (solver.NextSolution()) + { + Console.WriteLine("x: "); + for (int j = 0; j < 2; j++) + { + Console.Write("worker" + (j + 1).ToString() + ":"); for (int i = 0; i < 2; i++) { - b[i] = solver.MakeIsEqualCstVar(x[w, i], 0); + Console.Write(" {0,2} ", x[j, i].Value()); } - solver.Add(solver.MakeSumGreaterOrEqual(b, 2)); + Console.Write("\n"); } - - IntVar[] x_flat = x.Flatten(); - DecisionBuilder db = solver.MakePhase(x_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - solver.NewSearch(db); - while (solver.NextSolution()) - { - Console.WriteLine("x: "); - for (int j = 0; j < 2; j++) - { - Console.Write("worker" + (j + 1).ToString() + ":"); - for (int i = 0; i < 2; i++) - { - Console.Write(" {0,2} ", x[j, i].Value()); - } - Console.Write("\n"); - } - Console.WriteLine("End at---->" + DateTime.Now); - } - - Console.WriteLine("\nSolutions: {0}", solver.Solutions()); - Console.WriteLine("WallTime: {0}ms", solver.WallTime()); - Console.WriteLine("Failures: {0}", solver.Failures()); - Console.WriteLine("Branches: {0} ", solver.Branches()); - - solver.EndSearch(); - return 1; + Console.WriteLine("End at---->" + DateTime.Now); } - [Fact] - public void InitialPropagateTest() - { - Console.WriteLine("Check for minimum number of buses: "); - long num_buses = Solve(); - Console.WriteLine("\n... got {0} as minimal value.", num_buses); - Console.WriteLine("\nAll solutions: ", num_buses); - } + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + return 1; } + + [Fact] + public void InitialPropagateTest() + { + Console.WriteLine("Check for minimum number of buses: "); + long num_buses = Solve(); + Console.WriteLine("\n... got {0} as minimal value.", num_buses); + Console.WriteLine("\nAll solutions: ", num_buses); + } +} } // namespace Google.OrTools.Tests diff --git a/examples/tests/issue33.cs b/examples/tests/issue33.cs index 2e52f8453a..dad4f67771 100644 --- a/examples/tests/issue33.cs +++ b/examples/tests/issue33.cs @@ -24,656 +24,654 @@ using Xunit; namespace Google.OrTools.Test { - public class Task +public class Task +{ + public int Id { get; private set; } + public int TaskType { get; private set; } + public int LocationId { get; private set; } + public Dictionary Durations { get; private set; } + public int TaskPosition { get; private set; } + + public Task(int id, int taskType, int locationIndex, int taskPosition, Dictionary durations) { - public int Id { get; private set; } - public int TaskType { get; private set; } - public int LocationId { get; private set; } - public Dictionary Durations { get; private set; } - public int TaskPosition { get; private set; } + Id = id; + TaskType = taskType; + LocationId = locationIndex; + Durations = durations; + TaskPosition = taskPosition; + } - public Task(int id, int taskType, int locationIndex, int taskPosition, Dictionary durations) - { - Id = id; - TaskType = taskType; - LocationId = locationIndex; - Durations = durations; - TaskPosition = taskPosition; + public Task(int id, int taskType, int locationIndex, int taskPosition) + { + Id = id; + TaskType = taskType; + LocationId = locationIndex; + TaskPosition = taskPosition; + Durations = new Dictionary(); + } +} + +public class WorkLocation +{ + public int Id { get; private set; } + public int NbTasks + { + get { + Debug.Assert(Tasks != null); + return Tasks.Length; } + set { + Debug.Assert(Tasks == null); + Tasks = new Task[value]; + } + } + public Task[] Tasks { get; private set; } - public Task(int id, int taskType, int locationIndex, int taskPosition) - { - Id = id; - TaskType = taskType; - LocationId = locationIndex; - TaskPosition = taskPosition; - Durations = new Dictionary(); + public WorkLocation(int index) + { + Id = index; + } +} + +public class Tool +{ + public int Id { get; private set; } + public HashSet TaskTypes { get; set; } + public int[,] TravellingTime { get; set; } + public int InitialLocationId { get; set; } + + public Tool(int index, int initialLocation = 0) + { + Id = index; + InitialLocationId = initialLocation; + TaskTypes = new HashSet(); + } + + public void AddTaskType(int t) + { + TaskTypes.Add(t); + } + + public bool CanPerformTaskType(int taskType) + { + return TaskTypes.Contains(taskType); + } +} + +public class FactoryDescription +{ + public Tool[] Tools { get; private set; } + public WorkLocation[] Locations { get; private set; } + + public int NbWorkLocations + { + get { + return Locations.Length; + } + } + public int NbTools + { + get { + return Tools.Length; } } - public class WorkLocation - { - public int Id { get; private set; } - public int NbTasks - { - get { - Debug.Assert(Tasks != null); - return Tasks.Length; - } - set { - Debug.Assert(Tasks == null); - Tasks = new Task[value]; - } - } - public Task[] Tasks { get; private set; } + public int NbTaskPerCycle { get; private set; } + // TaskType go typically from 0 to 6. InspectionType indicates which + // is the TaskType that correspond to Inspection. + public int Inspection { get; private set; } + // All the time within the schedule horizon in which the blast can start. + public long[] InspectionStarts { get; private set; } - public WorkLocation(int index) - { - Id = index; - } + public int Horizon { get; private set; } + + // horizon equal to 2 weeks (in minutes). + public FactoryDescription(int nbTools, int nbLocations, int nbTaskPerCycle, int horizon = 14 * 24 * 60) + { + Debug.Assert(nbTools > 0); + Debug.Assert(nbLocations > 0); + Debug.Assert(nbTaskPerCycle > 0); + Debug.Assert(horizon > 0); + NbTaskPerCycle = nbTaskPerCycle; + Inspection = NbTaskPerCycle - 1; + Tools = new Tool[nbTools]; + Horizon = horizon; + for (int i = 0; i < nbTools; i++) + Tools[i] = new Tool(i); + Locations = new WorkLocation[nbLocations]; + for (int i = 0; i < nbLocations; i++) + Locations[i] = new WorkLocation(i); + + InspectionStarts = new long[] { -1, 600, 1200, 1800, 2400, 2800 }; } - public class Tool + public Tool[] getToolPerTaskType(int taskType) { - public int Id { get; private set; } - public HashSet TaskTypes { get; set; } - public int[,] TravellingTime { get; set; } - public int InitialLocationId { get; set; } - - public Tool(int index, int initialLocation = 0) - { - Id = index; - InitialLocationId = initialLocation; - TaskTypes = new HashSet(); - } - - public void AddTaskType(int t) - { - TaskTypes.Add(t); - } - - public bool CanPerformTaskType(int taskType) - { - return TaskTypes.Contains(taskType); - } + var elements = from tool in Tools + where tool.CanPerformTaskType(taskType) select tool; + return elements.ToArray(); } - public class FactoryDescription + public Task[] getFlatTaskList() { - public Tool[] Tools { get; private set; } - public WorkLocation[] Locations { get; private set; } + return (from location in Locations from task in location.Tasks orderby task.Id select task).ToArray(); + } - public int NbWorkLocations + public int[] getTaskTypes() + { + return (from location in Locations from task in location.Tasks select task.TaskType).Distinct().ToArray(); + } + + // TODO: This should be enhanced + public void SanityCheck() + { + foreach (Tool tool in Tools) { - get { - return Locations.Length; - } + Debug.Assert(tool.TravellingTime.GetLength(0) == NbWorkLocations); + Debug.Assert(tool.TravellingTime.GetLength(1) == NbWorkLocations); + for (int i = 0; i < NbWorkLocations; i++) + Debug.Assert(tool.TravellingTime[i, i] == 0); } - public int NbTools + } +} + +interface DataReader +{ + FactoryDescription FetchData(); +} + +public class SmallSyntheticData : DataReader +{ + public SmallSyntheticData() + { + } + + public FactoryDescription FetchData() + { + // deterministic seed for result reproducibility + Random randomDuration = new Random(2); + + // FactoryDescription(nbTools, nblocations, nbTasks per cycle) + FactoryDescription factoryDescription = new FactoryDescription(5, 4, 3); + + // Travelling time and distance are temporarily identical and they + // are no different for different tools + int[,] travellingTime = new int[factoryDescription.NbWorkLocations, factoryDescription.NbWorkLocations]; + for (int i = 0; i < travellingTime.GetLength(0); i++) { - get { - return Tools.Length; - } - } - - public int NbTaskPerCycle { get; private set; } - // TaskType go typically from 0 to 6. InspectionType indicates which - // is the TaskType that correspond to Inspection. - public int Inspection { get; private set; } - // All the time within the schedule horizon in which the blast can start. - public long[] InspectionStarts { get; private set; } - - public int Horizon { get; private set; } - - // horizon equal to 2 weeks (in minutes). - public FactoryDescription(int nbTools, int nbLocations, int nbTaskPerCycle, int horizon = 14 * 24 * 60) - { - Debug.Assert(nbTools > 0); - Debug.Assert(nbLocations > 0); - Debug.Assert(nbTaskPerCycle > 0); - Debug.Assert(horizon > 0); - NbTaskPerCycle = nbTaskPerCycle; - Inspection = NbTaskPerCycle - 1; - Tools = new Tool[nbTools]; - Horizon = horizon; - for (int i = 0; i < nbTools; i++) - Tools[i] = new Tool(i); - Locations = new WorkLocation[nbLocations]; - for (int i = 0; i < nbLocations; i++) - Locations[i] = new WorkLocation(i); - - InspectionStarts = new long[] { -1, 600, 1200, 1800, 2400, 2800 }; - } - - public Tool[] getToolPerTaskType(int taskType) - { - var elements = from tool in Tools - where tool.CanPerformTaskType(taskType) select tool; - return elements.ToArray(); - } - - public Task[] getFlatTaskList() - { - return (from location in Locations from task in location.Tasks orderby task.Id select task).ToArray(); - } - - public int[] getTaskTypes() - { - return (from location in Locations from task in location.Tasks select task.TaskType).Distinct().ToArray(); - } - - // TODO: This should be enhanced - public void SanityCheck() - { - foreach (Tool tool in Tools) + for (int j = 0; j < travellingTime.GetLength(1); j++) { - Debug.Assert(tool.TravellingTime.GetLength(0) == NbWorkLocations); - Debug.Assert(tool.TravellingTime.GetLength(1) == NbWorkLocations); - for (int i = 0; i < NbWorkLocations; i++) - Debug.Assert(tool.TravellingTime[i, i] == 0); + if (i == j) + travellingTime[i, j] = 0; + else + travellingTime[i, j] = (5 * Math.Abs(i - j)) * 10; } } - } - interface DataReader - { - FactoryDescription FetchData(); - } + factoryDescription.Tools[0].AddTaskType(0); + factoryDescription.Tools[1].AddTaskType(0); + factoryDescription.Tools[2].AddTaskType(1); + factoryDescription.Tools[3].AddTaskType(1); + factoryDescription.Tools[4].AddTaskType(2); + factoryDescription.Tools[1].AddTaskType(1); - public class SmallSyntheticData : DataReader - { - public SmallSyntheticData() + foreach (Tool tool in factoryDescription.Tools) + tool.TravellingTime = travellingTime; + + int c = 0; + int nbCyclePerWorkLocation = 2; + int[] boll = new int[100]; + for (int i = 0; i < factoryDescription.NbWorkLocations; i++) { - } - - public FactoryDescription FetchData() - { - // deterministic seed for result reproducibility - Random randomDuration = new Random(2); - - // FactoryDescription(nbTools, nblocations, nbTasks per cycle) - FactoryDescription factoryDescription = new FactoryDescription(5, 4, 3); - - // Travelling time and distance are temporarily identical and they - // are no different for different tools - int[,] travellingTime = new int[factoryDescription.NbWorkLocations, factoryDescription.NbWorkLocations]; - for (int i = 0; i < travellingTime.GetLength(0); i++) + factoryDescription.Locations[i].NbTasks = nbCyclePerWorkLocation * factoryDescription.NbTaskPerCycle; + for (int j = 0; j < nbCyclePerWorkLocation; j++) { - for (int j = 0; j < travellingTime.GetLength(1); j++) + for (int k = 0; k < factoryDescription.NbTaskPerCycle; k++) { - if (i == j) - travellingTime[i, j] = 0; - else - travellingTime[i, j] = (5 * Math.Abs(i - j)) * 10; - } - } + Task t = new Task(c, k, i, k + j * factoryDescription.NbTaskPerCycle); - factoryDescription.Tools[0].AddTaskType(0); - factoryDescription.Tools[1].AddTaskType(0); - factoryDescription.Tools[2].AddTaskType(1); - factoryDescription.Tools[3].AddTaskType(1); - factoryDescription.Tools[4].AddTaskType(2); - factoryDescription.Tools[1].AddTaskType(1); - - foreach (Tool tool in factoryDescription.Tools) - tool.TravellingTime = travellingTime; - - int c = 0; - int nbCyclePerWorkLocation = 2; - int[] boll = new int[100]; - for (int i = 0; i < factoryDescription.NbWorkLocations; i++) - { - factoryDescription.Locations[i].NbTasks = nbCyclePerWorkLocation * factoryDescription.NbTaskPerCycle; - for (int j = 0; j < nbCyclePerWorkLocation; j++) - { - for (int k = 0; k < factoryDescription.NbTaskPerCycle; k++) + // Filling in tool-dependent durations + Tool[] compatibleTools = factoryDescription.getToolPerTaskType(k); + foreach (Tool tool in compatibleTools) { - Task t = new Task(c, k, i, k + j * factoryDescription.NbTaskPerCycle); - - // Filling in tool-dependent durations - Tool[] compatibleTools = factoryDescription.getToolPerTaskType(k); - foreach (Tool tool in compatibleTools) - { - boll[c] = randomDuration.Next(13, 17) * 10; - ; - t.Durations[tool.Id] = boll[c]; - } - factoryDescription.Locations[i].Tasks[t.TaskPosition] = t; - c++; + boll[c] = randomDuration.Next(13, 17) * 10; + ; + t.Durations[tool.Id] = boll[c]; } + factoryDescription.Locations[i].Tasks[t.TaskPosition] = t; + c++; } } + } - factoryDescription.SanityCheck(); - return factoryDescription; + factoryDescription.SanityCheck(); + return factoryDescription; + } +} + +public class RandomSelectToolHeuristic : NetDecisionBuilder +{ + private FactoryScheduling factoryScheduling; + private Random rnd; + + public RandomSelectToolHeuristic(FactoryScheduling factoryScheduling, int seed) + { + this.factoryScheduling = factoryScheduling; + // deterministic seed for result reproducibility + this.rnd = new Random(seed); + } + + public override Decision Next(Solver solver) + { + foreach (IntVar var in factoryScheduling.SelectedTool) + { + if (!var.Bound()) + { + int min = (int)var.Min(); + int max = (int)var.Max(); + int rndVal = rnd.Next(min, max + 1); + while (!var.Contains(rndVal)) + rndVal = rnd.Next(min, max + 1); + return solver.MakeAssignVariableValue(var, rndVal); + } + } + return null; + } +} + +class TaskAlternative +{ + public Task Task { get; private set; } + public IntVar ToolVar { get; set; } + public List Intervals { get; private set; } + + public TaskAlternative(Task t) + { + Task = t; + Intervals = new List(); + } +} + +public class FactoryScheduling +{ + private FactoryDescription factoryData; + private Solver solver; + + private Task[] tasks; + private int[] taskTypes; + + /* Flat list of all the tasks */ + private TaskAlternative[] taskStructures; + + /* Task per WorkLocation: location2Task[d][i]: the i-th task of the + * d-th location */ + private TaskAlternative[][] location2Task; + + /* Task per Tool: tool2Task[t][i]: the i-th task of the t-th tool. + Note that it does NOT imply that the it will be the i-th + executed. In other words, it should be considered as an unordered + set. Furthermore, tool2Task[t][i] can also be *unperformed* */ + private List[] tool2Task; + + /* All the transition times for the tools. + tool2TransitionTimes[t][i]: the transition time of the t-th tool + from the i-th task to the next */ + private List[] tool2TransitionTimes; + + /* Map between the interval var of a tool to its related task id. + toolIntervalVar2TaskId[t][k] = i: in the t-th tool, the k-th + interval var correspond to tasks[i] */ + private List[] toolIntervalVar2TaskId; + + /* Tools per task type: taskType2Tool[tt][t]: the t-th tool capable + * of doing the tt-th task type */ + private List[] taskType2Tool; + + /* For each task which tools is performed upon */ + private List selectedTool; + public List SelectedTool + { + get { + return selectedTool; } } - public class RandomSelectToolHeuristic : NetDecisionBuilder + /* Sequence of task for each tool */ + private SequenceVar[] allToolSequences; + public SequenceVar[] AllToolSequences { - private FactoryScheduling factoryScheduling; - private Random rnd; - - public RandomSelectToolHeuristic(FactoryScheduling factoryScheduling, int seed) - { - this.factoryScheduling = factoryScheduling; - // deterministic seed for result reproducibility - this.rnd = new Random(seed); - } - - public override Decision Next(Solver solver) - { - foreach (IntVar var in factoryScheduling.SelectedTool) - { - if (!var.Bound()) - { - int min = (int)var.Min(); - int max = (int)var.Max(); - int rndVal = rnd.Next(min, max + 1); - while (!var.Contains(rndVal)) - rndVal = rnd.Next(min, max + 1); - return solver.MakeAssignVariableValue(var, rndVal); - } - } - return null; + get { + return allToolSequences; } } - class TaskAlternative - { - public Task Task { get; private set; } - public IntVar ToolVar { get; set; } - public List Intervals { get; private set; } + /* Makespan var */ + private IntVar makespan; - public TaskAlternative(Task t) - { - Task = t; - Intervals = new List(); - } + /* Objective */ + private OptimizeVar objective; + + /* maximum horizon */ + private int horizon; + + /* Start & End times of IntervalVars*/ + IntVar[][] startingTimes; + IntVar[][] endTimes; + + public FactoryScheduling(FactoryDescription data) + { + factoryData = data; } - public class FactoryScheduling + private void Init() { - private FactoryDescription factoryData; - private Solver solver; + horizon = factoryData.Horizon; + solver = new Solver("Factory Scheduling"); + tasks = factoryData.getFlatTaskList(); + taskTypes = factoryData.getTaskTypes(); + taskStructures = new TaskAlternative[tasks.Length]; + location2Task = new TaskAlternative[factoryData.NbWorkLocations][]; + tool2Task = new List[factoryData.NbTools]; + toolIntervalVar2TaskId = new List[factoryData.NbTools]; + tool2TransitionTimes = new List[factoryData.NbTools]; - private Task[] tasks; - private int[] taskTypes; + taskType2Tool = new List[taskTypes.Length]; + selectedTool = new List(); + for (int tt = 0; tt < taskTypes.Length; tt++) + taskType2Tool[tt] = new List(); - /* Flat list of all the tasks */ - private TaskAlternative[] taskStructures; - - /* Task per WorkLocation: location2Task[d][i]: the i-th task of the - * d-th location */ - private TaskAlternative[][] location2Task; - - /* Task per Tool: tool2Task[t][i]: the i-th task of the t-th tool. - Note that it does NOT imply that the it will be the i-th - executed. In other words, it should be considered as an unordered - set. Furthermore, tool2Task[t][i] can also be *unperformed* */ - private List[] tool2Task; - - /* All the transition times for the tools. - tool2TransitionTimes[t][i]: the transition time of the t-th tool - from the i-th task to the next */ - private List[] tool2TransitionTimes; - - /* Map between the interval var of a tool to its related task id. - toolIntervalVar2TaskId[t][k] = i: in the t-th tool, the k-th - interval var correspond to tasks[i] */ - private List[] toolIntervalVar2TaskId; - - /* Tools per task type: taskType2Tool[tt][t]: the t-th tool capable - * of doing the tt-th task type */ - private List[] taskType2Tool; - - /* For each task which tools is performed upon */ - private List selectedTool; - public List SelectedTool + foreach (Tool tool in factoryData.Tools) + foreach (int taskType in tool.TaskTypes) + taskType2Tool[taskType].Add(tool); + for (int d = 0; d < factoryData.NbWorkLocations; d++) + location2Task[d] = new TaskAlternative[factoryData.Locations[d].NbTasks]; + for (int t = 0; t < factoryData.NbTools; t++) { - get { - return selectedTool; - } + tool2Task[t] = new List(); + toolIntervalVar2TaskId[t] = new List(); + tool2TransitionTimes[t] = new List(); } - /* Sequence of task for each tool */ - private SequenceVar[] allToolSequences; - public SequenceVar[] AllToolSequences + allToolSequences = new SequenceVar[factoryData.NbTools - 1]; + + startingTimes = new IntVar[factoryData.NbTools - 1][]; + endTimes = new IntVar[factoryData.NbTools - 1][]; + } + + private void PostTransitionTimeConstraints(int t, bool postTransitionsConstraint = true) + { + Tool tool = factoryData.Tools[t]; + // if it is a inspection, we make sure there are no transitiontimes + if (tool.CanPerformTaskType(factoryData.Inspection)) + tool2TransitionTimes[t].Add(null); + else { - get { - return allToolSequences; - } - } + int[,] tt = tool.TravellingTime; - /* Makespan var */ - private IntVar makespan; + SequenceVar seq = allToolSequences[t]; + long s = seq.Size(); + IntVar[] nextLocation = new IntVar[s + 1]; - /* Objective */ - private OptimizeVar objective; + // The seq.Next(i) represents the task performed after the i-th + // task in the sequence seq.Next(0) represents the first task + // performed for extracting travelling times we need to get the + // related location In case a task is not performed (seq.Next(i) + // == i), i.e. it's pointing to itself The last performed task + // (or pre-start task, if no tasks are performed) will have + // seq.Next(i) == s + 1 therefore we add a virtual location + // whose travelling time is equal to 0 + // + // NOTE: The index of a SequenceVar are 0..n, but the domain + // range is 1..(n+1), this is due to that the start node = 0 is + // a dummy node, and the node where seq.Next(i) == n+1 is the + // end node - /* maximum horizon */ - private int horizon; + // Extra elements for the unreachable start node (0), and the + // end node whose next task takes place in a virtual location + int[] taskIndex2locationId = new int[s + 2]; + taskIndex2locationId[0] = -10; + for (int i = 0; i < s; i++) + taskIndex2locationId[i + 1] = tasks[toolIntervalVar2TaskId[t][i]].LocationId; - /* Start & End times of IntervalVars*/ - IntVar[][] startingTimes; - IntVar[][] endTimes; + // this is the virtual location for unperformed tasks + taskIndex2locationId[s + 1] = factoryData.NbWorkLocations; - public FactoryScheduling(FactoryDescription data) - { - factoryData = data; - } - - private void Init() - { - horizon = factoryData.Horizon; - solver = new Solver("Factory Scheduling"); - tasks = factoryData.getFlatTaskList(); - taskTypes = factoryData.getTaskTypes(); - taskStructures = new TaskAlternative[tasks.Length]; - location2Task = new TaskAlternative[factoryData.NbWorkLocations][]; - tool2Task = new List[factoryData.NbTools]; - toolIntervalVar2TaskId = new List[factoryData.NbTools]; - tool2TransitionTimes = new List[factoryData.NbTools]; - - taskType2Tool = new List[taskTypes.Length]; - selectedTool = new List(); - for (int tt = 0; tt < taskTypes.Length; tt++) - taskType2Tool[tt] = new List(); - - foreach (Tool tool in factoryData.Tools) - foreach (int taskType in tool.TaskTypes) - taskType2Tool[taskType].Add(tool); - for (int d = 0; d < factoryData.NbWorkLocations; d++) - location2Task[d] = new TaskAlternative[factoryData.Locations[d].NbTasks]; - for (int t = 0; t < factoryData.NbTools; t++) + // Build the travelling time matrix with the additional virtual location + int[][] ttWithVirtualLocation = new int [factoryData.NbWorkLocations + 1][]; + for (int d1 = 0; d1 < ttWithVirtualLocation.Length; d1++) { - tool2Task[t] = new List(); - toolIntervalVar2TaskId[t] = new List(); - tool2TransitionTimes[t] = new List(); - } - - allToolSequences = new SequenceVar[factoryData.NbTools - 1]; - - startingTimes = new IntVar[factoryData.NbTools - 1][]; - endTimes = new IntVar[factoryData.NbTools - 1][]; - } - - private void PostTransitionTimeConstraints(int t, bool postTransitionsConstraint = true) - { - Tool tool = factoryData.Tools[t]; - // if it is a inspection, we make sure there are no transitiontimes - if (tool.CanPerformTaskType(factoryData.Inspection)) - tool2TransitionTimes[t].Add(null); - else - { - int[,] tt = tool.TravellingTime; - - SequenceVar seq = allToolSequences[t]; - long s = seq.Size(); - IntVar[] nextLocation = new IntVar[s + 1]; - - // The seq.Next(i) represents the task performed after the i-th - // task in the sequence seq.Next(0) represents the first task - // performed for extracting travelling times we need to get the - // related location In case a task is not performed (seq.Next(i) - // == i), i.e. it's pointing to itself The last performed task - // (or pre-start task, if no tasks are performed) will have - // seq.Next(i) == s + 1 therefore we add a virtual location - // whose travelling time is equal to 0 - // - // NOTE: The index of a SequenceVar are 0..n, but the domain - // range is 1..(n+1), this is due to that the start node = 0 is - // a dummy node, and the node where seq.Next(i) == n+1 is the - // end node - - // Extra elements for the unreachable start node (0), and the - // end node whose next task takes place in a virtual location - int[] taskIndex2locationId = new int[s + 2]; - taskIndex2locationId[0] = -10; - for (int i = 0; i < s; i++) - taskIndex2locationId[i + 1] = tasks[toolIntervalVar2TaskId[t][i]].LocationId; - - // this is the virtual location for unperformed tasks - taskIndex2locationId[s + 1] = factoryData.NbWorkLocations; - - // Build the travelling time matrix with the additional virtual location - int[][] ttWithVirtualLocation = new int [factoryData.NbWorkLocations + 1][]; - for (int d1 = 0; d1 < ttWithVirtualLocation.Length; d1++) - { - ttWithVirtualLocation[d1] = new int[factoryData.NbWorkLocations + 1]; - for (int d2 = 0; d2 < ttWithVirtualLocation.Length; d2++) - if (d1 == factoryData.NbWorkLocations) - { - ttWithVirtualLocation[d1][d2] = 0; - } - else - { - ttWithVirtualLocation[d1][d2] = (d2 == factoryData.NbWorkLocations) ? 0 : tt[d1, d2]; - } - } - - for (int i = 0; i < nextLocation.Length; i++) - { - // this is the next-location associated with the i-th task - nextLocation[i] = solver.MakeElement(taskIndex2locationId, seq.Next(i)).Var(); - - int d = (i == 0) ? tool.InitialLocationId : tasks[toolIntervalVar2TaskId[t][i - 1]].LocationId; - if (i == 0) + ttWithVirtualLocation[d1] = new int[factoryData.NbWorkLocations + 1]; + for (int d2 = 0; d2 < ttWithVirtualLocation.Length; d2++) + if (d1 == factoryData.NbWorkLocations) { - // To be changed - right now we don't have meaningful indata - // of previous location Ugly way of setting initial travel - // time to = 0, as this is how we find common grounds - // between benchmark algorithm and this - tool2TransitionTimes[t].Add( - solver.MakeElement(new int[ttWithVirtualLocation[d].Length], nextLocation[i]).Var()); + ttWithVirtualLocation[d1][d2] = 0; } else { - tool2TransitionTimes[t].Add( - solver.MakeElement(ttWithVirtualLocation[d], nextLocation[i]).Var()); + ttWithVirtualLocation[d1][d2] = (d2 == factoryData.NbWorkLocations) ? 0 : tt[d1, d2]; } - } - - // Extra elements for the unreachable start node (0), and the - // end node whose next task takes place in a virtual location - startingTimes[t] = new IntVar[s + 2]; - endTimes[t] = new IntVar[s + 2]; - - startingTimes[t][0] = solver.MakeIntConst(0); - // Tbd: Set this endtime to the estimated time of finishing - // previous task for the current tool - endTimes[t][0] = solver.MakeIntConst(0); - - for (int i = 0; i < s; i++) - { - startingTimes[t][i + 1] = tool2Task[t][i].SafeStartExpr(-1).Var(); - endTimes[t][i + 1] = tool2Task[t][i].SafeEndExpr(-1).Var(); - } - startingTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); - endTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); - - // Enforce (or not) that each task is separated by the - // transition time to the next task - for (int i = 0; i < nextLocation.Length; i++) - { - IntVar nextStart = solver.MakeElement(startingTimes[t], seq.Next(i).Var()).Var(); - if (postTransitionsConstraint) - solver.Add(endTimes[t][i] + tool2TransitionTimes[t][i] <= nextStart); - } } - } - private void Model() - { - /* Building basic task data structures */ - for (int i = 0; i < tasks.Length; i++) + for (int i = 0; i < nextLocation.Length; i++) { - /* Create a new set of possible IntervalVars & IntVar to decide - * which one (and only 1) is performed */ - taskStructures[i] = new TaskAlternative(tasks[i]); + // this is the next-location associated with the i-th task + nextLocation[i] = solver.MakeElement(taskIndex2locationId, seq.Next(i)).Var(); - /* Container to use when posting constraints */ - location2Task[tasks[i].LocationId][tasks[i].TaskPosition] = taskStructures[i]; - - /* Get task type */ - int taskType = tasks[i].TaskType; - - /* Possible tool for this task */ - List tools = taskType2Tool[taskType]; - bool optional = tools.Count > 1; - - /* List of boolean variables. If performedOnTool[t] == true then - * the task is performed on tool t */ - List performedOnTool = new List(); - for (int t = 0; t < tools.Count; t++) + int d = (i == 0) ? tool.InitialLocationId : tasks[toolIntervalVar2TaskId[t][i - 1]].LocationId; + if (i == 0) { - /* Creating an IntervalVar. If tools.Count > 1 the intervalVar - * is *OPTIONAL* */ - int toolId = tools[t].Id; - Debug.Assert(tasks[i].Durations.ContainsKey(toolId)); - int duration = tasks[i].Durations[toolId]; - string name = "J " + tasks[i].Id + " [" + toolId + "]"; - - IntervalVar intervalVar; - if (taskType == factoryData.Inspection) - { - /* We set a 0 time if the task is an inspection */ - duration = 0; - intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); - IntVar start = intervalVar.SafeStartExpr(-1).Var(); - - intervalVar.SafeStartExpr(-1).Var().SetValues(factoryData.InspectionStarts); - } - else - { - intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); - } - - taskStructures[i].Intervals.Add(intervalVar); - tool2Task[toolId].Add(intervalVar); - toolIntervalVar2TaskId[toolId].Add(i); - - /* Collecting all the bool vars, even if they are optional */ - performedOnTool.Add(intervalVar.PerformedExpr().Var()); + // To be changed - right now we don't have meaningful indata + // of previous location Ugly way of setting initial travel + // time to = 0, as this is how we find common grounds + // between benchmark algorithm and this + tool2TransitionTimes[t].Add( + solver.MakeElement(new int[ttWithVirtualLocation[d].Length], nextLocation[i]).Var()); } - - /* Linking the bool var to a single integer variable: */ - /* if alternativeToolVar == t <=> performedOnTool[t] == true */ - string alternativeName = "J " + tasks[i].Id; - IntVar alternativeToolVar = solver.MakeIntVar(0, tools.Count - 1, alternativeName); - taskStructures[i].ToolVar = alternativeToolVar; - - solver.Add(solver.MakeMapDomain(alternativeToolVar, performedOnTool.ToArray())); - Debug.Assert(performedOnTool.ToArray().Length == alternativeToolVar.Max() + 1); - - selectedTool.Add(alternativeToolVar); - } - - /* Creates precedences on a work Location in order to enforce a - * fully ordered set within the same location - */ - for (int d = 0; d < location2Task.Length; d++) - { - for (int i = 0; i < location2Task[d].Length - 1; i++) + else { - TaskAlternative task1 = location2Task[d][i]; - TaskAlternative task2 = location2Task[d][i + 1]; - /* task1 must end before task2 starts */ - /* Adding precedence for each possible alternative pair */ - for (int t1 = 0; t1 < task1.Intervals.Count(); t1++) - { - IntervalVar task1Alternative = task1.Intervals[t1]; - for (int t2 = 0; t2 < task2.Intervals.Count(); t2++) - { - IntervalVar task2Alternative = task2.Intervals[t2]; - Constraint precedence = solver.MakeIntervalVarRelation( - task2Alternative, Solver.STARTS_AFTER_END, task1Alternative); - solver.Add(precedence); - } - } + tool2TransitionTimes[t].Add(solver.MakeElement(ttWithVirtualLocation[d], nextLocation[i]).Var()); } } - /* Adds disjunctive constraints on unary resources, and creates - * sequence variables. */ - for (int t = 0; t < factoryData.NbTools; t++) + // Extra elements for the unreachable start node (0), and the + // end node whose next task takes place in a virtual location + startingTimes[t] = new IntVar[s + 2]; + endTimes[t] = new IntVar[s + 2]; + + startingTimes[t][0] = solver.MakeIntConst(0); + // Tbd: Set this endtime to the estimated time of finishing + // previous task for the current tool + endTimes[t][0] = solver.MakeIntConst(0); + + for (int i = 0; i < s; i++) { - string name = "Tool " + t; - - if (!factoryData.Tools[t].CanPerformTaskType(factoryData.Inspection)) - { - DisjunctiveConstraint ct = solver.MakeDisjunctiveConstraint(tool2Task[t].ToArray(), name); - solver.Add(ct); - allToolSequences[t] = ct.SequenceVar(); - } - PostTransitionTimeConstraints(t, true); + startingTimes[t][i + 1] = tool2Task[t][i].SafeStartExpr(-1).Var(); + endTimes[t][i + 1] = tool2Task[t][i].SafeEndExpr(-1).Var(); } + startingTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); + endTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); - /* Collecting all tasks end for makespan objective function */ - List intervalEnds = new List(); - for (int i = 0; i < tasks.Length; i++) - foreach (IntervalVar var in taskStructures[i].Intervals) - intervalEnds.Add(var.SafeEndExpr(-1).Var()); - - /* Objective: minimize the makespan (maximum end times of all tasks) */ - makespan = solver.MakeMax(intervalEnds.ToArray()).Var(); - objective = solver.MakeMinimize(makespan, 1); - } - - private void Search() - { - int seed = 2; // This is a good seed to show the crash - - /* Assigning first tools */ - DecisionBuilder myToolAssignmentPhase = new RandomSelectToolHeuristic(this, seed); - - /* Ranking of the tools */ - DecisionBuilder sequencingPhase = solver.MakePhase(allToolSequences, Solver.SEQUENCE_DEFAULT); - - /* Then fixing time of tasks as early as possible */ - DecisionBuilder timingPhase = - solver.MakePhase(makespan, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - - /* Overall phase */ - DecisionBuilder mainPhase = solver.Compose(myToolAssignmentPhase, sequencingPhase, timingPhase); - - /* Logging */ - const int logFrequency = 1000000; - SearchMonitor searchLog = solver.MakeSearchLog(logFrequency, objective); - - /* Restarts */ - SearchMonitor searchRestart = solver.MakeLubyRestart(100); - - /* Search Limit in ms */ - SearchLimit limit = solver.MakeTimeLimit(180 * 1000); - - /* Collecting best solution */ - SolutionCollector collector = solver.MakeLastSolutionCollector(); - collector.AddObjective(makespan); - - // collector.Add( pile.ToArray() ); - solver.NewSearch(mainPhase, searchLog, searchRestart, objective, limit); - while (solver.NextSolution()) + // Enforce (or not) that each task is separated by the + // transition time to the next task + for (int i = 0; i < nextLocation.Length; i++) { - Console.WriteLine("MAKESPAN: " + makespan.Value()); + IntVar nextStart = solver.MakeElement(startingTimes[t], seq.Next(i).Var()).Var(); + if (postTransitionsConstraint) + solver.Add(endTimes[t][i] + tool2TransitionTimes[t][i] <= nextStart); } } - - public void Solve() - { - Init(); - Model(); - Search(); - } } - public class Issue18Test + private void Model() { - [Fact] - public void FactorySchedulingTest() + /* Building basic task data structures */ + for (int i = 0; i < tasks.Length; i++) { - FactoryScheduling scheduling = new FactoryScheduling(new SmallSyntheticData().FetchData()); - scheduling.Solve(); + /* Create a new set of possible IntervalVars & IntVar to decide + * which one (and only 1) is performed */ + taskStructures[i] = new TaskAlternative(tasks[i]); + + /* Container to use when posting constraints */ + location2Task[tasks[i].LocationId][tasks[i].TaskPosition] = taskStructures[i]; + + /* Get task type */ + int taskType = tasks[i].TaskType; + + /* Possible tool for this task */ + List tools = taskType2Tool[taskType]; + bool optional = tools.Count > 1; + + /* List of boolean variables. If performedOnTool[t] == true then + * the task is performed on tool t */ + List performedOnTool = new List(); + for (int t = 0; t < tools.Count; t++) + { + /* Creating an IntervalVar. If tools.Count > 1 the intervalVar + * is *OPTIONAL* */ + int toolId = tools[t].Id; + Debug.Assert(tasks[i].Durations.ContainsKey(toolId)); + int duration = tasks[i].Durations[toolId]; + string name = "J " + tasks[i].Id + " [" + toolId + "]"; + + IntervalVar intervalVar; + if (taskType == factoryData.Inspection) + { + /* We set a 0 time if the task is an inspection */ + duration = 0; + intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); + IntVar start = intervalVar.SafeStartExpr(-1).Var(); + + intervalVar.SafeStartExpr(-1).Var().SetValues(factoryData.InspectionStarts); + } + else + { + intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); + } + + taskStructures[i].Intervals.Add(intervalVar); + tool2Task[toolId].Add(intervalVar); + toolIntervalVar2TaskId[toolId].Add(i); + + /* Collecting all the bool vars, even if they are optional */ + performedOnTool.Add(intervalVar.PerformedExpr().Var()); + } + + /* Linking the bool var to a single integer variable: */ + /* if alternativeToolVar == t <=> performedOnTool[t] == true */ + string alternativeName = "J " + tasks[i].Id; + IntVar alternativeToolVar = solver.MakeIntVar(0, tools.Count - 1, alternativeName); + taskStructures[i].ToolVar = alternativeToolVar; + + solver.Add(solver.MakeMapDomain(alternativeToolVar, performedOnTool.ToArray())); + Debug.Assert(performedOnTool.ToArray().Length == alternativeToolVar.Max() + 1); + + selectedTool.Add(alternativeToolVar); + } + + /* Creates precedences on a work Location in order to enforce a + * fully ordered set within the same location + */ + for (int d = 0; d < location2Task.Length; d++) + { + for (int i = 0; i < location2Task[d].Length - 1; i++) + { + TaskAlternative task1 = location2Task[d][i]; + TaskAlternative task2 = location2Task[d][i + 1]; + /* task1 must end before task2 starts */ + /* Adding precedence for each possible alternative pair */ + for (int t1 = 0; t1 < task1.Intervals.Count(); t1++) + { + IntervalVar task1Alternative = task1.Intervals[t1]; + for (int t2 = 0; t2 < task2.Intervals.Count(); t2++) + { + IntervalVar task2Alternative = task2.Intervals[t2]; + Constraint precedence = + solver.MakeIntervalVarRelation(task2Alternative, Solver.STARTS_AFTER_END, task1Alternative); + solver.Add(precedence); + } + } + } + } + + /* Adds disjunctive constraints on unary resources, and creates + * sequence variables. */ + for (int t = 0; t < factoryData.NbTools; t++) + { + string name = "Tool " + t; + + if (!factoryData.Tools[t].CanPerformTaskType(factoryData.Inspection)) + { + DisjunctiveConstraint ct = solver.MakeDisjunctiveConstraint(tool2Task[t].ToArray(), name); + solver.Add(ct); + allToolSequences[t] = ct.SequenceVar(); + } + PostTransitionTimeConstraints(t, true); + } + + /* Collecting all tasks end for makespan objective function */ + List intervalEnds = new List(); + for (int i = 0; i < tasks.Length; i++) + foreach (IntervalVar var in taskStructures[i].Intervals) + intervalEnds.Add(var.SafeEndExpr(-1).Var()); + + /* Objective: minimize the makespan (maximum end times of all tasks) */ + makespan = solver.MakeMax(intervalEnds.ToArray()).Var(); + objective = solver.MakeMinimize(makespan, 1); + } + + private void Search() + { + int seed = 2; // This is a good seed to show the crash + + /* Assigning first tools */ + DecisionBuilder myToolAssignmentPhase = new RandomSelectToolHeuristic(this, seed); + + /* Ranking of the tools */ + DecisionBuilder sequencingPhase = solver.MakePhase(allToolSequences, Solver.SEQUENCE_DEFAULT); + + /* Then fixing time of tasks as early as possible */ + DecisionBuilder timingPhase = solver.MakePhase(makespan, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + + /* Overall phase */ + DecisionBuilder mainPhase = solver.Compose(myToolAssignmentPhase, sequencingPhase, timingPhase); + + /* Logging */ + const int logFrequency = 1000000; + SearchMonitor searchLog = solver.MakeSearchLog(logFrequency, objective); + + /* Restarts */ + SearchMonitor searchRestart = solver.MakeLubyRestart(100); + + /* Search Limit in ms */ + SearchLimit limit = solver.MakeTimeLimit(180 * 1000); + + /* Collecting best solution */ + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(makespan); + + // collector.Add( pile.ToArray() ); + solver.NewSearch(mainPhase, searchLog, searchRestart, objective, limit); + while (solver.NextSolution()) + { + Console.WriteLine("MAKESPAN: " + makespan.Value()); } } + + public void Solve() + { + Init(); + Model(); + Search(); + } +} + +public class Issue18Test +{ + [Fact] + public void FactorySchedulingTest() + { + FactoryScheduling scheduling = new FactoryScheduling(new SmallSyntheticData().FetchData()); + scheduling.Solve(); + } +} } // namespace Google.OrTools.Test diff --git a/ortools/sat/csharp/IntegerExpressions.cs b/ortools/sat/csharp/IntegerExpressions.cs index 86a49ba9f5..5aaccb58ac 100644 --- a/ortools/sat/csharp/IntegerExpressions.cs +++ b/ortools/sat/csharp/IntegerExpressions.cs @@ -53,7 +53,7 @@ public class LinearExpr public static LinearExpr Sum(IEnumerable vars) { return NewBuilder().AddSum(vars); - } + } public static LinearExpr WeightedSum(IEnumerable exprs, IEnumerable coeffs) { @@ -73,7 +73,7 @@ public class LinearExpr public static LinearExpr WeightedSum(IEnumerable literals, IEnumerable coeffs) { return NewBuilder().AddWeightedSum(literals, coeffs); - } + } public static LinearExpr WeightedSum(IEnumerable vars, IEnumerable coeffs) { @@ -83,29 +83,29 @@ public class LinearExpr public static LinearExpr WeightedSum(IEnumerable vars, IEnumerable coeffs) { return NewBuilder().AddWeightedSum(vars, coeffs); - } + } public static LinearExpr Term(LinearExpr expr, long coeff) { return Prod(expr, coeff); } - public static LinearExpr Term(ILiteral literal, long coeff) + public static LinearExpr Term(ILiteral literal, long coeff) { - if (literal is BoolVar) + if (literal is BoolVar) { - return Prod((IntVar) literal, coeff); + return Prod((IntVar)literal, coeff); } else { - return Affine((BoolVar) literal.Not(), -coeff, coeff); + return Affine((BoolVar)literal.Not(), -coeff, coeff); } } - public static LinearExpr Term(BoolVar var, long coeff) + public static LinearExpr Term(BoolVar var, long coeff) { return Prod(var, coeff); - } + } public static LinearExpr Affine(LinearExpr expr, long coeff, long offset) { @@ -445,14 +445,14 @@ public class LinearExprBuilder : LinearExpr return this; } - public LinearExprBuilder AddSum(IEnumerable vars) + public LinearExprBuilder AddSum(IEnumerable vars) { foreach (BoolVar var in vars) { AddTerm(var, 1); } return this; - } + } public LinearExprBuilder AddWeightedSum(IEnumerable exprs, IEnumerable coefficients) { foreach (var p in exprs.Zip(coefficients, (e, c) => new { Expr = e, Coeff = c })) @@ -506,7 +506,7 @@ public class LinearExprBuilder : LinearExpr } return this; } - + public override string ToString() { string result = "";