[CP-SAT] change constraints in CpModel C# API to allow incremental filling like in C++/Java

This commit is contained in:
Laurent Perron
2022-01-11 12:43:56 +01:00
parent c22032a7fe
commit 577f5aef77
10 changed files with 425 additions and 283 deletions

View File

@@ -89,7 +89,11 @@ public class GateSchedulingSat
}
// Max Length constraint (modeled as a cumulative)
model.AddCumulative(intervals, demands, max_length);
CumulativeConstraint cumul = model.AddCumulative(max_length);
foreach (var p in intervals.Zip(demands, (i, d) => new { Interval = i, Demand = d }))
{
cumul.AddDemand(p.Interval, p.Demand);
}
// Choose which machine to perform the jobs on.
model.AddNoOverlap(intervals0);

View File

@@ -18,8 +18,8 @@ using Google.OrTools.Sat;
public class NurseSolutionObserver : CpSolverSolutionCallback
{
public NurseSolutionObserver(BoolVar[,,] shifts, int num_nurses, int num_days, int num_shifts, HashSet<int> to_print,
int last_solution_explored)
public NurseSolutionObserver(BoolVar[,,] shifts, int num_nurses, int num_days, int num_shifts,
HashSet<int> to_print, int last_solution_explored)
{
shifts_ = shifts;
num_nurses_ = num_nurses;

View File

@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System;
using System.Collections.Generic;
using Google.OrTools.Sat;

View File

@@ -72,8 +72,9 @@ class Tsp
}
// Add dummy dimension to test API.
routing.AddDimension(routing.RegisterUnaryTransitCallback((long index) => { return 1; }), size + 1, size + 1,
true, "dummy");
routing.AddDimension(routing.RegisterUnaryTransitCallback((long index) =>
{ return 1; }),
size + 1, size + 1, true, "dummy");
// Solve, returns a solution if any (owned by RoutingModel).
RoutingSearchParameters search_parameters =

View File

@@ -145,7 +145,7 @@ public class SatSolverTest
CpSolverResponse response = solver.Response;
Assert.Equal(30, response.ObjectiveValue);
Assert.Equal(new long[] { 10, 10, 30 }, response.Solution);
// Console.WriteLine("response = " + reponse.ToString());
// Console.WriteLine("response = " + response.ToString());
}
[Fact]
@@ -165,7 +165,7 @@ public class SatSolverTest
CpSolverResponse response = solver.Response;
Assert.Equal(30, response.ObjectiveValue);
Assert.Equal(new long[] { 10, -10 }, response.Solution);
// Console.WriteLine("response = " + reponse.ToString());
// Console.WriteLine("response = " + response.ToString());
}
[Fact]
@@ -188,7 +188,7 @@ public class SatSolverTest
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());
// Console.WriteLine("response = " + response.ToString());
}
[Fact]
@@ -233,7 +233,10 @@ public class SatSolverTest
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);
TableConstraint ct = model.AddAllowedAssignments(new IntVar[] { delta, squaredDelta });
for (int i = 0; i < tuples.GetLength(0); ++i) {
ct.AddTuple(new long[] { tuples[i, 0], tuples[i, 1]});
}
model.Minimize(squaredDelta);
CpSolver solver = new CpSolver();
@@ -266,7 +269,7 @@ public class SatSolverTest
Assert.Equal(1, solver.Value(v2));
Assert.Equal(new long[] { 3, 1 }, response.Solution);
Assert.Equal(0, response.ObjectiveValue);
// Console.WriteLine("response = " + reponse.ToString());
// Console.WriteLine("response = " + response.ToString());
}
[Fact]
@@ -287,7 +290,7 @@ public class SatSolverTest
Assert.Equal(4, solver.Value(v2));
Assert.Equal(new long[] { 3, 4 }, response.Solution);
Assert.Equal(0, response.ObjectiveValue);
// Console.WriteLine("response = " + reponse.ToString());
// Console.WriteLine("response = " + response.ToString());
}
[Fact]
@@ -376,6 +379,7 @@ public class SatSolverTest
[Fact]
public void LinearExprStaticCompileTest()
{
Console.WriteLine("LinearExprStaticCompileTest");
CpModel model = new CpModel();
IntVar v1 = model.NewIntVar(-10, 10, "v1");
IntVar v2 = model.NewIntVar(-10, 10, "v2");
@@ -406,6 +410,7 @@ public class SatSolverTest
[Fact]
public void LinearExprBuilderCompileTest()
{
Console.WriteLine("LinearExprBuilderCompileTest");
CpModel model = new CpModel();
IntVar v1 = model.NewIntVar(-10, 10, "v1");
IntVar v2 = model.NewIntVar(-10, 10, "v2");
@@ -443,8 +448,79 @@ public class SatSolverTest
Console.WriteLine(e14.ToString());
LinearExpr e15 = LinearExpr.NewBuilder().AddTerm(b1.Not(), -2);
Console.WriteLine(e15.ToString());
}
}
[Fact]
public void LinearExprIntVarOperatorTest()
{
Console.WriteLine("LinearExprIntVarOperatorTest");
CpModel model = new CpModel();
IntVar v = model.NewIntVar(-10, 10, "v");
LinearExpr e = v * 2;
Console.WriteLine(e);
e = 2 * v;
Console.WriteLine(e);
e = v + 2;
Console.WriteLine(e);
e = 2 + v;
Console.WriteLine(e);
e = v;
Console.WriteLine(e);
e = -v;
Console.WriteLine(e);
e = 1 - v;
Console.WriteLine(e);
e = v - 1;
Console.WriteLine(e);
}
[Fact]
public void LinearExprBoolVarOperatorTest()
{
Console.WriteLine("LinearExprBoolVarOperatorTest");
CpModel model = new CpModel();
BoolVar v = model.NewBoolVar("v");
LinearExpr e = v * 2;
Console.WriteLine(e);
e = 2 * v;
Console.WriteLine(e);
e = v + 2;
Console.WriteLine(e);
e = 2 + v;
Console.WriteLine(e);
e = v;
Console.WriteLine(e);
e = -v;
Console.WriteLine(e);
e = 1 - v;
Console.WriteLine(e);
e = v - 1;
Console.WriteLine(e);
}
[Fact]
public void LinearExprNotBoolVarOperatorTest()
{
Console.WriteLine("LinearExprBoolVarNotOperatorTest");
CpModel model = new CpModel();
ILiteral v = model.NewBoolVar("v");
LinearExpr e = v.NotAsExpr() * 2;
Console.WriteLine(e);
e = 2 * v.NotAsExpr();
Console.WriteLine(e);
e = v.NotAsExpr() + 2;
Console.WriteLine(e);
e = 2 + v.NotAsExpr();
Console.WriteLine(e);
e = v.NotAsExpr();
Console.WriteLine(e);
e = -v.NotAsExpr();
Console.WriteLine(e);
e = 1 - v.NotAsExpr();
Console.WriteLine(e);
e = v.NotAsExpr() - 1;
Console.WriteLine(e);
}
[Fact]
public void ExportModel()
{

View File

@@ -59,4 +59,226 @@ public class Constraint
private ConstraintProto constraint_;
}
/**
* Specialized circuit constraint.
*
* <p>This constraint allows adding arcs to the circuit constraint incrementally.
*/
public class CircuitConstraint : Constraint
{
public CircuitConstraint(CpModelProto model) : base(model)
{
}
/**
* Add an arc to the graph of the circuit constraint.
*
* @param tail the index of the tail node.
* @param head the index of the head node.
* @param literal it will be set to true if the arc is selected.
*/
public void AddArc(int tail, int head, ILiteral literal)
{
CircuitConstraintProto circuit = Proto.Circuit;
circuit.Tails.Add(tail);
circuit.Heads.Add(head);
circuit.Literals.Add(literal.GetIndex());
}
}
/**
* Specialized multiple circuit constraint.
*
* <p>This constraint allows adding arcs to the multiple circuit constraint incrementally.
*/
public class MultipleCircuitConstraint : Constraint
{
public MultipleCircuitConstraint(CpModelProto model) : base(model)
{
}
/**
* Add an arc to the graph of the circuit constraint.
*
* @param tail the index of the tail node.
* @param head the index of the head node.
* @param literal it will be set to true if the arc is selected.
*/
public void AddArc(int tail, int head, ILiteral literal)
{
RoutesConstraintProto routes = Proto.Routes;
routes.Tails.Add(tail);
routes.Heads.Add(head);
routes.Literals.Add(literal.GetIndex());
}
}
/**
* Specialized assignment constraint.
*
* <p>This constraint allows adding tuples to the allowed/forbidden assignment constraint
* incrementally.
*/
public class TableConstraint : Constraint
{
public TableConstraint(CpModelProto model) : base(model)
{
}
/**
* Adds a tuple of possible/forbidden values to the constraint.
*
* @param tuple the tuple to add to the constraint.
* @throws CpModel.WrongLength if the tuple does not have the same length as the array of
* variables of the constraint.
*/
public void AddTuple(IEnumerable<int> tuple)
{
TableConstraintProto table = Proto.Table;
int count = 0;
foreach (int value in tuple)
{
table.Values.Add(value);
count++;
}
if (count != table.Vars.Count)
{
throw new ArgumentException("addTuple", "tuple does not have the same length as the variables");
}
}
/**
* Adds a tuple of possible/forbidden values to the constraint.
*
* @param tuple the tuple to add to the constraint.
* @throws CpModel.WrongLength if the tuple does not have the same length as the array of
* variables of the constraint.
*/
public void AddTuple(IEnumerable<long> tuple)
{
TableConstraintProto table = Proto.Table;
int count = 0;
foreach (long value in tuple)
{
table.Values.Add(value);
count++;
}
if (count != table.Vars.Count)
{
throw new ArgumentException("addTuple", "tuple does not have the same length as the variables");
}
}
}
/**
* Specialized automaton constraint.
*
* <p>This constraint allows adding transitions to the automaton constraint incrementally.
*/
public class AutomatonConstraint : Constraint
{
public AutomatonConstraint(CpModelProto model) : base(model)
{
}
/// Adds a transitions to the automaton.
public void AddTransition(int tail, int head, long label)
{
AutomatonConstraintProto aut = Proto.Automaton;
aut.TransitionTail.Add(tail);
aut.TransitionLabel.Add(label);
aut.TransitionHead.Add(head);
}
}
/**
* Specialized reservoir constraint.
*
* <p>This constraint allows adding events (time, levelChange, isActive (optional)) to the reservoir
* constraint incrementally.
*/
public class ReservoirConstraint : Constraint
{
public ReservoirConstraint(CpModel cp_model, CpModelProto model) : base(model)
{
this.cp_model_ = cp_model;
}
/**
* Adds a mandatory event
*
* <p>It will increase the used capacity by `levelChange` at time `time`. `time` must be an affine
* expression.
*/
public void AddEvent<T, L>(T time, L level_change)
{
ReservoirConstraintProto res = Proto.Reservoir;
res.TimeExprs.Add(cp_model_.GetLinearExpressionProto(cp_model_.GetLinearExpr(time)));
res.LevelChanges.Add(Convert.ToInt64(level_change));
res.ActiveLiterals.Add(cp_model_.TrueLiteral().GetIndex());
}
/**
* Adds an optional event
*
* <p>If `isActive` is true, It will increase the used capacity by `levelChange` at time `time`.
* `time` must be an affine expression.
*/
public void AddOptionalEvent<T, L>(T time, L level_change, ILiteral literal)
{
ReservoirConstraintProto res = Proto.Reservoir;
res.TimeExprs.Add(cp_model_.GetLinearExpressionProto(cp_model_.GetLinearExpr(time)));
res.LevelChanges.Add(Convert.ToInt64(level_change));
res.ActiveLiterals.Add(literal.GetIndex());
}
private CpModel cp_model_;
}
/**
* Specialized cumulative constraint.
*
* <p>This constraint allows adding (interval, demand) pairs to the cumulative constraint
* incrementally.
*/
public class CumulativeConstraint : Constraint
{
public CumulativeConstraint(CpModel cp_model, CpModelProto model) : base(model)
{
this.cp_model_ = cp_model;
}
/// Adds a pair (interval, demand) to the constraint.
public void AddDemand<D>(IntervalVar interval, D demand)
{
CumulativeConstraintProto cumul = Proto.Cumulative;
cumul.Intervals.Add(interval.GetIndex());
LinearExpr demandExpr = cp_model_.GetLinearExpr(demand);
cumul.Demands.Add(cp_model_.GetLinearExpressionProto(demandExpr));
}
private CpModel cp_model_;
}
/**
* Specialized NoOverlap2D constraint.
*
* <p>This constraint allows adding rectanles to the NoOverlap2D constraint incrementally.
*/
public class NoOverlap2dConstraint : Constraint
{
public NoOverlap2dConstraint(CpModelProto model) : base(model)
{
}
/// Adds a rectangle (xInterval, yInterval) to the constraint.
public void AddRectangle(IntervalVar xInterval, IntervalVar yInterval)
{
Proto.NoOverlap2D.XIntervals.Add(xInterval.GetIndex());
Proto.NoOverlap2D.YIntervals.Add(yInterval.GetIndex());
}
}
} // namespace Google.OrTools.Sat

