Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.chocosolver.samples.nsp.NSCPModelConstrained Maven / Gradle / Ivy
/**
* Copyright (c) 2015, Ecole des Mines de Nantes
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the .
* 4. Neither the name of the nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.chocosolver.samples.nsp;
import org.chocosolver.solver.Solver;
import org.chocosolver.solver.constraints.IntConstraintFactory;
import org.chocosolver.solver.constraints.LogicalConstraintFactory;
import org.chocosolver.solver.constraints.SatFactory;
import org.chocosolver.solver.constraints.nary.automata.CostRegular;
import org.chocosolver.solver.constraints.nary.automata.FA.CostAutomaton;
import org.chocosolver.solver.constraints.nary.automata.FA.FiniteAutomaton;
import org.chocosolver.solver.constraints.nary.cnf.LogOp;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.VariableFactory;
import org.chocosolver.util.tools.ArrayUtils;
/*
* Created by IntelliJ IDEA.
* User: sofdem - sophie.demassey{at}emn.fr
* Date: Jul 29, 2010 - 3:32:02 PM
*/
public class NSCPModelConstrained extends NurseSchedulingProblem {
public CostRegular[][] cregs;
public enum ConstraintOptions {
BASIC("cover preAssign") {},
REDUNDANT("cover preAssign symBreak coupling") {},
RED_EQUITY("cover equity preAssign symBreak coupling") {},
WITH_MCRW("pat[MCRegularWeek]") {},
WITH_MCR("countW[occ] pat[MCRegular]") {},
WITH_REG("countM countW[occ] pat[regular]") {},
WITH_REIF("span countW[occ] countM pat[reif]") {};
String option;
ConstraintOptions(String opt) {
option = opt;
}
public String getOptions() {
return option;
}
public boolean isPatternOption() {
return this.name().startsWith("WITH");
}
}
public NSCPModelConstrained(Solver solver) {
this(NSData.makeDefaultInstance(), solver);
}
public NSCPModelConstrained(NSData data, Solver solver) {
this(data, ConstraintOptions.REDUNDANT, ConstraintOptions.WITH_REIF, solver);
}
public NSCPModelConstrained(NSData data, ConstraintOptions basisOptions, ConstraintOptions patternOptions, Solver solver) {
this(data, basisOptions.getOptions(), patternOptions.getOptions(), solver);
}
public NSCPModelConstrained(NSData data, String options, String patternOptions, Solver solver) {
super(data, options + " " + patternOptions, solver);
this.makeConstraints(solver);
}
private void makeConstraints(Solver solver) {
if (this.isSetConstraint("cover")) {
this.makeCover(solver);
}
if (this.isSetConstraint("equity")) {
this.makeEquity();
}
if (this.isSetConstraint("preAssign")) {
this.makePreAssignments(solver);
}
// if (this.isSetConstraint("symBreak")) {
// this.makeSymmetryBreaking(solver);
// }
if (this.isSetConstraint("coupling")) {
this.makeCoverCounterCoupling(solver);
}
if (this.isSetConstraint("countM")) {
this.makeMonthlyCounters(solver);
}
if (this.isSetConstraint("countW")) {
this.makeWeeklyCounters(solver);
}
// if (this.isSetConstraint("span")) {
// this.makeMaxWorkSpan(solver);
// }
if (this.isSetConstraint("pat")) {
this.makeForbiddenPatterns(solver);
}
}
//**************************************************
// MANDATORY/FORBIDDEN Assignments
//**************************************************
private void makePreAssignments(Solver solver) {
description += "preAssign ";
for (int[] trip : data.preAssignments()) {
boolean mandatory = trip[0] > 0;
int e = trip[1];
int t = trip[2];
int a = trip[3];
if (mandatory) {
solver.post(IntConstraintFactory.arithm(shifts[e][t], "=", a));
} else {
solver.post(IntConstraintFactory.arithm(shifts[e][t], "!=", a));
}
}
}
//**************************************************
// SYMMETRY BREAKING Constraints
//**************************************************
/*private void makeSymmetryBreaking(Solver solver) {
description += "symBreak ";
for (int[] group : data.symmetricEmployeeGroups()) {
this.makeSymmetryBreaking(group);
}
}*/
/*private void makeSymmetryBreaking(int[] group) {
IntVar[][] vars = new IntVar[group.length][];
for (int i = 0; i < vars.length; i++) {
vars[i] = shifts[group[i]];
}
solver.post(ConstraintFactory.lexChainEq(vars));
}*/
//**************************************************
// EQUALITY Constraints
//**************************************************
private void makeEquity() {
description += "equity ";
data.equityEmployeeGroups().forEach(this::makeEquityDirect);
}
private void makeEquityDirect(int[] group) {
for (int e = 1; e < group.length; e++) {
occurrences[group[e]] = occurrences[group[0]];
}
}
//**************************************************
// COVER Constraints
//**************************************************
private void makeCover(Solver solver) {
String option = this.getConstraintOption("pat");
if (option.equals("gccFix")) {
this.makeCoverWithGCCFix(solver);
} else {
this.makeCoverWithGCCVar(solver);
}
}
private void makeCoverWithGCCVar(Solver solver) {
description += "cover[gcc] ";
IntVar[][] cards = ArrayUtils.transpose(covers);
IntVar[][] vars = ArrayUtils.transpose(shifts);
for (int t = 0; t < vars.length; t++) {
int[] values = new int[cards[t].length];
for (int i = 0; i < values.length; i++) {
values[i] = i;
}
solver.post(IntConstraintFactory.global_cardinality(vars[t], values, cards[t], false));
}
}
private void makeCoverWithGCCFix(Solver solver) {
description += "cover[gccFix] ";
IntVar[] cover = new IntVar[data.nbActivities()];
for (int a = 0; a < data.nbActivities(); a++) {
cover[a] = VariableFactory.bounded("cover_" + a, data.getCoverLB(a), data.getCoverUB(a), solver);
}
IntVar[][] vars = ArrayUtils.transpose(shifts);
for (IntVar[] var : vars) {
int[] values = new int[cover.length];
for (int i = 0; i < values.length; i++) {
values[i] = i;
}
solver.post(IntConstraintFactory.global_cardinality(var, values, cover, false));
}
}
//**************************************************
// COVER-COUNTER COUPLING Constraints
//**************************************************
private void makeCoverCounterCoupling(Solver solver) {
if (this.isSetConstraint("equity")) {
this.makeCoverCounterCouplingAndEquity(solver);
} else {
for (int a = 0; a < data.nbActivities(); a++) {
this.makeCoverCounterCoupling(solver, a);
}
// this.makeCoverCounterCoupling(data.getValue("REST"));
}
}
private void makeCoverCounterCoupling(Solver solver, int a) {
description += "coupling[" + a + "] ";
solver.post(IntConstraintFactory.sum(ArrayUtils.getColumn(occurrences, a), VariableFactory.fixed(data.getTotalCover(a), solver)));
}
private void makeCoverCounterCouplingAndEquity(Solver solver) {
int[] belongsToNbGroups = new int[data.nbEmployees()];
for (int[] group : data.equityEmployeeGroups()) {
for (int e : group) {
belongsToNbGroups[e]++;
}
}
boolean eachEmployeeBelongsToExactlyOneGroup = true;
for (int n : belongsToNbGroups) {
if (n != 1) {
eachEmployeeBelongsToExactlyOneGroup = false;
break;
}
}
if (eachEmployeeBelongsToExactlyOneGroup) {
for (int a = 0; a < data.nbActivities(); a++) {
this.makeCoverCounterCouplingAndEquity(solver, a);
}
}
}
private void makeCoverCounterCouplingAndEquity(Solver solver, int a) {
IntVar[] occ = new IntVar[data.equityEmployeeGroups().size()];
int sizes[] = new int[occ.length];
int g = 0;
for (int[] group : data.equityEmployeeGroups()) {
occ[g] = occurrences[group[0]][a];
sizes[g] = group.length;
g++;
}
description += "couplingEquality[" + a + "] ";
solver.post(IntConstraintFactory.scalar(occ, sizes, VariableFactory.fixed(data.getTotalCover(a), solver)));
}
//**************************************************
// MONTHLY ACTIVITY COUNTER Constraints
//**************************************************
private void makeMonthlyCounters(Solver solver) {
String option = this.getConstraintOption("countM");
if (option.equals("occ")) {
this.makeMonthlyCountersWithOccurrence(solver);
} else {
this.makeMonthlyCountersWithGCC(solver);
}
}
private void makeMonthlyCountersWithGCC(Solver solver) {
description += "countM[gcc] ";
for (int e = 0; e < data.nbEmployees(); e++) {
int[] values = new int[occurrences[e].length];
for (int i = 0; i < values.length; i++) {
values[i] = i;
}
solver.post(IntConstraintFactory.global_cardinality(shifts[e], values, occurrences[e], false));
}
}
private void makeMonthlyCountersWithOccurrence(Solver solver) {
description += "countM[occ] ";
for (int a = 0; a < data.nbActivities(); a++) {
this.makeMonthlyCounterWithOccurrence(solver, a);
}
}
private void makeMonthlyCounterWithOccurrence(Solver solver, int a) {
for (int e = 0; e < data.nbEmployees(); e++) {
if (occurrences[e][a].getLB() > 0 || occurrences[e][a].getUB() < data.nbDays()) {
solver.post(IntConstraintFactory.count(a, shifts[e], occurrences[e][a]));
}
}
}
//**************************************************
// WEEKLY ACTIVITY COUNTER Constraints
//**************************************************
private void makeWeeklyCounters(Solver solver) {
String option = this.getConstraintOption("countW");
if (option.equals("occ")) {
this.makeWeeklyCountersWithOccurrence(solver);
} else {
this.makeWeeklyCountersWithGCC(solver);
}
}
private void makeWeeklyCountersWithOccurrence(Solver solver) {
description += "countW[occ] ";
for (int a = 0; a < data.nbActivities(); a++) {
this.makeWeeklyCountersWithOccurrence(solver, a);
}
}
private void makeWeeklyCountersWithOccurrence(Solver solver, int a) {
IntVar[] vars = new IntVar[7];
for (int e = 0; e < data.nbEmployees(); e++) {
int lb = data.getWeekCounterLB(e, a);
int ub = data.getWeekCounterUB(e, a);
if (lb > 0 || ub < 7) {
for (int t = 0; t < data.nbWeeks(); t++) {
// IntVar occ = ConstraintFactory.makeIntVar("nW" + t + data.getLiteral(a) + e, lb, ub, "cp:bound", Options.V_NO_DECISION);
IntVar occ = VariableFactory.bounded("nW" + t + data.getLiteral(a) + e, lb, ub, solver);
System.arraycopy(shifts[e], t * 7, vars, 0, 7);
solver.post(IntConstraintFactory.count(a, vars, occ));
}
}
}
}
private void makeWeeklyCountersWithGCC(Solver solver) {
description += "countW[gcc] ";
IntVar[] vars = new IntVar[7];
for (int e = 0; e < data.nbEmployees(); e++) {
IntVar[] cards = new IntVar[data.nbActivities()];
for (int a = 0; a < data.nbActivities(); a++) {
cards[a] = VariableFactory.bounded("card_" + a, data.getWeekCounterLB(e, a), data.getWeekCounterLB(e, a), solver);
}
for (int t = 0; t < data.nbWeeks(); t++) {
System.arraycopy(shifts[e], t * 7, vars, 0, 7);
int[] values = new int[cards.length];
for (int i = 0; i < values.length; i++) {
values[i] = i;
}
solver.post(IntConstraintFactory.global_cardinality(vars, values, cards, false));
}
}
}
//**************************************************
// MAXIMAL WORK SPAN Constraints
//**************************************************
/*private void makeMaxWorkSpan(Solver solver) {
this.makeMaxWorkSpanWithMax(solver);
}*/
/*// use the fact that value "REST" is greater than the value of any worked activity
private void makeMaxWorkSpanWithMax(Solver solver) {
description += "span[max] ";
int r = data.getValue("REST");
assert r >= data.nbActivities() - 1;
for (int e = 0; e < data.nbEmployees(); e++) {
int patternLength = data.getMaxWorkSpan(e) + 1;
IntVar restVar = ConstraintFactory.makeIntVar("Rcst", r, r, Options.V_NO_DECISION);
IntVar[] vars = new IntVar[patternLength];
for (int t = 0; t + patternLength < data.nbDays(); t++) {
System.arraycopy(shifts[e], t, vars, 0, patternLength);
solver.post(new MaxXYZ(vars, restVar));
}
}
}*/
//**************************************************
// FORBIDDEN PATTERNS Constraints
//**************************************************
private void makeForbiddenPatterns(Solver solver) {
String option = this.getConstraintOption("pat");
switch (option) {
case "reif":
this.makeForbiddenPatternsWithExtensionOrReified(solver);
break;
case "MCRegular":
this.makeForbiddenPatternsAndMonthlyCountersWithMultiCostRegular(solver);
break;
default:
this.makeForbiddenPatternsWithRegular(solver);
break;
}
}
private void makeForbiddenPatternsWithExtensionOrReified(Solver solver) {
this.makeCompleteWEWithLogic(solver);
//this.makeCompleteWEWithFeasTuple();
//this.makeCompleteWEWithInfeasTuple();
//this.makeForbidNightBeforeFreeWE(solver);
this.makeForceRestRestAfterNights(solver);
this.makeForbidTreeConsecutiveWEs(solver);
}
/*private void makeForbidNightBeforeFreeWE(Solver solver) {
description += "patNWE[extAC] ";
int n = data.getValue("NIGHT");
int r = data.getValue("REST");
List tuples = new ArrayList(1);
tuples.add(new int[]{n, r, r});
for (IntVar[] s : this.shifts) {
for (int t = 4; t + 2 < data.nbDays(); t += 7) {
solver.post(ConstraintFactory.infeasTupleAC(tuples, s[t], s[t + 1], s[t + 2]));
}
}
}*/
private void makeForceRestRestAfterNights(Solver solver) {
description += "patNRR[reif] ";
this.makeForceRestRestAfterNightsLogic(solver);
}
private void makeForceRestRestAfterNightsLogic(Solver solver) {
int n = data.getValue("NIGHT");
int r = data.getValue("REST");
for (IntVar[] s : this.shifts) {
for (int t = 0; t + 2 < data.nbDays(); t++) {
//solver.post(Options.E_DECOMP, ConstraintFactory.ifThenElse(ConstraintFactory.eq(s[t], n), ConstraintFactory.or(ConstraintFactory.eq(s[t+1], n), ConstraintFactory.and(ConstraintFactory.eq(s[t+1], r), ConstraintFactory.eq(s[t+2], r)))));
BoolVar[] bvars = VariableFactory.boolArray("b", 4, solver);
LogOp tree = LogOp.implies(
bvars[0],
LogOp.or(bvars[1], bvars[2], bvars[3])
);
SatFactory.addClauses(tree, solver);
LogicalConstraintFactory.ifThenElse(bvars[0],
IntConstraintFactory.arithm(s[t], "=", n), IntConstraintFactory.arithm(s[t], "!=", n));
LogicalConstraintFactory.ifThenElse(bvars[1],
IntConstraintFactory.arithm(s[t + 1], "=", n), IntConstraintFactory.arithm(s[t + 1], "!=", n));
LogicalConstraintFactory.ifThenElse(bvars[2],
IntConstraintFactory.arithm(s[t + 1], "=", r), IntConstraintFactory.arithm(s[t + 1], "!=", r));
LogicalConstraintFactory.ifThenElse(bvars[3],
IntConstraintFactory.arithm(s[t + 2], "=", r), IntConstraintFactory.arithm(s[t + 2], "!=", r));
}
int t = data.nbDays() - 2;
BoolVar[] bvars = VariableFactory.boolArray("b", 2, solver);
// solver.post(ConstraintFactory.ifThenElse(ConstraintFactory.eq(s[t], n), ConstraintFactory.eq(s[t + 1], n)));
LogOp tree = LogOp.implies(bvars[0], bvars[1]);
SatFactory.addClauses(tree, solver);
LogicalConstraintFactory.ifThenElse(bvars[0],
IntConstraintFactory.arithm(s[t], "=", n), IntConstraintFactory.arithm(s[t], "!=", n));
LogicalConstraintFactory.ifThenElse(bvars[1],
IntConstraintFactory.arithm(s[t + 1], "=", n), IntConstraintFactory.arithm(s[t + 1], "!=", n));
}
}
/*private void makeForceRestRestAfterNightsReifTuple(Solver solver) {
int n = data.getValue("NIGHT");
int r = data.getValue("REST");
List tuples = new ArrayList(3);
tuples.add(new int[]{n, r});
tuples.add(new int[]{n, n});
tuples.add(new int[]{r, r});
for (IntVar[] s : this.shifts) {
for (int t = 0; t + 2 < data.nbDays(); t++) {
solver.post(ConstraintFactory.ifThenElse(ConstraintFactory.eq(s[t], n), ConstraintFactory.feasPairAC(s[t + 1], s[t + 2], tuples)));
}
int t = data.nbDays() - 2;
solver.post(ConstraintFactory.ifThenElse(ConstraintFactory.eq(s[t], n), ConstraintFactory.eq(s[t + 1], n)));
}
}*/
private void makeForbidTreeConsecutiveWEs(Solver solver) {
description += "patConsWE[reif] ";
int r = data.getValue("REST");
for (IntVar[] s : this.shifts) {
for (int t = 5; t + 15 < data.nbDays(); t += 7) {
BoolVar[] bvars = VariableFactory.boolArray("b", 6, solver);
LogOp tree = LogOp.implies(
LogOp.and(
LogOp.or(bvars[0], bvars[1]),
LogOp.or(bvars[2], bvars[3])),
LogOp.and(bvars[4], bvars[5])
);
SatFactory.addClauses(tree, solver);
LogicalConstraintFactory.ifThenElse(bvars[0],
IntConstraintFactory.arithm(s[t], "=", r),
IntConstraintFactory.arithm(s[t], "!=", r));
LogicalConstraintFactory.ifThenElse(bvars[1],
IntConstraintFactory.arithm(s[t + 1], "=", r),
IntConstraintFactory.arithm(s[t + 1], "!=", r));
LogicalConstraintFactory.ifThenElse(bvars[2],
IntConstraintFactory.arithm(s[t + 7], "=", r), IntConstraintFactory.arithm(s[t + 7], "!=", r));
LogicalConstraintFactory.ifThenElse(bvars[3],
IntConstraintFactory.arithm(s[t + 8], "=", r), IntConstraintFactory.arithm(s[t + 8], "!=", r));
LogicalConstraintFactory.ifThenElse(bvars[4],
IntConstraintFactory.arithm(s[t + 14], "=", r), IntConstraintFactory.arithm(s[t + 14], "!=", r));
LogicalConstraintFactory.ifThenElse(bvars[5],
IntConstraintFactory.arithm(s[t + 15], "=", r), IntConstraintFactory.arithm(s[t + 15], "!=", r));
/*solver.post(ConstraintFactory.ifThenElse(
ConstraintFactory.and(
ConstraintFactory.or(ConstraintFactory.neq(s[t], r),
ConstraintFactory.neq(s[t + 1], r)),
ConstraintFactory.or(ConstraintFactory.neq(s[t + 7], r),
ConstraintFactory.neq(s[t + 8], r))),
ConstraintFactory.and(ConstraintFactory.eq(s[t + 14], r), ConstraintFactory.eq(s[t + 15], r))));*/
}
}
}
//**************************************************
// COMPLETE WEEK-END Constraints
//**************************************************
/*private void makeCompleteWEWithFeasTuple(Solver solver) {
description += "patCompWE[feasAC] ";
int r = data.getValue("REST");
List tuples = new ArrayList();
tuples.add(new int[]{r, r});
for (int w1 = 0; w1 < r; w1++) {
tuples.add(new int[]{w1, w1});
for (int w2 = w1 + 1; w2 < r; w2++) {
tuples.add(new int[]{w1, w2});
tuples.add(new int[]{w2, w1});
}
}
for (IntVar[] s : this.shifts) {
for (int t = 5; t + 1 < data.nbDays(); t += 7) {
solver.post(ConstraintFactory.feasPairAC(s[t], s[t + 1], tuples));
}
}
}*/
/*private void makeCompleteWEWithInfeasTuple(Solver solver) {
description += "patCompWE[infeasAC] ";
int r = data.getValue("REST");
List tuples = new ArrayList();
for (int w = 0; w < r; w++) {
tuples.add(new int[]{w, r});
tuples.add(new int[]{r, w});
}
for (IntVar[] s : this.shifts) {
for (int t = 5; t + 1 < data.nbDays(); t += 7) {
solver.post(ConstraintFactory.infeasPairAC(s[t], s[t + 1], tuples));
}
}
}*/
private void makeCompleteWEWithLogic(Solver solver) {
description += "patCompWE[reif] ";
int r = data.getValue("REST");
for (IntVar[] s : this.shifts) {
for (int t = 5; t + 1 < data.nbDays(); t += 7) {
BoolVar[] bvars = VariableFactory.boolArray("b", 2, solver);
LogOp tree = LogOp.ifOnlyIf(bvars[0], bvars[1]);
SatFactory.addClauses(tree, solver);
LogicalConstraintFactory.ifThenElse(bvars[0],
IntConstraintFactory.arithm(s[t], "=", r),
IntConstraintFactory.arithm(s[t], "!=", r));
LogicalConstraintFactory.ifThenElse(bvars[1],
IntConstraintFactory.arithm(s[t + 1], "=", r),
IntConstraintFactory.arithm(s[t + 1], "!=", r));
//solver.post(ConstraintFactory.ifOnlyIf(ConstraintFactory.eq(s[t], r), ConstraintFactory.eq(s[t + 1], r)));
}
}
}
//**************************************************
// AUTOMATON Constraints
//**************************************************
private FiniteAutomaton makeForbiddenPatternsAsAutomaton() {
FiniteAutomaton automaton = new FiniteAutomaton();
for (String reg : data.forbiddenRegExps()) {
FiniteAutomaton a = new FiniteAutomaton(reg);
automaton = automaton.union(a);
automaton.minimize();
}
for (int a = 0; a < data.nbActivities(); a++) {
automaton.addToAlphabet(a);
}
automaton = automaton.complement();
automaton.minimize();
return automaton;
}
private void makeForbiddenPatternsWithRegular(Solver solver) {
FiniteAutomaton automaton = this.makeForbiddenPatternsAsAutomaton();
description += "pat[regular/" + automaton.getNbStates() + "] ";
for (int e = 0; e < data.nbEmployees(); e++) {
solver.post(IntConstraintFactory.regular(shifts[e], automaton));
}
}
private void makeForbiddenPatternsAndMonthlyCountersWithMultiCostRegular(Solver solver) {
FiniteAutomaton automaton = this.makeForbiddenPatternsAsAutomaton();
int[][][] costs = new int[data.nbDays()][data.nbActivities()][data.nbActivities()];
for (int a = 0; a < data.nbActivities(); a++) {
for (int t = 0; t < data.nbDays(); t++) {
costs[t][a][a] = 1;
}
}
description += "pat[MCRegular/" + automaton.getNbStates() + "/" + costs[0][0].length + "] ";
for (int e = 0; e < data.nbEmployees(); e++) {
solver.post(IntConstraintFactory.multicost_regular(shifts[e], occurrences[e], CostAutomaton.makeMultiResources(automaton, costs, occurrences[e])));
}
}
}