I'm trying to assign costs if there is a "reciprocal" assignment in an IntVar[]. Here's how I've accomplished it in the older ConstraintSolver...
IntVar[] assignments = solver.MakeIntVarArray(size, 0, size-1, "assignments");
var cost = Enumerable.Range(0, size)
.Select(i => 999 * (assignments.Element(assignments[i]) == i))
.ToArray()
.ToSum()
.Var();
var objective = cost.Minimize(1);
Now I'm trying to use the newer CpSolver
in Google.OrTools.Sat
where the .Element
extension is absent (I assume for good reason). I've managed to get it to "work" using four IntVars[] but I suspect this is just a big modeling fail on my part.
foreach (var i in Enumerable.Range(0, size))
{
model.AddElement(assignments[i], assignments, reciprocals[i]);
model.Add(reciprocals[i] == i).OnlyEnforceIf(reciprocalBools[i]);
model.Add(reciprocals[i] != i).OnlyEnforceIf(reciprocalBools[i].Not());
model.Add(costs[i] == 999).OnlyEnforceIf(reciprocalBools[i]);
model.Add(costs[i] == 0).OnlyEnforceIf(reciprocalBools[i].Not());
}
model.Minimize(costs.Sum());
Based on my testing, the above seems to be functionally correct, however as size
gets larger, the SAT version of my test app performs orders of magnitude worse than the CS version.
Any suggestions would be greatly appreciated.