View File

@@ -195,50 +195,43 @@ public class CpModel
return ct;
}
public Constraint AddCircuit(IEnumerable<Tuple<int, int, ILiteral>> arcs)
public CircuitConstraint AddCircuit()
{
Constraint ct = new Constraint(model_);
CircuitConstraintProto circuit = new CircuitConstraintProto();
foreach (var arc in arcs)
{
circuit.Tails.Add(arc.Item1);
circuit.Heads.Add(arc.Item2);
circuit.Literals.Add(arc.Item3.GetIndex());
}
ct.Proto.Circuit = circuit;
CircuitConstraint ct = new CircuitConstraint(model_);
ct.Proto.Circuit = new CircuitConstraintProto();
return ct;
}
public Constraint AddAllowedAssignments(IEnumerable<IntVar> vars, long[,] tuples)
public MultipleCircuitConstraint AddMultipleCircuit()
{
Constraint ct = new Constraint(model_);
MultipleCircuitConstraint ct = new MultipleCircuitConstraint(model_);
ct.Proto.Routes = new RoutesConstraintProto();
return ct;
}
public TableConstraint AddAllowedAssignments(IEnumerable<IntVar> vars)
{
TableConstraint ct = new TableConstraint(model_);
TableConstraintProto table = new TableConstraintProto();
foreach (IntVar var in vars)
{
table.Vars.Add(var.Index);
}
for (int i = 0; i < tuples.GetLength(0); ++i)
{
for (int j = 0; j < tuples.GetLength(1); ++j)
{
table.Values.Add(tuples[i, j]);
}
}
ct.Proto.Table = table;
return ct;
}
public Constraint AddForbiddenAssignments(IEnumerable<IntVar> vars, long[,] tuples)
public TableConstraint AddForbiddenAssignments(IEnumerable<IntVar> vars)
{
Constraint ct = AddAllowedAssignments(vars, tuples);
TableConstraint ct = AddAllowedAssignments(vars);
ct.Proto.Table.Negated = true;
return ct;
}
public Constraint AddAutomaton(IEnumerable<IntVar> vars, long starting_state, long[,] transitions,
IEnumerable<long> final_states)
public AutomatonConstraint AddAutomaton(IEnumerable<IntVar> vars, long starting_state,
IEnumerable<long> final_states)
{
Constraint ct = new Constraint(model_);
AutomatonConstraint ct = new AutomatonConstraint(model_);
AutomatonConstraintProto aut = new AutomatonConstraintProto();
foreach (IntVar var in vars)
{
@@ -249,38 +242,6 @@ public class CpModel
{
aut.FinalStates.Add(f);
}
for (int i = 0; i < transitions.GetLength(0); ++i)
{
aut.TransitionTail.Add(transitions[i, 0]);
aut.TransitionLabel.Add(transitions[i, 1]);
aut.TransitionHead.Add(transitions[i, 2]);
}
ct.Proto.Automaton = aut;
return ct;
}
public Constraint AddAutomaton(IEnumerable<IntVar> vars, long starting_state,
IEnumerable<Tuple<long, long, long>> transitions, IEnumerable<long> final_states)
{
Constraint ct = new Constraint(model_);
AutomatonConstraintProto aut = new AutomatonConstraintProto();
foreach (IntVar var in vars)
{
aut.Vars.Add(var.Index);
}
aut.StartingState = starting_state;
foreach (long f in final_states)
{
aut.FinalStates.Add(f);
}
foreach (Tuple<long, long, long> transition in transitions)
{
aut.TransitionHead.Add(transition.Item1);
aut.TransitionLabel.Add(transition.Item2);
aut.TransitionTail.Add(transition.Item3);
}
ct.Proto.Automaton = aut;
return ct;
}
@@ -301,19 +262,10 @@ public class CpModel
return ct;
}
public Constraint AddReservoirConstraint<I>(IEnumerable<IntVar> times, IEnumerable<I> levelChanges, long minLevel,
long maxLevel)
public ReservoirConstraint AddReservoirConstraint(long minLevel, long maxLevel)
{
Constraint ct = new Constraint(model_);
ReservoirConstraint ct = new ReservoirConstraint(this, model_);
ReservoirConstraintProto res = new ReservoirConstraintProto();
foreach (IntVar time in times)
{
res.TimeExprs.Add(GetLinearExpressionProto(time));
}
foreach (I d in levelChanges)
{
res.LevelChanges.Add(Convert.ToInt64(d));
}
res.MinLevel = minLevel;
res.MaxLevel = maxLevel;
@@ -322,75 +274,6 @@ public class CpModel
return ct;
}
public Constraint AddReservoirConstraintWithActive<I>(IEnumerable<IntVar> times, IEnumerable<I> levelChanges,
IEnumerable<IntVar> actives, long minLevel, long maxLevel)
{
Constraint ct = new Constraint(model_);
ReservoirConstraintProto res = new ReservoirConstraintProto();
foreach (IntVar time in times)
{
res.TimeExprs.Add(GetLinearExpressionProto(time));
}
foreach (I d in levelChanges)
{
res.LevelChanges.Add(Convert.ToInt64(d));
}
foreach (IntVar var in actives)
{
res.ActiveLiterals.Add(var.Index);
}
res.MinLevel = minLevel;
res.MaxLevel = maxLevel;
ct.Proto.Reservoir = res;
return ct;
}
public Constraint AddReservoirConstraint<I>(IEnumerable<LinearExpr> times, IEnumerable<I> levelChanges,
long minLevel, long maxLevel)
{
Constraint ct = new Constraint(model_);
ReservoirConstraintProto res = new ReservoirConstraintProto();
foreach (LinearExpr time in times)
{
res.TimeExprs.Add(GetLinearExpressionProto(time));
}
foreach (I d in levelChanges)
{
res.LevelChanges.Add(Convert.ToInt64(d));
}
res.MinLevel = minLevel;
res.MaxLevel = maxLevel;
ct.Proto.Reservoir = res;
return ct;
}
public Constraint AddReservoirConstraintWithActive<I>(IEnumerable<LinearExpr> times, IEnumerable<I> levelChanges,
IEnumerable<IntVar> actives, long minLevel, long maxLevel)
{
Constraint ct = new Constraint(model_);
ReservoirConstraintProto res = new ReservoirConstraintProto();
foreach (LinearExpr time in times)
{
res.TimeExprs.Add(GetLinearExpressionProto(time));
}
foreach (I d in levelChanges)
{
res.LevelChanges.Add(Convert.ToInt64(d));
}
foreach (IntVar var in actives)
{
res.ActiveLiterals.Add(var.Index);
}
res.MinLevel = minLevel;
res.MaxLevel = maxLevel;
ct.Proto.Reservoir = res;
return ct;
}
public void AddMapDomain(IntVar var, IEnumerable<IntVar> bool_vars, long offset = 0)
{
int i = 0;
@@ -500,133 +383,89 @@ public class CpModel
return ct;
}
public Constraint AddMinEquality(LinearExpr target, IEnumerable<IntVar> vars)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
foreach (IntVar var in vars)
{
args.Exprs.Add(GetLinearExpressionProto(var, /*negate=*/true));
}
args.Target = GetLinearExpressionProto(target, /*negate=*/true);
ct.Proto.LinMax = args;
return ct;
}
public Constraint AddMinEquality(LinearExpr target, IEnumerable<LinearExpr> exprs)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
LinearArgumentProto lin = new LinearArgumentProto();
foreach (LinearExpr expr in exprs)
{
args.Exprs.Add(GetLinearExpressionProto(expr, /*negate=*/true));
lin.Exprs.Add(GetLinearExpressionProto(expr, /*negate=*/true));
}
args.Target = GetLinearExpressionProto(target, /*negate=*/true);
ct.Proto.LinMax = args;
return ct;
}
public Constraint AddMaxEquality(IntVar target, IEnumerable<IntVar> vars)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
foreach (IntVar var in vars)
{
args.Exprs.Add(GetLinearExpressionProto(var));
}
args.Target = GetLinearExpressionProto(target);
ct.Proto.LinMax = args;
lin.Target = GetLinearExpressionProto(target, /*negate=*/true);
ct.Proto.LinMax = lin;
return ct;
}
public Constraint AddMaxEquality(LinearExpr target, IEnumerable<LinearExpr> exprs)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
LinearArgumentProto lin = new LinearArgumentProto();
foreach (LinearExpr expr in exprs)
{
args.Exprs.Add(GetLinearExpressionProto(expr));
lin.Exprs.Add(GetLinearExpressionProto(expr));
}
args.Target = GetLinearExpressionProto(target);
ct.Proto.LinMax = args;
lin.Target = GetLinearExpressionProto(target);
ct.Proto.LinMax = lin;
return ct;
}
public Constraint AddDivisionEquality<T, N, D>(T target, N num, D denom)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
args.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(num)));
args.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(denom)));
args.Target = GetLinearExpressionProto(GetLinearExpr(target));
ct.Proto.IntDiv = args;
LinearArgumentProto div = new LinearArgumentProto();
div.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(num)));
div.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(denom)));
div.Target = GetLinearExpressionProto(GetLinearExpr(target));
ct.Proto.IntDiv = div;
return ct;
}
public Constraint AddAbsEquality(LinearExpr target, LinearExpr expr)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
args.Exprs.Add(GetLinearExpressionProto(expr));
args.Exprs.Add(GetLinearExpressionProto(expr, /*negate=*/true));
args.Target = GetLinearExpressionProto(target);
ct.Proto.LinMax = args;
LinearArgumentProto abs = new LinearArgumentProto();
abs.Exprs.Add(GetLinearExpressionProto(expr));
abs.Exprs.Add(GetLinearExpressionProto(expr, /*negate=*/true));
abs.Target = GetLinearExpressionProto(target);
ct.Proto.LinMax = abs;
return ct;
}
public Constraint AddModuloEquality<T, V, M>(T target, V v, M m)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
args.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(v)));
args.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(m)));
args.Target = GetLinearExpressionProto(GetLinearExpr(target));
ct.Proto.IntMod = args;
return ct;
}
public Constraint AddMultiplicationEquality(LinearExpr target, IEnumerable<IntVar> vars)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
args.Target = GetLinearExpressionProto(target);
foreach (IntVar var in vars)
{
args.Exprs.Add(GetLinearExpressionProto(var));
}
ct.Proto.IntProd = args;
LinearArgumentProto mod = new LinearArgumentProto();
mod.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(v)));
mod.Exprs.Add(GetLinearExpressionProto(GetLinearExpr(m)));
mod.Target = GetLinearExpressionProto(GetLinearExpr(target));
ct.Proto.IntMod = mod;
return ct;
}
public Constraint AddMultiplicationEquality(LinearExpr target, IEnumerable<LinearExpr> exprs)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
args.Target = GetLinearExpressionProto(target);
LinearArgumentProto prod = new LinearArgumentProto();
prod.Target = GetLinearExpressionProto(target);
foreach (LinearExpr expr in exprs)
{
args.Exprs.Add(GetLinearExpressionProto(expr));
prod.Exprs.Add(GetLinearExpressionProto(expr));
}
ct.Proto.IntProd = args;
ct.Proto.IntProd = prod;
return ct;
}
public Constraint AddMultiplicationEquality(LinearExpr target, LinearExpr left, LinearExpr right)
{
Constraint ct = new Constraint(model_);
LinearArgumentProto args = new LinearArgumentProto();
args.Target = GetLinearExpressionProto(target);
args.Exprs.Add(GetLinearExpressionProto(left));
args.Exprs.Add(GetLinearExpressionProto(right));
ct.Proto.IntProd = args;
LinearArgumentProto prod = new LinearArgumentProto();
prod.Target = GetLinearExpressionProto(target);
prod.Exprs.Add(GetLinearExpressionProto(left));
prod.Exprs.Add(GetLinearExpressionProto(right));
ct.Proto.IntProd = prod;
return ct;
}
public Constraint AddProdEquality(IntVar target, IEnumerable<IntVar> vars)
{
return AddMultiplicationEquality(target, vars);
}
// Scheduling support
public IntervalVar NewIntervalVar<S, D, E>(S start, D duration, E end, string name)
@@ -682,44 +521,26 @@ public class CpModel
public Constraint AddNoOverlap(IEnumerable<IntervalVar> intervals)
{
Constraint ct = new Constraint(model_);
NoOverlapConstraintProto args = new NoOverlapConstraintProto();
NoOverlapConstraintProto no_overlap = new NoOverlapConstraintProto();
foreach (IntervalVar var in intervals)
{
args.Intervals.Add(var.GetIndex());
no_overlap.Intervals.Add(var.GetIndex());
}
ct.Proto.NoOverlap = args;
ct.Proto.NoOverlap = no_overlap;
return ct;
}
public Constraint AddNoOverlap2D(IEnumerable<IntervalVar> x_intervals, IEnumerable<IntervalVar> y_intervals)
public NoOverlap2dConstraint AddNoOverlap2D()
{
Constraint ct = new Constraint(model_);
NoOverlap2DConstraintProto args = new NoOverlap2DConstraintProto();
foreach (IntervalVar var in x_intervals)
{
args.XIntervals.Add(var.GetIndex());
}
foreach (IntervalVar var in y_intervals)
{
args.YIntervals.Add(var.GetIndex());
}
ct.Proto.NoOverlap2D = args;
NoOverlap2dConstraint ct = new NoOverlap2dConstraint(model_);
ct.Proto.NoOverlap2D = new NoOverlap2DConstraintProto();
return ct;
}
public Constraint AddCumulative<D, C>(IEnumerable<IntervalVar> intervals, IEnumerable<D> demands, C capacity)
public CumulativeConstraint AddCumulative<C>(C capacity)
{
Constraint ct = new Constraint(model_);
CumulativeConstraint ct = new CumulativeConstraint(this, model_);
CumulativeConstraintProto cumul = new CumulativeConstraintProto();
foreach (IntervalVar var in intervals)
{
cumul.Intervals.Add(var.GetIndex());
}
foreach (D demand in demands)
{
LinearExpr demandExpr = GetLinearExpr(demand);
cumul.Demands.Add(GetLinearExpressionProto(demandExpr));
}
LinearExpr capacityExpr = GetLinearExpr(capacity);
cumul.Capacity = GetLinearExpressionProto(capacityExpr);
ct.Proto.Cumulative = cumul;
@@ -890,7 +711,7 @@ public class CpModel
throw new ArgumentException("Cannot extract index from argument");
}
private LinearExpr GetLinearExpr<X>(X x)
public LinearExpr GetLinearExpr<X>(X x)
{
if (typeof(X) == typeof(IntVar))
{
@@ -907,7 +728,7 @@ public class CpModel
throw new ArgumentException("Cannot convert argument to LinearExpr");
}
private LinearExpressionProto GetLinearExpressionProto(LinearExpr expr, bool negate = false)
public LinearExpressionProto GetLinearExpressionProto(LinearExpr expr, bool negate = false)
{
Dictionary<IntVar, long> dict = new Dictionary<IntVar, long>();
long constant = LinearExpr.GetVarValueMap(expr, 1L, dict);

View File

@@ -22,6 +22,7 @@ public interface ILiteral
{
ILiteral Not();
int GetIndex();
LinearExpr NotAsExpr();
}
// Holds a term (expression * coefficient)
@@ -333,7 +334,7 @@ public class LinearExpr
}
else if (term.expr is NotBoolVar)
{
IntVar i = ((NotBoolVar)term.expr).NotVar();
IntVar i = (IntVar)((NotBoolVar)term.expr).Not();
if (dict.ContainsKey(i))
{
dict[i] -= term.coefficient;
@@ -712,6 +713,11 @@ public class BoolVar : IntVar, ILiteral
return negation_;
}
public LinearExpr NotAsExpr()
{
return (LinearExpr)Not();
}
private NotBoolVar negation_;
}
@@ -739,9 +745,9 @@ public class NotBoolVar : LinearExpr, ILiteral
return boolvar_;
}
public BoolVar NotVar()
public LinearExpr NotAsExpr()
{
return boolvar_;
return (LinearExpr)Not();
}
public override string ToString()

View File

@@ -40,28 +40,28 @@ public class AssignmentGroupsSat
// Allowed groups of workers:
// [START allowed_groups]
long[,] group1 = {
{ 0, 0, 1, 1 }, // Workers 2, 3
{ 0, 1, 0, 1 }, // Workers 1, 3
{ 0, 1, 1, 0 }, // Workers 1, 2
{ 1, 1, 0, 0 }, // Workers 0, 1
{ 1, 0, 1, 0 }, // Workers 0, 2
long[][] group1 = {
new long[] { 0, 0, 1, 1 }, // Workers 2, 3
new long[] { 0, 1, 0, 1 }, // Workers 1, 3
new long[] { 0, 1, 1, 0 }, // Workers 1, 2
new long[] { 1, 1, 0, 0 }, // Workers 0, 1
new long[] { 1, 0, 1, 0 }, // Workers 0, 2
};
long[,] group2 = {
{ 0, 0, 1, 1 }, // Workers 6, 7
{ 0, 1, 0, 1 }, // Workers 5, 7
{ 0, 1, 1, 0 }, // Workers 5, 6
{ 1, 1, 0, 0 }, // Workers 4, 5
{ 1, 0, 0, 1 }, // Workers 4, 7
long[][] group2 = {
new long[] { 0, 0, 1, 1 }, // Workers 6, 7
new long[] { 0, 1, 0, 1 }, // Workers 5, 7
new long[] { 0, 1, 1, 0 }, // Workers 5, 6
new long[] { 1, 1, 0, 0 }, // Workers 4, 5
new long[] { 1, 0, 0, 1 }, // Workers 4, 7
};
long[,] group3 = {
{ 0, 0, 1, 1 }, // Workers 10, 11
{ 0, 1, 0, 1 }, // Workers 9, 11
{ 0, 1, 1, 0 }, // Workers 9, 10
{ 1, 0, 1, 0 }, // Workers 8, 10
{ 1, 0, 0, 1 }, // Workers 8, 11
long[][] group3 = {
new long[] { 0, 0, 1, 1 }, // Workers 10, 11
new long[] { 0, 1, 0, 1 }, // Workers 9, 11
new long[] { 0, 1, 1, 0 }, // Workers 9, 10
new long[] { 1, 0, 1, 0 }, // Workers 8, 10
new long[] { 1, 0, 0, 1 }, // Workers 8, 11
};
// [END allowed_groups]
@@ -127,9 +127,21 @@ public class AssignmentGroupsSat
}
// Define the allowed groups of worders
model.AddAllowedAssignments(new IntVar[] { work[0], work[1], work[2], work[3] }, group1);
model.AddAllowedAssignments(new IntVar[] { work[4], work[5], work[6], work[7] }, group2);
model.AddAllowedAssignments(new IntVar[] { work[8], work[9], work[10], work[11] }, group3);
TableConstraint group1_ct = model.AddAllowedAssignments(new IntVar[] { work[0], work[1], work[2], work[3] });
foreach (long[] tuple in group1)
{
group1_ct.AddTuple(tuple);
}
TableConstraint group2_ct = model.AddAllowedAssignments(new IntVar[] { work[4], work[5], work[6], work[7] });
foreach (long[] tuple in group2)
{
group2_ct.AddTuple(tuple);
}
TableConstraint group3_ct = model.AddAllowedAssignments(new IntVar[] { work[8], work[9], work[10], work[11] });
foreach (long[] tuple in group3)
{
group3_ct.AddTuple(tuple);
}
// [END assignments]
// Objective

View File

@@ -61,23 +61,23 @@ public class AssignmentTeamsSat
// Constraints
// [START constraints]
// Each worker is assigned to at most one task.
for (int i = 0; i < numWorkers; ++i)
foreach (int worker in allWorkers)
{
List<ILiteral> tasks = new List<ILiteral>();
for (int j = 0; j < numTasks; ++j)
foreach (int task in allTasks)
{
tasks.Add(x[i, j]);
tasks.Add(x[worker, task]);
}
model.AddAtMostOne(tasks);
}
// Each task is assigned to exactly one worker.
for (int j = 0; j < numTasks; ++j)
foreach (int task in allTasks)
{
List<ILiteral> workers = new List<ILiteral>();
for (int i = 0; i < numWorkers; ++i)
foreach (int worker in allWorkers)
{
workers.Add(x[i, j]);
workers.Add(x[worker, task]);
}
model.AddExactlyOne(workers);
}