diff --git a/lib/cpsolver-all-1.3.jar b/lib/cpsolver-all-1.3.jar index c087139..dc59a5d 100644 Binary files a/lib/cpsolver-all-1.3.jar and b/lib/cpsolver-all-1.3.jar differ diff --git a/src/ctt.properties b/src/ctt.properties index de0b143..bc445f5 100644 --- a/src/ctt.properties +++ b/src/ctt.properties @@ -42,4 +42,7 @@ SimulatedAnnealing.ReheatLengthCoef=7 Extensions.Classes=org.cpsolver.ifs.extension.ConflictStatistics #Other -General.SaveBestUnassigned=0 \ No newline at end of file +General.SaveBestUnassigned=0 + +ParallelSolver.SingleSolution=false +ParallelSolver.SingleSolutionNeighbourCheck=true \ No newline at end of file diff --git a/src/exam.properties b/src/exam.properties index 96aa8bb..4dbb19b 100644 --- a/src/exam.properties +++ b/src/exam.properties @@ -35,4 +35,7 @@ General.SaveBestUnassigned=0 #Extensions Exam.AllowDirectConflict=false Exam.AllowBinaryViolations=false -Extensions.Classes=org.cpsolver.ifs.extension.ConflictStatistics \ No newline at end of file +Extensions.Classes=org.cpsolver.ifs.extension.ConflictStatistics + +ParallelSolver.SingleSolution=false +ParallelSolver.SingleSolutionNeighbourCheck=true \ No newline at end of file diff --git a/src/net/sf/cpsolver/itc/ItcModel.java b/src/net/sf/cpsolver/itc/ItcModel.java index b58c8e2..45ad107 100644 --- a/src/net/sf/cpsolver/itc/ItcModel.java +++ b/src/net/sf/cpsolver/itc/ItcModel.java @@ -54,4 +54,9 @@ public boolean cvsPrint(Assignment assignment) { public void makeFeasible(Assignment assignment) {} public abstract Assignment createAssignment(int index, Assignment assignment); + + @Override + public String toString(Assignment a) { + return "V:"+a.nrAssignedVariables()+"/"+variables().size()+", P:"+Math.round(getTotalValue(a))+" ("+csvLine(a)+")"; + } } diff --git a/src/net/sf/cpsolver/itc/ItcTest.java b/src/net/sf/cpsolver/itc/ItcTest.java index 7130faf..55badbf 100644 --- a/src/net/sf/cpsolver/itc/ItcTest.java +++ b/src/net/sf/cpsolver/itc/ItcTest.java @@ -4,6 +4,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; import java.text.DecimalFormat; import java.util.Collection; import java.util.List; @@ -250,24 +252,27 @@ protected Solution createParallelSolution(int index) { solution.addSolutionListener(listener); return solution; } + @Override + public boolean hasSingleSolution() { + return sConfig.getPropertyBoolean("ParallelSolver.SingleSolution", false); + } }; Solution solution = new Solution(model, model.createAssignment(0, null)); solver.setInitalSolution(solution); + solution.getModel().createAssignmentContexts(solution.getAssignment(), false); solver.currentSolution().addSolutionListener(new SolutionListener() { - public void solutionUpdated(Solution solution) {} + public void solutionUpdated(Solution solution) { + //sLog.info("**CURRENT["+solution.getIteration()+"]** " + solution); + } public void getInfo(Solution solution, Map info) {} public void getInfo(Solution solution, Map info, Collection variables) {} public void bestCleared(Solution solution) {} public void bestSaved(Solution solution) { - ItcModel m = (ItcModel)solution.getModel(); - Assignment a = solution.getAssignment(); - sLog.info("**BEST["+solution.getIteration()+"]** V:"+a.nrAssignedVariables()+"/"+m.variables().size()+", P:"+Math.round(m.getTotalValue(a))+" ("+m.csvLine(a)+")"); + sLog.info("**BEST["+solution.getIteration()+"]** " + solution); } public void bestRestored(Solution solution) { - ItcModel m = (ItcModel)solution.getModel(); - Assignment a = solution.getAssignment(); - sLog.info("##RESTORED["+solution.getIteration()+"]## V:"+a.nrAssignedVariables()+"/"+m.variables().size()+", P:"+Math.round(m.getTotalValue(a))+" ("+m.csvLine(a)+")"); + sLog.info("##RESTORED["+solution.getIteration()+"]## " + solution); } }); @@ -287,7 +292,7 @@ public static Solution solve() { Solution solution = solver.currentSolution(); solution.restoreBest(); ((ItcModel)solution.getModel()).makeFeasible(solution.getAssignment()); - solution.saveBest(); + // solution.saveBest(); return output(solver); } catch (Exception e) { @@ -305,7 +310,7 @@ private static Solution output(Solver solver) throws Exception { sLog.error("No best solution found."); return null; } - solution.restoreBest(); + // solution.restoreBest(); sLog.info("Best solution:"+ToolBox.dict2string(solution.getExtendedInfo(),1)); @@ -325,6 +330,11 @@ private static Solution output(Solver solver) throws Exception { ItcModel m = (ItcModel)solution.getModel(); Assignment a = solution.getAssignment(); DecimalFormat df = new DecimalFormat("0.00"); + double cpu = 0.0; + OperatingSystemMXBean bean = ManagementFactory.getOperatingSystemMXBean(); + if (bean instanceof com.sun.management.OperatingSystemMXBean) { + cpu = ((com.sun.management.OperatingSystemMXBean)bean).getProcessCpuTime() / 1e9; + } w.println( sSeed+","+ sTimeOut+","+ @@ -332,7 +342,10 @@ private static Solution output(Solver solver) throws Exception { solution.getBestIteration()+","+ Math.round(m.getTotalValue(solution.getAssignment()) + 5000 * a.nrUnassignedVariables(m))+","+ model.csvLine(a) + "," + - model.getProperties().getPropertyInt("Parallel.NrSolvers", 4)); + model.getProperties().getPropertyInt("Parallel.NrSolvers", 4) + + (solver.hasSingleSolution() ? "s": "p") + + (solver.hasSingleSolution() && model.getProperties().getPropertyBoolean("ParallelSolver.SingleSolutionQueue", true) ? "q" : "") + "," + + df.format(solution.getIteration() / solution.getTime()) + "," + df.format(cpu)); w.flush(); w.close(); } @@ -353,7 +366,7 @@ public void run() { Solution solution = iSolver.currentSolution(); solution.restoreBest(); ((ItcModel)solution.getModel()).makeFeasible(solution.getAssignment()); - solution.saveBest(); + // solution.saveBest(); output(iSolver); } catch (Exception e) { diff --git a/src/net/sf/cpsolver/itc/ctt/model/CttAssignment.java b/src/net/sf/cpsolver/itc/ctt/model/CttAssignment.java index 9661ca6..729320a 100644 --- a/src/net/sf/cpsolver/itc/ctt/model/CttAssignment.java +++ b/src/net/sf/cpsolver/itc/ctt/model/CttAssignment.java @@ -7,6 +7,7 @@ import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.AssignmentAbstract; +import org.cpsolver.ifs.assignment.context.AssignmentContextHolder; import org.cpsolver.ifs.assignment.context.DefaultParallelAssignmentContextHolder; import org.cpsolver.ifs.model.Model; @@ -34,17 +35,28 @@ * http://www.gnu.org/licenses/. */ public class CttAssignment extends AssignmentAbstract { - private CttModel iModel; - private int iIndex; - private int iNrAssigned = 0; + protected CttModel iModel; + protected int iIndex; + protected int iNrAssigned = 0; + protected CttPlacement[] iAssignment = null; + protected CttAssignment iParent = null; public CttAssignment(CttModel model, int index, Assignment assignment) { super(new DefaultParallelAssignmentContextHolder(index)); iIndex = index; iModel = model; - if (assignment != null) - for (CttPlacement value: assignment.assignedValues()) - setValueInternal(0, value.variable(), value); + iParent = (CttAssignment) assignment; + if (iParent == null) { + iAssignment = new CttPlacement[model.variables().size()]; + iNrAssigned = 0; + } else { + iAssignment = Arrays.copyOf(((CttAssignment)iParent).toArray(), model.variables().size()); + iNrAssigned = iParent.nrAssignedVariables(); + } + } + + protected CttAssignment(AssignmentContextHolder contextHolder) { + super(contextHolder); } @Override @@ -97,35 +109,16 @@ public Collection assignedVariables() { return assigned; } - protected CttPlacement[] getAssignments(CttLecture variable) { - synchronized (variable) { - CttPlacement[] assignments = (CttPlacement[])variable.getExtra(); - if (assignments == null) { - assignments = (CttPlacement[])new CttPlacement[Math.max(10, 1 + iIndex)]; - variable.setExtra(assignments); - } else if (assignments.length <= iIndex) { - assignments = Arrays.copyOf(assignments, 10 + iIndex); - variable.setExtra(assignments); - } - return assignments; - } - } - @Override - @SuppressWarnings("deprecation") protected CttPlacement getValueInternal(CttLecture variable) { - if (iIndex == 1) - return variable.getAssignment(); - return getAssignments(variable)[iIndex]; + // return (CttPlacement) variable.getAssignments()[iIndex]; + return iAssignment[variable.getIndex()]; } @Override - @SuppressWarnings("deprecation") protected void setValueInternal(long iteration, CttLecture variable, CttPlacement value) { - if (iIndex == 1) - variable.setAssignment(value); - else - getAssignments(variable)[iIndex] = value; + //variable.getAssignments()[iIndex] = value; + iAssignment[variable.getIndex()] = value; if (value == null) iNrAssigned --; else @@ -141,4 +134,12 @@ public int getIndex() { public int nrAssignedVariables() { return iNrAssigned; } + + public CttPlacement[] toArray() { + return iAssignment; + } + + public Assignment getParentAssignment() { + return iParent; + } } diff --git a/src/net/sf/cpsolver/itc/ctt/model/CttCurricula.java b/src/net/sf/cpsolver/itc/ctt/model/CttCurricula.java index 891f69f..9ac3d32 100644 --- a/src/net/sf/cpsolver/itc/ctt/model/CttCurricula.java +++ b/src/net/sf/cpsolver/itc/ctt/model/CttCurricula.java @@ -1,12 +1,14 @@ package net.sf.cpsolver.itc.ctt.model; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.assignment.context.ConstraintWithContext; import org.cpsolver.ifs.model.ConstraintListener; @@ -148,7 +150,7 @@ public int getCompactPenalty(Assignment assignment, Ct * same curricula are placed at the same time. * */ - public class CurriculaConstraint extends ConstraintWithContext { + public class CurriculaConstraint extends ConstraintWithContext implements CanInheritContext { /** Constructor */ public CurriculaConstraint() { @@ -212,9 +214,14 @@ public int hashCode() { public Table createAssignmentContext(Assignment assignment) { return new Table(assignment); } + + @Override + public Table inheritAssignmentContext(Assignment assignment, Table parentContext) { + return new Table(assignment, parentContext); + } } - private class Table implements AssignmentConstraintContext { + public class Table implements AssignmentConstraintContext { public CttPlacement[] iPlacement; public Table(Assignment assignment) { @@ -226,6 +233,10 @@ public Table(Assignment assignment) { if (p != null) assigned(assignment, p); } } + + public Table(Assignment assignment, Table parent) { + iPlacement = Arrays.copyOf(parent.iPlacement, parent.iPlacement.length); + } @Override public void assigned(Assignment assignment, CttPlacement p) { diff --git a/src/net/sf/cpsolver/itc/ctt/model/CttInheritedAssignment.java b/src/net/sf/cpsolver/itc/ctt/model/CttInheritedAssignment.java new file mode 100644 index 0000000..03cec4c --- /dev/null +++ b/src/net/sf/cpsolver/itc/ctt/model/CttInheritedAssignment.java @@ -0,0 +1,56 @@ +package net.sf.cpsolver.itc.ctt.model; + +import java.util.Arrays; +import java.util.concurrent.locks.Lock; + +import org.cpsolver.ifs.assignment.InheritedAssignment; +import org.cpsolver.ifs.assignment.context.InheritedAssignmentContextHolder; +import org.cpsolver.ifs.solution.Solution; + +/** + * Curriculum based Course Timetabling (CTT) inherited assignment model. + * + * @version + * ITC2007 1.0
+ * Copyright (C) 2014 Tomas Muller
+ * muller@unitime.org
+ * http://muller.unitime.org
+ *
+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not see + * http://www.gnu.org/licenses/. + */ +public class CttInheritedAssignment extends CttAssignment implements InheritedAssignment { + private long iVersion = -1; + + public CttInheritedAssignment(Solution solution, int index) { + super(new InheritedAssignmentContextHolder(index, solution.getIteration())); + iIndex = index; + iVersion = solution.getIteration(); + iModel = (CttModel) solution.getModel(); + iParent = (CttAssignment) solution.getAssignment(); + Lock lock = solution.getLock().readLock(); + lock.lock(); + try { + iNrAssigned = iParent.nrAssignedVariables(); + iAssignment = Arrays.copyOf(((CttAssignment)iParent).toArray(), iModel.variables().size()); + } finally { + lock.unlock(); + } + } + + @Override + public long getVersion() { + return iVersion; + } +} diff --git a/src/net/sf/cpsolver/itc/ctt/model/CttLecture.java b/src/net/sf/cpsolver/itc/ctt/model/CttLecture.java index cf95b81..81fee96 100644 --- a/src/net/sf/cpsolver/itc/ctt/model/CttLecture.java +++ b/src/net/sf/cpsolver/itc/ctt/model/CttLecture.java @@ -4,9 +4,10 @@ import java.util.List; import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.model.LazySwap; import org.cpsolver.ifs.model.Neighbour; import org.cpsolver.ifs.model.Variable; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazySwap; + import net.sf.cpsolver.itc.heuristics.neighbour.ItcSwap.Swapable; /** @@ -37,6 +38,7 @@ public class CttLecture extends Variable implements Swapable { private int iIdx; private CttCourse iCourse = null; + private int iHashCode; /** Constructor * @param course course to which this lecture belongs @@ -54,6 +56,7 @@ public CttLecture(CttCourse course, int idx) { curricula.getConstraint().addVariable(this); setValues(computeValues()); getModel().addVariable(this); + iHashCode = toString().hashCode(); } /** Return course to which this lecture belong */ @@ -102,7 +105,7 @@ public boolean equals(Object o) { /** Hash code */ public int hashCode() { - return toString().hashCode(); + return iHashCode; } /** Compare two lectures, return the harder one. That is the one with more curriculas, or smaller domain/constraint ratio. */ @@ -142,7 +145,7 @@ public Neighbour findSwap(Assignment(assignment, np1, np2); + return new LazySwap(np1, np2); /* double value = 0; //value += np1.extraPenalty() + np2.extraPenalty() - p1.extraPenalty() - p2.extraPenalty(); diff --git a/src/net/sf/cpsolver/itc/ctt/model/CttModel.java b/src/net/sf/cpsolver/itc/ctt/model/CttModel.java index 9ff6f7b..2b6f4f6 100644 --- a/src/net/sf/cpsolver/itc/ctt/model/CttModel.java +++ b/src/net/sf/cpsolver/itc/ctt/model/CttModel.java @@ -11,9 +11,12 @@ import java.util.StringTokenizer; import org.apache.log4j.Logger; - import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.assignment.InheritedAssignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; +import org.cpsolver.ifs.solution.Solution; + import net.sf.cpsolver.itc.ItcModel; /** @@ -39,7 +42,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class CttModel extends ItcModel { +public class CttModel extends ItcModel implements CanInheritContext> { private static Logger sLog = Logger.getLogger(CttModel.class); private String iName = null; private int iNrDays = 0; @@ -345,6 +348,12 @@ public AssignmentConstraintContext createAssignmentCon return new Penalties(assignment); } + @Override + public AssignmentConstraintContext inheritAssignmentContext(Assignment assignment, + AssignmentConstraintContext parentContext) { + return new Penalties(assignment, (Penalties)parentContext); + } + /** * Update penalty counters when a variable is unassigned */ @@ -361,7 +370,6 @@ public void beforeAssigned(Assignment assignment, long getPenalties(assignment).beforeAssigned(assignment, placement); } - private class Penalties implements AssignmentConstraintContext { private int iCompactPenalty = 0; private int iRoomPenalty = 0; @@ -375,6 +383,13 @@ public Penalties(Assignment assignment) { iRoomCapPenalty = CttModel.this.getRoomCapPenalty(assignment, true); } + public Penalties(Assignment assignment, Penalties parent) { + iCompactPenalty = parent.getCompactPenalty(); + iRoomPenalty = parent.getRoomPenalty(); + iMinDaysPenalty = parent.getMinDaysPenalty(); + iRoomCapPenalty = parent.getRoomCapPenalty(); + } + @Override public void assigned(Assignment assignment, CttPlacement placement) {} @@ -408,9 +423,14 @@ public void afterUnassigned(Assignment assignment, Ctt public int getRoomCapPenalty() { return iRoomCapPenalty; } } - @Override public Assignment createAssignment(int index, Assignment assignment) { return new CttAssignment(this, index, assignment); } + + @Override + public InheritedAssignment createInheritedAssignment(Solution solution, int index) { + return new CttInheritedAssignment(solution, index); + } + } diff --git a/src/net/sf/cpsolver/itc/ctt/model/CttRoom.java b/src/net/sf/cpsolver/itc/ctt/model/CttRoom.java index 4387220..56e8e79 100644 --- a/src/net/sf/cpsolver/itc/ctt/model/CttRoom.java +++ b/src/net/sf/cpsolver/itc/ctt/model/CttRoom.java @@ -1,10 +1,12 @@ package net.sf.cpsolver.itc.ctt.model; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.assignment.context.ConstraintWithContext; import org.cpsolver.ifs.model.ConstraintListener; @@ -91,7 +93,7 @@ public RoomConstraint getConstraint() { /** Room constraint. This hard constraint ensures that no two lectures * are placed into this room at the same time. */ - public class RoomConstraint extends ConstraintWithContext { + public class RoomConstraint extends ConstraintWithContext implements CanInheritContext { /** Constructor */ public RoomConstraint() { @@ -173,6 +175,11 @@ public CttPlacement getPlacement(Assignment assignment public Table createAssignmentContext(Assignment assignment) { return new Table(assignment); } + + @Override + public Table inheritAssignmentContext(Assignment assignment, Table parentContext) { + return new Table(assignment, parentContext); + } } private class Table implements AssignmentConstraintContext { @@ -186,6 +193,10 @@ public Table(Assignment assignment) { if (p.getRoom().equals(CttRoom.this)) assigned(assignment, p); } + + public Table(Assignment assignment, Table parent) { + iPlacement = Arrays.copyOf(parent.iPlacement, parent.iPlacement.length); + } @Override public void assigned(Assignment assignment, CttPlacement p) { diff --git a/src/net/sf/cpsolver/itc/ctt/model/CttTeacher.java b/src/net/sf/cpsolver/itc/ctt/model/CttTeacher.java index 88af8cc..38c72d9 100644 --- a/src/net/sf/cpsolver/itc/ctt/model/CttTeacher.java +++ b/src/net/sf/cpsolver/itc/ctt/model/CttTeacher.java @@ -1,10 +1,12 @@ package net.sf.cpsolver.itc.ctt.model; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.assignment.context.ConstraintWithContext; import org.cpsolver.ifs.model.ConstraintListener; @@ -85,7 +87,7 @@ public TeacherConstraint getConstraint() { * lectures that are taught by the same teacher are not placed * at the same day and time. */ - public class TeacherConstraint extends ConstraintWithContext { + public class TeacherConstraint extends ConstraintWithContext implements CanInheritContext { /** Constructor */ public TeacherConstraint() { @@ -164,8 +166,13 @@ public int hashCode() { public Table createAssignmentContext(Assignment assignment) { return new Table(assignment); } - } + @Override + public Table inheritAssignmentContext(Assignment assignment, Table parentContext) { + return new Table(assignment, parentContext); + } + } + private class Table implements AssignmentConstraintContext { private CttPlacement[] iPlacement; @@ -178,6 +185,10 @@ public Table(Assignment assignment) { if (p != null) assigned(assignment, p); } } + + public Table(Assignment assignment, Table parent) { + iPlacement = Arrays.copyOf(parent.iPlacement, parent.iPlacement.length); + } @Override public void assigned(Assignment assignment, CttPlacement p) { diff --git a/src/net/sf/cpsolver/itc/ctt/neighbours/CttCurriculumCompactnessMove.java b/src/net/sf/cpsolver/itc/ctt/neighbours/CttCurriculumCompactnessMove.java index 6818dd8..42fac9b 100644 --- a/src/net/sf/cpsolver/itc/ctt/neighbours/CttCurriculumCompactnessMove.java +++ b/src/net/sf/cpsolver/itc/ctt/neighbours/CttCurriculumCompactnessMove.java @@ -63,13 +63,14 @@ public Neighbour selectNeighbour(Solution adepts = new ArrayList(); for (int d=0;d selectNeighbour(Solution(assignment, + return new LazySwap( new CttPlacement(lecture, room, day, slot), new CttPlacement(confLect, placement.getRoom(), placement.getDay(), placement.getSlot())); } diff --git a/src/net/sf/cpsolver/itc/exam/model/ExAssignment.java b/src/net/sf/cpsolver/itc/exam/model/ExAssignment.java index d6c2db7..fb562d6 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExAssignment.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExAssignment.java @@ -7,6 +7,7 @@ import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.AssignmentAbstract; +import org.cpsolver.ifs.assignment.context.AssignmentContextHolder; import org.cpsolver.ifs.assignment.context.DefaultParallelAssignmentContextHolder; import org.cpsolver.ifs.model.BinaryConstraint; import org.cpsolver.ifs.model.Model; @@ -35,17 +36,28 @@ * http://www.gnu.org/licenses/. */ public class ExAssignment extends AssignmentAbstract { - private ExModel iModel; - private int iIndex; - private int iNrAssigned = 0; + protected int iIndex; + protected int iNrAssigned = 0; + protected ExModel iModel; + protected ExPlacement[] iAssignment = null; + protected ExAssignment iParent = null; public ExAssignment(ExModel model, int index, Assignment assignment) { super(new DefaultParallelAssignmentContextHolder(index)); iIndex = index; iModel = model; - if (assignment != null) - for (ExPlacement value: assignment.assignedValues()) - setValueInternal(0, value.variable(), value); + iParent = (ExAssignment) assignment; + if (iParent == null) { + iAssignment = new ExPlacement[model.variables().size()]; + iNrAssigned = 0; + } else { + iAssignment = Arrays.copyOf(((ExAssignment)iParent).toArray(), model.variables().size()); + iNrAssigned = iParent.nrAssignedVariables(); + } + } + + protected ExAssignment(AssignmentContextHolder contextHolder) { + super(contextHolder); } @Override @@ -119,20 +131,24 @@ protected ExPlacement[] getAssignments(ExExam variable) { } @Override - @SuppressWarnings("deprecation") protected ExPlacement getValueInternal(ExExam variable) { + return iAssignment[variable.getIndex()]; + /* if (iIndex == 1) return variable.getAssignment(); return getAssignments(variable)[iIndex]; + */ } @Override - @SuppressWarnings("deprecation") protected void setValueInternal(long iteration, ExExam variable, ExPlacement value) { + iAssignment[variable.getIndex()] = value; + /* if (iIndex == 1) variable.setAssignment(value); else getAssignments(variable)[iIndex] = value; + */ if (value == null) iNrAssigned --; else @@ -148,4 +164,12 @@ public int getIndex() { public int nrAssignedVariables() { return iNrAssigned; } + + public ExPlacement[] toArray() { + return iAssignment; + } + + public Assignment getParentAssignment() { + return iParent; + } } diff --git a/src/net/sf/cpsolver/itc/exam/model/ExExam.java b/src/net/sf/cpsolver/itc/exam/model/ExExam.java index 716baea..2a8b086 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExExam.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExExam.java @@ -8,14 +8,14 @@ import java.util.Set; import org.apache.log4j.Logger; - import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.model.BinaryConstraint; import org.cpsolver.ifs.model.Constraint; +import org.cpsolver.ifs.model.LazySwap; import org.cpsolver.ifs.model.Neighbour; import org.cpsolver.ifs.model.Variable; import org.cpsolver.ifs.util.ToolBox; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazySwap; + import net.sf.cpsolver.itc.heuristics.neighbour.ItcSwap.Swapable; /** @@ -225,7 +225,7 @@ public Neighbour findSwap(Assignment a if (constraint.inConflict(assignment, np2)) return null; } } - return new ItcLazySwap(assignment, np1, np2); + return new LazySwap(np1, np2); } ExRoom r1 = p1.getRoom(); ExRoom r2 = p2.getRoom(); @@ -235,10 +235,10 @@ public Neighbour findSwap(Assignment a if (ex2.isRoomExclusive() && r1.getExams(assignment, p1.getPeriod()).size()>1) return null; if (!model.areDirectConflictsAllowed() && p1.getPeriodIndex()!=p2.getPeriodIndex()) { for (ExStudent s: ex1.getStudents()) { - if (s.hasExam(assignment, p2.getPeriod(),ex2)) return null; + if (s.getContext(assignment).hasExam(p2.getPeriodIndex(), ex2)) return null; } for (ExStudent s: ex2.getStudents()) { - if (s.hasExam(assignment, p1.getPeriod(),ex1)) return null; + if (s.getContext(assignment).hasExam(p1.getPeriodIndex(), ex1)) return null; } } ExPlacement np1 = new ExPlacement(ex1, p2.getPeriod(), p2.getRoom()); @@ -253,7 +253,7 @@ public Neighbour findSwap(Assignment a if (constraint.inConflict(assignment, np2)) return null; } } - return new ItcLazySwap(assignment, np1, np2); + return new LazySwap(np1, np2); } /** Compare two exams for equality */ diff --git a/src/net/sf/cpsolver/itc/exam/model/ExInheritedAssignment.java b/src/net/sf/cpsolver/itc/exam/model/ExInheritedAssignment.java new file mode 100644 index 0000000..f0a3612 --- /dev/null +++ b/src/net/sf/cpsolver/itc/exam/model/ExInheritedAssignment.java @@ -0,0 +1,57 @@ +package net.sf.cpsolver.itc.exam.model; + +import java.util.Arrays; +import java.util.concurrent.locks.Lock; + +import org.cpsolver.ifs.assignment.InheritedAssignment; +import org.cpsolver.ifs.assignment.context.InheritedAssignmentContextHolder; +import org.cpsolver.ifs.solution.Solution; + +/** + * Examination Timetabling (exam) inherited assignment model. + * + * @version + * ITC2007 1.0
+ * Copyright (C) 2014 Tomas Muller
+ * muller@unitime.org
+ * http://muller.unitime.org
+ *
+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not see + * http://www.gnu.org/licenses/. + */ +public class ExInheritedAssignment extends ExAssignment implements InheritedAssignment { + private long iVersion = -1; + + public ExInheritedAssignment(Solution solution, int index) { + super(new InheritedAssignmentContextHolder(index, solution.getIteration())); + iIndex = index; + iVersion = solution.getIteration(); + iModel = (ExModel) solution.getModel(); + iParent = (ExAssignment) solution.getAssignment(); + Lock lock = solution.getLock().readLock(); + lock.lock(); + try { + iNrAssigned = iParent.nrAssignedVariables(); + iAssignment = Arrays.copyOf(((ExAssignment)iParent).toArray(), iModel.variables().size()); + } finally { + lock.unlock(); + } + } + + @Override + public long getVersion() { + return iVersion; + } + +} diff --git a/src/net/sf/cpsolver/itc/exam/model/ExModel.java b/src/net/sf/cpsolver/itc/exam/model/ExModel.java index 23cfb06..03edce5 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExModel.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExModel.java @@ -18,11 +18,14 @@ import java.util.TreeSet; import org.apache.log4j.Logger; - import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.assignment.InheritedAssignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.model.BinaryConstraint; import org.cpsolver.ifs.model.Constraint; +import org.cpsolver.ifs.solution.Solution; + import net.sf.cpsolver.itc.ItcModel; /** @@ -48,7 +51,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class ExModel extends ItcModel { +public class ExModel extends ItcModel implements CanInheritContext> { private static Logger sLog = Logger.getLogger(ExModel.class); private List iPeriods = new ArrayList(); private List iRooms = new ArrayList(); @@ -666,7 +669,7 @@ public int compare(ExExam e1, ExExam e2) { int twoInRowThisExam = 0, twoInDayThisExam = 0, widerSpreadThisExam = 0; for (ExStudent s: exam.getStudents()) { for (ExPeriod p=firstPeriod(); p!=null; p=p.next()) { - if (!s.hasExam(assignment, p)) continue; + if (!s.getContext(assignment).hasExam(p.getIndex())) continue; if (p.getIndex()<=placement.getPeriod().getIndex()) continue; int dist = Math.abs(p.getIndex()-placement.getPeriodIndex()); if (p.getDay()==placement.getPeriod().getDay()) { @@ -769,6 +772,18 @@ public Context(Assignment assignment) { for (ExPlacement value: assignment.assignedValues()) assigned(assignment, value); } + + public Context(Assignment assignment, Context parent) { + iFronLoadPenalty = parent.iFronLoadPenalty; + iPeriodPenalty = parent.iPeriodPenalty; + iRoomPenalty = parent.iRoomPenalty; + iTwoInARowPenalty = parent.iTwoInARowPenalty; + iTwoInADayPenalty = parent.iTwoInADayPenalty; + iWiderSpreadPenalty = parent.iWiderSpreadPenalty; + iMixedDurationsPenalty = parent.iMixedDurationsPenalty; + iBinaryViolations = parent.iBinaryViolations; + iNrDirectConflicts = parent.iNrDirectConflicts; + } @Override public void assigned(Assignment assignment, ExPlacement placement) { @@ -820,4 +835,21 @@ public Context getPenalties(Assignment assignment) { public Assignment createAssignment(int index, Assignment assignment) { return new ExAssignment(this, index, assignment); } + + @Override + public InheritedAssignment createInheritedAssignment(Solution solution, int index) { + return new ExInheritedAssignment(solution, index); + } + + @Override + public AssignmentConstraintContext inheritAssignmentContext(Assignment assignment, + AssignmentConstraintContext parentContext) { + return new Context(assignment, (Context)parentContext); + } + + @Override + public boolean inConflict(Assignment assignment, ExPlacement value) { + if (value.getRoom().inConflict(assignment, value)) return true; + return super.inConflict(assignment, value); + } } diff --git a/src/net/sf/cpsolver/itc/exam/model/ExPlacement.java b/src/net/sf/cpsolver/itc/exam/model/ExPlacement.java index 9237153..ff6a920 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExPlacement.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExPlacement.java @@ -90,9 +90,10 @@ public int twoInARowPenalty(Assignment assignment) { if (!prev && !next) return 0; int penalty = 0; for (ExStudent student: variable().getStudents()) { - if (m.areDirectConflictsAllowed() && student.nrExams(assignment, getPeriod(), exam)>0) continue; - if (next && student.hasExam(assignment, nextPeriod,exam)) penalty++; - if (prev && student.hasExam(assignment, prevPeriod,exam)) penalty++; + ExStudent.Context cx = student.getContext(assignment); + if (m.areDirectConflictsAllowed() && cx.nrExams(getPeriod().getIndex(), exam)>0) continue; + if (next && cx.hasExam(nextPeriod.getIndex(), exam)) penalty++; + if (prev && cx.hasExam(prevPeriod.getIndex(), exam)) penalty++; } return penalty; } @@ -103,8 +104,9 @@ public int nrDirectConflicts(Assignment assignment) { ExModel m = (ExModel)exam.getModel(); if (!m.areDirectConflictsAllowed()) return 0; int conflicts = 0; - for (ExStudent student: variable().getStudents()) - if (student.nrExams(assignment, getPeriod(), exam)>0) conflicts++; + for (ExStudent student: variable().getStudents()) { + if (student.getContext(assignment).nrExams(getPeriod().getIndex(), exam)>0) conflicts++; + } return conflicts; } @@ -119,13 +121,14 @@ public int twoInADayPenalty(Assignment assignment) { if (!prev && !next) return 0; int penalty = 0; for (ExStudent student: variable().getStudents()) { - if (m.areDirectConflictsAllowed() && student.nrExams(assignment, getPeriod(), exam)>0) continue; + ExStudent.Context cx = student.getContext(assignment); + if (m.areDirectConflictsAllowed() && cx.nrExams(getPeriod().getIndex(), exam)>0) continue; if (prev) for (ExPeriod p=prevPeriod;p!=null && p.getDay()==getPeriod().getDay();p=p.prev()) - if (student.hasExam(assignment, p,exam)) penalty++; + if (cx.hasExam(p.getIndex(), exam)) penalty++; if (next) for (ExPeriod p=nextPeriod;p!=null && p.getDay()==getPeriod().getDay();p=p.next()) - if (student.hasExam(assignment, p,exam)) penalty++; + if (cx.hasExam(p.getIndex(), exam)) penalty++; } return penalty; } @@ -136,11 +139,13 @@ public int widerSpreadPenalty(Assignment assignment) { ExModel m = (ExModel)exam.getModel(); int penalty = 0; for (ExStudent student: variable().getStudents()) { - if (m.areDirectConflictsAllowed() && student.nrExams(assignment, getPeriod(), exam)>0) continue; + ExStudent.Context cx = student.getContext(assignment); + + if (m.areDirectConflictsAllowed() && cx.nrExams(getPeriod().getIndex(), exam)>0) continue; for (ExPeriod p=getPeriod().next();p!=null && p.getIndex()-getPeriod().getIndex()<=m.getPeriodSpreadLength(); p=p.next()) - if (student.hasExam(assignment, p,exam)) penalty++; + if (cx.hasExam(p.getIndex(), exam)) penalty++; for (ExPeriod p=getPeriod().prev();p!=null && getPeriod().getIndex()-p.getIndex()<=m.getPeriodSpreadLength(); p=p.prev()) - if (student.hasExam(assignment, p,exam)) penalty++; + if (cx.hasExam(p.getIndex(), exam)) penalty++; } return penalty; } @@ -158,9 +163,10 @@ private int combinedPenalty(Assignment assignment) { boolean next2 = nextPeriod2!=null && getPeriod().getDay()==nextPeriod2.getDay(); int penalty = 0; for (ExStudent student: variable().getStudents()) { + ExStudent.Context cx = student.getContext(assignment); if (m.areDirectConflictsAllowed()) { - Set exams = student.getExams(assignment, getPeriod()); + Set exams = cx.getExams(getPeriod().getIndex()); int nrExams = exams.size() + (exams.contains(exam)?0:1); if (nrExams>1) { penalty+=m.getDirectConflictWeight(); @@ -169,19 +175,19 @@ private int combinedPenalty(Assignment assignment) { } for (ExPeriod p=getPeriod().next();p!=null && p.getIndex()-getPeriod().getIndex()<=m.getPeriodSpreadLength(); p=p.next()) - if (student.hasExam(assignment, p,exam)) penalty++; + if (cx.hasExam(p.getIndex(), exam)) penalty++; for (ExPeriod p=getPeriod().prev();p!=null && getPeriod().getIndex()-p.getIndex()<=m.getPeriodSpreadLength(); p=p.prev()) - if (student.hasExam(assignment, p,exam)) penalty++; + if (cx.hasExam(p.getIndex(), exam)) penalty++; - if (prev && student.hasExam(assignment, prevPeriod,exam)) penalty+=m.getTwoInARowWeight(); - if (next && student.hasExam(assignment, nextPeriod,exam)) penalty+=m.getTwoInARowWeight(); + if (prev && cx.hasExam(prevPeriod.getIndex(), exam)) penalty+=m.getTwoInARowWeight(); + if (next && cx.hasExam(nextPeriod.getIndex(), exam)) penalty+=m.getTwoInARowWeight(); if (prev2) for (ExPeriod p=prevPeriod2;p!=null && p.getDay()==getPeriod().getDay();p=p.prev()) - if (student.hasExam(assignment, p,exam)) penalty+=m.getTwoInADayWeight(); + if (cx.hasExam(p.getIndex(), exam)) penalty+=m.getTwoInADayWeight(); if (next2) for (ExPeriod p=nextPeriod2;p!=null && p.getDay()==getPeriod().getDay();p=p.next()) - if (student.hasExam(assignment, p,exam)) penalty+=m.getTwoInADayWeight(); + if (cx.hasExam(p.getIndex(), exam)) penalty+=m.getTwoInADayWeight(); } return penalty; } diff --git a/src/net/sf/cpsolver/itc/exam/model/ExRoom.java b/src/net/sf/cpsolver/itc/exam/model/ExRoom.java index f0457c6..e62dd35 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExRoom.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExRoom.java @@ -1,11 +1,15 @@ package net.sf.cpsolver.itc.exam.model; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.TreeSet; import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.assignment.context.ConstraintWithContext; import org.cpsolver.ifs.model.ConstraintListener; @@ -33,7 +37,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class ExRoom extends ConstraintWithContext { +public class ExRoom extends ConstraintWithContext implements CanInheritContext { private int iSize; private int iPenalty; @@ -76,6 +80,12 @@ public void computeConflicts(Assignment assignment, ExPlace Context context = getContext(assignment); if (p.getRoom().equals(this)) { ExExam ex = p.variable(); + /* + if (ex.getStudents().size() > getSize()) { + conflicts.add(p); + return; + } + */ if (ex.isRoomExclusive() || context.isExclusive(p.getPeriodIndex())) { conflicts.addAll(context.getExams(p.getPeriodIndex())); return; @@ -99,6 +109,7 @@ public void computeConflicts(Assignment assignment, ExPlace if (adeptDiff==0) break; } } + if (adept == null) break; adepts.remove(adept); rem += ((ExExam)adept.variable()).getStudents().size(); conflicts.add(adept); @@ -112,6 +123,7 @@ public void computeConflicts(Assignment assignment, ExPlace public boolean inConflict(Assignment assignment, ExPlacement p) { if (!p.getRoom().equals(this)) return false; ExExam ex = p.variable(); + // if (ex.getStudents().size() > getSize()) return true; Context context = getContext(assignment); return ( context.isExclusive(p.getPeriodIndex()) || @@ -278,23 +290,39 @@ private Context(ExModel m, Assignment assignment) { if (value.getRoom().equals(ExRoom.this)) assigned(assignment, value); } + + @SuppressWarnings("unchecked") + private Context(ExModel m, Assignment assignment, Context parent) { + synchronized (parent.iExams) { + iUsedSpace = Arrays.copyOf(parent.iUsedSpace, m.getNrPeriods()); + iExclusive = Arrays.copyOf(parent.iExclusive, m.getNrPeriods()); + iExams = new HashSet[m.getNrPeriods()]; + for (int i = 0; i < m.getNrPeriods(); i++) { + iExams[i] = new HashSet(parent.iExams[i]); + } + } + } @Override public void assigned(Assignment assignment, ExPlacement p) { - iExams[p.getPeriodIndex()].add(p); - iUsedSpace[p.getPeriodIndex()] += p.variable().getStudents().size(); - iExclusive[p.getPeriodIndex()] = p.variable().isRoomExclusive(); + synchronized (iExams) { + iExams[p.getPeriodIndex()].add(p); + iUsedSpace[p.getPeriodIndex()] += p.variable().getStudents().size(); + iExclusive[p.getPeriodIndex()] = p.variable().isRoomExclusive(); + } } @Override public void unassigned(Assignment assignment, ExPlacement p) { - iExams[p.getPeriodIndex()].remove(p); - iUsedSpace[p.getPeriodIndex()] -= p.variable().getStudents().size(); - iExclusive[p.getPeriodIndex()] = false; + synchronized (iExams) { + iExams[p.getPeriodIndex()].remove(p); + iUsedSpace[p.getPeriodIndex()] -= p.variable().getStudents().size(); + iExclusive[p.getPeriodIndex()] = false; + } } public int getUsedSpace(int period) { - return iUsedSpace[period]; + return Math.max(0, iUsedSpace[period]); } public boolean isExclusive(int period) { @@ -304,11 +332,23 @@ public boolean isExclusive(int period) { public Set getExams(int period) { return iExams[period]; } -} + + public List getExaminations(int period) { + List exams = new ArrayList(iExams[period].size()); + for (ExPlacement p: getExams(period)) + exams.add(p.variable()); + return exams; + } + } @Override public Context createAssignmentContext(Assignment assignment) { return new Context((ExModel)getModel(), assignment); } + + @Override + public Context inheritAssignmentContext(Assignment assignment, Context parentContext) { + return new Context((ExModel)getModel(), assignment, parentContext); + } } diff --git a/src/net/sf/cpsolver/itc/exam/model/ExStudent.java b/src/net/sf/cpsolver/itc/exam/model/ExStudent.java index 655718a..a085d81 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExStudent.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExStudent.java @@ -47,41 +47,71 @@ public ExStudent(int id) { } /** List of exams that this student has at the given period */ - public abstract Set getExams(Assignment assignment, int period); + @Deprecated + public Set getExams(Assignment assignment, int period) { + return getContext(assignment).getExams(period); + } + /** List of exams that this student has at the given period */ + @Deprecated public Set getExams(Assignment assignment, ExPeriod period) { - return getExams(assignment, period.getIndex()); + return getContext(assignment).getExams(period.getIndex()); } + /** List of exams that this student has at the given period */ public String getExamStr(Assignment assignment, ExPeriod period) { StringBuffer sb = new StringBuffer(); - for (Iterator i=getExams(assignment, period.getIndex()).iterator();i.hasNext();) { + for (Iterator i=getContext(assignment).getExams(period.getIndex()).iterator();i.hasNext();) { sb.append(i.next().getId()); sb.append(i.hasNext()?",":""); } return sb.toString(); } + /** True, if this student has one or more exams at the given period */ - public abstract boolean hasExam(Assignment assignment, int period); + @Deprecated + public boolean hasExam(Assignment assignment, int period) { + return getContext(assignment).hasExam(period); + } /** True, if this student has one or more exams at the given period */ + @Deprecated public boolean hasExam(Assignment assignment, ExPeriod period) { - return hasExam(assignment, period.getIndex()); + return getContext(assignment).hasExam(period.getIndex()); } /** True, if this student has one or more exams at the given period (given exam excluded) */ - public abstract boolean hasExam(Assignment assignment, int period, ExExam exclude); + @Deprecated + public boolean hasExam(Assignment assignment, int period, ExExam exclude) { + return getContext(assignment).hasExam(period, exclude); + } + /** True, if this student has one or more exams at the given period (given exam excluded) */ + @Deprecated public boolean hasExam(Assignment assignment, ExPeriod period, ExExam exclude) { - return hasExam(assignment, period.getIndex(), exclude); + return getContext(assignment).hasExam(period.getIndex(), exclude); } + /** Number of exams that this student has at the given period */ - public abstract int nrExams(Assignment assignment, int period); + @Deprecated + public int nrExams(Assignment assignment, int period) { + return getContext(assignment).nrExams(period); + } + /** Number of exams that this student has at the given period */ - public int nrExams(Assignment assignment, ExPeriod period) { return nrExams(assignment, period.getIndex()); } + @Deprecated + public int nrExams(Assignment assignment, ExPeriod period) { + return getContext(assignment).nrExams(period.getIndex()); + } + /** Number of exams that this student has at the given period (given exam excluded) */ - public abstract int nrExams(Assignment assignment, int period, ExExam exclude); + @Deprecated + public int nrExams(Assignment assignment, int period, ExExam exclude) { + return getContext(assignment).nrExams(period, exclude); + } + /** Number of exams that this student has at the given period (given exam excluded) */ + @Deprecated public int nrExams(Assignment assignment, ExPeriod period, ExExam exclude) { - return nrExams(assignment, period.getIndex(), exclude); + return getContext(assignment).nrExams(period.getIndex(), exclude); } /** @@ -192,7 +222,10 @@ public int getNrDirectConflicts(Assignment assignment) { public interface Context extends AssignmentConstraintContext { public ExPlacement getPlacement(int period); + public boolean hasExam(int period); + public boolean hasExam(int period, ExExam exclude); public Set getExams(int period); public int nrExams(int period); + public int nrExams(int period, ExExam exclude); } } diff --git a/src/net/sf/cpsolver/itc/exam/model/ExStudentHard.java b/src/net/sf/cpsolver/itc/exam/model/ExStudentHard.java index 82c5d90..7dc09a4 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExStudentHard.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExStudentHard.java @@ -1,9 +1,11 @@ package net.sf.cpsolver.itc.exam.model; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.model.ConstraintListener; /** @@ -30,7 +32,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class ExStudentHard extends ExStudent { +public class ExStudentHard extends ExStudent implements CanInheritContext { /** * Constructor @@ -98,34 +100,7 @@ public ExPlacement getPlacement(Assignment assignment, int public ExPlacement getPlacement(Assignment assignment, ExPeriod period) { return getPlacement(assignment, period.getIndex()); } - /** - * Return exams that are assigned to the given period - */ - public Set getExams(Assignment assignment, int period) { - Set set = new HashSet(); - ExPlacement p = getPlacement(assignment, period); - if (p != null) set.add(p.variable()); - return set; - } - - /** True, if this student has one or more exams at the given period */ - public boolean hasExam(Assignment assignment, int period) { - return getPlacement(assignment, period) != null; - } - /** True, if this student has one or more exams at the given period (given exam excluded) */ - public boolean hasExam(Assignment assignment, int period, ExExam exclude) { - ExPlacement p = getPlacement(assignment, period); - return p != null && !p.variable().equals(exclude); - } - /** Number of exams that this student has at the given period */ - public int nrExams(Assignment assignment, int period) { - return (hasExam(assignment, period)?1:0); - } - /** Number of exams that this student has at the given period (given exam excluded)*/ - public int nrExams(Assignment assignment, int period, ExExam exclude) { - return (hasExam(assignment, period,exclude)?1:0); - } - + public class Context implements ExStudent.Context { private ExPlacement[] iTable = null; @@ -140,6 +115,10 @@ private Context(ExModel m, Assignment assignment) { assigned(assignment, p); } } + + private Context(ExModel m, Assignment assignment, Context parent) { + iTable = Arrays.copyOf(parent.iTable, m.getNrPeriods()); + } @Override public void assigned(Assignment assignment, ExPlacement p) { @@ -167,10 +146,32 @@ public Set getExams(int period) { public int nrExams(int period) { return iTable[period] != null ? 1 : 0; } + + @Override + public boolean hasExam(int period) { + return iTable[period] != null; + } + + @Override + public boolean hasExam(int period, ExExam exclude) { + ExPlacement p = iTable[period]; + return p != null && !p.variable().equals(exclude); + } + + @Override + public int nrExams(int period, ExExam exclude) { + ExPlacement p = iTable[period]; + return p != null && !p.variable().equals(exclude) ? 1 : 0; + } } @Override public Context createAssignmentContext(Assignment assignment) { return new Context((ExModel)getModel(), assignment); } + + @Override + public Context inheritAssignmentContext(Assignment assignment, Context parentContext) { + return new Context((ExModel)getModel(), assignment, parentContext); + } } diff --git a/src/net/sf/cpsolver/itc/exam/model/ExStudentSoft.java b/src/net/sf/cpsolver/itc/exam/model/ExStudentSoft.java index 2f31254..557d18b 100644 --- a/src/net/sf/cpsolver/itc/exam/model/ExStudentSoft.java +++ b/src/net/sf/cpsolver/itc/exam/model/ExStudentSoft.java @@ -1,9 +1,11 @@ package net.sf.cpsolver.itc.exam.model; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.assignment.context.CanInheritContext; /** * Representation of a student. Direct student conflicts are allowed @@ -29,7 +31,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class ExStudentSoft extends ExStudent { +public class ExStudentSoft extends ExStudent implements CanInheritContext { /** * Constructor @@ -73,31 +75,6 @@ public void afterUnassigned(Assignment assignment, long ite getContext(assignment).unassigned(assignment, p); } - /** List of exams that this student has at the given period */ - public Set getExams(Assignment assignment, int period) { - return getContext(assignment).getExams(period); - } - /** True, if this student has one or more exams at the given period */ - public boolean hasExam(Assignment assignment, int period) { - return getContext(assignment).nrExams(period) > 0; - } - /** Number of exams that this student has at the given period */ - public int nrExams(Assignment assignment, int period) { - return getContext(assignment).nrExams(period); - } - /** True, if this student has one or more exams at the given period (given exam excluded)*/ - public boolean hasExam(Assignment assignment, int period, ExExam exclude) { - if (exclude == null) return hasExam(assignment, period); - ExStudent.Context context = getContext(assignment); - return (context.nrExams(period) > (context.getExams(period).contains(exclude) ? 1 : 0)); - } - /** Number of exams that this student has at the given period (given exam excluded)*/ - public int nrExams(Assignment assignment, int period, ExExam exclude) { - if (exclude == null) return nrExams(assignment, period); - ExStudent.Context context = getContext(assignment); - return context.nrExams(period) - (context.getExams(period).contains(exclude) ? 1 : 0); - } - public void assigned(long iteration, ExPlacement value) { } @@ -123,17 +100,32 @@ private Context(ExModel m, Assignment assignment) { assigned(assignment, p); } } + + @SuppressWarnings("unchecked") + private Context(ExModel m, Assignment assignment, Context parent) { + synchronized (parent.iExams) { + iNrExams = Arrays.copyOf(parent.iNrExams, m.getNrPeriods()); + iExams = new HashSet[m.getNrPeriods()]; + for (int i=0;i(parent.iExams[i]); + } + } + } @Override public void assigned(Assignment assignment, ExPlacement p) { - if (iExams[p.getPeriodIndex()].add(p.variable())) - iNrExams[p.getPeriodIndex()]++; + synchronized (iExams) { + if (iExams[p.getPeriodIndex()].add(p.variable())) + iNrExams[p.getPeriodIndex()]++; + } } @Override public void unassigned(Assignment assignment, ExPlacement p) { - if (iExams[p.getPeriodIndex()].remove(p.variable())) - iNrExams[p.getPeriodIndex()]--; + synchronized (iExams) { + if (iExams[p.getPeriodIndex()].remove(p.variable())) + iNrExams[p.getPeriodIndex()]--; + } } @Override @@ -150,6 +142,23 @@ public int nrExams(int period) { public ExPlacement getPlacement(int period) { return null; } + + @Override + public boolean hasExam(int period) { + return iNrExams[period] > 0; + } + + @Override + public boolean hasExam(int period, ExExam exclude) { + if (exclude == null) return iNrExams[period] > 0; + return (iNrExams[period] > (iExams[period].contains(exclude) ? 1 : 0)); + } + + @Override + public int nrExams(int period, ExExam exclude) { + if (exclude == null) return iNrExams[period]; + return (iExams[period].contains(exclude) ? iNrExams[period] - 1 : iNrExams[period]); + } } @Override @@ -157,4 +166,9 @@ public Context createAssignmentContext(Assignment assignmen return new Context((ExModel)getModel(), assignment); } + @Override + public Context inheritAssignmentContext(Assignment assignment, Context parentContext) { + return new Context((ExModel)getModel(), assignment, parentContext); + } + } diff --git a/src/net/sf/cpsolver/itc/heuristics/ItcNeighbourSelection.java b/src/net/sf/cpsolver/itc/heuristics/ItcNeighbourSelection.java index 025a590..865faa1 100644 --- a/src/net/sf/cpsolver/itc/heuristics/ItcNeighbourSelection.java +++ b/src/net/sf/cpsolver/itc/heuristics/ItcNeighbourSelection.java @@ -16,7 +16,6 @@ import org.cpsolver.ifs.solver.Solver; import org.cpsolver.ifs.util.DataProperties; -import net.sf.cpsolver.itc.ItcModel; import net.sf.cpsolver.itc.heuristics.search.ItcHillClimber; import net.sf.cpsolver.itc.heuristics.search.ItcTabuSearch; import net.sf.cpsolver.itc.heuristics.search.ItcGreatDeluge; @@ -118,27 +117,30 @@ public void init(Solver solver) { /** Neighbour selection -- based on the phase, construction strategy is used first, * than it iterates between two or three given neighbour selections*/ public Neighbour selectNeighbour(Solution solution) { - Neighbour n = null; + if (!solution.isComplete() && solution.getModel().getBestUnassignedVariables() == 0) + return iConstruct.selectNeighbour(solution); + + Neighbour n = null; Phase phase = getContext(solution.getAssignment()); switch (phase.getPhase()) { case 0 : n = iConstruct.selectNeighbour(solution); if (n!=null) return n; - phase.incPhase(solution, "first"); + phase.setPhase(1, solution, "one"); case 1 : n = iFirst.selectNeighbour(solution); if (n!=null) return n; - phase.incPhase(solution, "second"); + phase.setPhase(2, solution, "two"); case 2 : n = iSecond.selectNeighbour(solution); if (n!=null) return n; - phase.incPhase(solution, "third"); + phase.setPhase(3, solution, "three"); case 3 : n = (iThird==null?null:iThird.selectNeighbour(solution)); if (n!=null) return n; - phase.incPhase(solution, "first"); + phase.setPhase(4, solution, "one"); default : - phase.setPhase(1); + phase.setPhase(1, solution, "one"); return selectNeighbour(solution); } } @@ -151,18 +153,14 @@ public int getPhase() { } /** Change phase, i.e., what selector is to be used next */ - public void incPhase(Solution solution, String name) { - iPhase ++; + public synchronized void setPhase(int phase, Solution solution, String name) { + if (iPhase == phase) return; + iPhase = phase; if (sLog.isInfoEnabled()) { - ItcModel m = (ItcModel)solution.getModel(); - sLog.info("**CURR["+solution.getIteration()+"]** P:"+Math.round(m.getTotalValue(solution.getAssignment()))+" ("+m.csvLine(solution.getAssignment())+")"); + sLog.info("**CURR["+solution.getIteration()+"]** " + solution); sLog.info("Phase "+name); } } - - public void setPhase(int phase) { - iPhase = phase; - } } @Override diff --git a/src/net/sf/cpsolver/itc/heuristics/ItcUnassignedVariableSelection.java b/src/net/sf/cpsolver/itc/heuristics/ItcUnassignedVariableSelection.java index 7fd013d..bf8ac4d 100644 --- a/src/net/sf/cpsolver/itc/heuristics/ItcUnassignedVariableSelection.java +++ b/src/net/sf/cpsolver/itc/heuristics/ItcUnassignedVariableSelection.java @@ -1,6 +1,10 @@ package net.sf.cpsolver.itc.heuristics; +import java.lang.reflect.Array; +import java.util.Collection; + import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.assignment.InheritedAssignment; import org.cpsolver.ifs.heuristics.VariableSelection; import org.cpsolver.ifs.model.Model; import org.cpsolver.ifs.model.Value; @@ -8,6 +12,7 @@ import org.cpsolver.ifs.solution.Solution; import org.cpsolver.ifs.solver.Solver; import org.cpsolver.ifs.util.DataProperties; +import org.cpsolver.ifs.util.ToolBox; /** * Unassigned variable selection. The "biggest" variable (using {@link Variable#compareTo(Object)}) @@ -44,13 +49,41 @@ public void init(Solver solver) {} /** Variable selection */ public V selectVariable(Solution solution) { - Model model = solution.getModel(); + Model model = solution.getModel(); Assignment assignment = solution.getAssignment(); - if (assignment.nrAssignedVariables() == model.variables().size()) return null; - V variable = null; - for (V v: assignment.unassignedVariables(model)) { - if (variable==null || v.compareTo(variable)<0) variable = v; + if (assignment.nrAssignedVariables() == model.variables().size()) return null; + int index = solution.getAssignment().getIndex(); + Collection unassigned = assignment.unassignedVariables(model); + if (solution.getAssignment() instanceof InheritedAssignment) { + if (index < 0 || unassigned.size() < index) { + return ToolBox.random(unassigned); + } else if (index <= 1) { + V variable = null; + for (V v: unassigned) { + if (variable==null || v.compareTo(variable)<0) variable = v; + } + return variable; + } else { + @SuppressWarnings("unchecked") + V[] adepts = (V[])Array.newInstance(Variable.class, index); + for (V v: unassigned) { + for (int i = 0; i < adepts.length; i++) { + V x = adepts[i]; + if (x == null) { + adepts[i] = v; break; + } else if (v.compareTo(x) < 0) { + adepts[i] = v; v = x; + } + } + } + return adepts[index - 1]; + } + } else { + V variable = null; + for (V v: unassigned) { + if (variable==null || v.compareTo(variable)<0) variable = v; + } + return variable; } - return variable; } } diff --git a/src/net/sf/cpsolver/itc/heuristics/neighbour/ItcLazyNeighbour.java b/src/net/sf/cpsolver/itc/heuristics/neighbour/ItcLazyNeighbour.java deleted file mode 100644 index 0d7ebfe..0000000 --- a/src/net/sf/cpsolver/itc/heuristics/neighbour/ItcLazyNeighbour.java +++ /dev/null @@ -1,86 +0,0 @@ -package net.sf.cpsolver.itc.heuristics.neighbour; - -import org.cpsolver.ifs.assignment.Assignment; -import org.cpsolver.ifs.model.Neighbour; -import org.cpsolver.ifs.model.Value; -import org.cpsolver.ifs.model.Variable; -import net.sf.cpsolver.itc.ItcModel; - -/** - * Lazy neigbour (a change of the overall solution value is unknown before - * the neighbour is assigned, it is possible to undo the neighbour instead). - * This neighbour is useful when it is - * two expensive to compute change of overall solution value before the - * variable is reassigned. It is possible to undo the neighbour instead. - * Search strategy has to implement {@link LazyNeighbourAcceptanceCriterion}. - * - * @version - * ITC2007 1.0
- * Copyright (C) 2007 Tomas Muller
- * muller@unitime.org
- * http://muller.unitime.org
- *
- * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - *

- * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - *

- * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not see - * http://www.gnu.org/licenses/. - */ -public abstract class ItcLazyNeighbour, T extends Value> implements Neighbour { - private LazyNeighbourAcceptanceCriterion iCriterion = null; - - /** - * Set acceptance criterion (to be used by a search strategy before the - * neighbour is accepted, so that it can be undone if desired) - */ - public void setAcceptanceCriterion(LazyNeighbourAcceptanceCriterion criterion) { - iCriterion = criterion; - } - - /** - * Assign neighbour, check given acceptance criterion, and undo - * assignment if the change is not accepted. - */ - public void assign(Assignment assignment, long iteration) { - double before = getModel().getTotalValue(assignment); - doAssign(assignment, iteration); - double after = getModel().getTotalValue(assignment); - if (!iCriterion.accept(assignment, this, after - before)) undoAssign(assignment, iteration); - } - /** - * Return -1 (neighbour is always accepted). The search strategy that - * is using this neighbour must implement {@link LazyNeighbourAcceptanceCriterion}. - */ - public double value(Assignment assignment) { return -1; } - - /** Perform assignment */ - protected abstract void doAssign(Assignment assignment, long iteration); - /** Undo assignment */ - protected abstract void undoAssign(Assignment assignment, long iteration); - /** Return problem model (it is needed in order to be able to get - * overall solution value before and after the assignment of this neighbour) */ - public abstract ItcModel getModel(); - - /** Neighbour acceptance criterion interface (to be implemented - * by search strategies that are using {@link ItcLazyNeighbour}. - * It is also required to call {@link ItcLazyNeighbour#setAcceptanceCriterion(net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour.LazyNeighbourAcceptanceCriterion)} - * before the neighbour is accepted by the search strategy. - */ - public static interface LazyNeighbourAcceptanceCriterion, T extends Value> { - /** True when the currently assigned neighbour should be accepted (false means - * that the change will be undone - * @param neighbour neighbour that was assigned - * @param value change in overall solution value - * @return true if the neighbour can be accepted (false to undo the assignment) - */ - public boolean accept(Assignment assignment, ItcLazyNeighbour neighbour, double value); - } -} diff --git a/src/net/sf/cpsolver/itc/heuristics/neighbour/ItcLazySwap.java b/src/net/sf/cpsolver/itc/heuristics/neighbour/ItcLazySwap.java deleted file mode 100644 index 6584c91..0000000 --- a/src/net/sf/cpsolver/itc/heuristics/neighbour/ItcLazySwap.java +++ /dev/null @@ -1,82 +0,0 @@ -package net.sf.cpsolver.itc.heuristics.neighbour; - -import java.util.HashMap; -import java.util.Map; - -import org.cpsolver.ifs.assignment.Assignment; -import org.cpsolver.ifs.model.Value; -import org.cpsolver.ifs.model.Variable; -import net.sf.cpsolver.itc.ItcModel; - -/** - * Lazy swap of two variables. See {@link ItcLazyNeighbour}. - * - * @version - * ITC2007 1.0
- * Copyright (C) 2007 Tomas Muller
- * muller@unitime.org
- * http://muller.unitime.org
- *
- * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - *

- * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - *

- * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not see - * http://www.gnu.org/licenses/. - */ -public class ItcLazySwap, T extends Value> extends ItcLazyNeighbour { - private T iV1, iV2, iOldV1, iOldV2; - - /** - * Constructor - * @param v1 first variable - * @param v2 second variable - */ - public ItcLazySwap(Assignment assignment, T v1, T v2) { - iV1 = v1; - iV2 = v2; - iOldV1 = assignment.getValue(v1.variable()); - iOldV2 = assignment.getValue(v2.variable()); - } - - /** Perform swap */ - protected void doAssign(Assignment assignment, long iteration) { - iOldV1 = assignment.unassign(iteration, iV1.variable()); - iOldV2 = assignment.unassign(iteration, iV2.variable()); - assignment.assign(iteration, iV1); - assignment.assign(iteration, iV2); - } - - /** Undo the swap */ - protected void undoAssign(Assignment assignment, long iteration) { - assignment.unassign(iteration, iV1.variable()); - assignment.unassign(iteration, iV2.variable()); - if (iOldV1 != null) assignment.assign(iteration, iOldV1); - if (iOldV2 != null) assignment.assign(iteration, iOldV2); - } - /** Return problem model */ - public ItcModel getModel() { - return (ItcModel)iV1.variable().getModel(); - } - - /** String representation */ - public String toString() { - return "Lazy " + iOldV1 + " -> " + iV1 + ", " + iOldV2 + " -> " + iV2; - } - - @Override - public Map assignments() { - Map ret = new HashMap(); - ret.put(iV1.variable(), iV1); - ret.put(iV2.variable(), iV2); - return ret; - } - -} diff --git a/src/net/sf/cpsolver/itc/heuristics/search/ItcGreatDeluge.java b/src/net/sf/cpsolver/itc/heuristics/search/ItcGreatDeluge.java index cd959bd..c4a781c 100644 --- a/src/net/sf/cpsolver/itc/heuristics/search/ItcGreatDeluge.java +++ b/src/net/sf/cpsolver/itc/heuristics/search/ItcGreatDeluge.java @@ -9,12 +9,13 @@ import java.util.StringTokenizer; import org.apache.log4j.Logger; - import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.context.AssignmentContext; import org.cpsolver.ifs.assignment.context.NeighbourSelectionWithContext; import org.cpsolver.ifs.extension.Extension; import org.cpsolver.ifs.heuristics.NeighbourSelection; +import org.cpsolver.ifs.model.LazyNeighbour; +import org.cpsolver.ifs.model.LazyNeighbour.LazyNeighbourAcceptanceCriterion; import org.cpsolver.ifs.model.Model; import org.cpsolver.ifs.model.Neighbour; import org.cpsolver.ifs.model.Value; @@ -24,10 +25,9 @@ import org.cpsolver.ifs.solver.Solver; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.ifs.util.ToolBox; + import net.sf.cpsolver.itc.heuristics.ItcParameterWeightOscillation; import net.sf.cpsolver.itc.heuristics.ItcParameterWeightOscillation.OscillationListener; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour.LazyNeighbourAcceptanceCriterion; import net.sf.cpsolver.itc.heuristics.neighbour.selection.ItcNotConflictingMove; import net.sf.cpsolver.itc.heuristics.neighbour.selection.ItcSwapMove; import net.sf.cpsolver.itc.heuristics.search.ItcHillClimber.NeighbourSelector; @@ -201,14 +201,14 @@ private Neighbour genMove(Solution solution) { } private boolean accept(Solution solution, Neighbour neighbour) { - if (neighbour instanceof ItcLazyNeighbour) { - ((ItcLazyNeighbour)neighbour).setAcceptanceCriterion(this); + if (neighbour instanceof LazyNeighbour) { + ((LazyNeighbour)neighbour).setAcceptanceCriterion(this); return true; } else return (neighbour.value(solution.getAssignment())<=0 || solution.getModel().getTotalValue(solution.getAssignment())+neighbour.value(solution.getAssignment())<= getContext(solution.getAssignment()).getBound()); } /** Implementation of {@link net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour.LazyNeighbourAcceptanceCriterion} interface */ - public boolean accept(Assignment assignment, ItcLazyNeighbour neighbour, double value) { + public boolean accept(Assignment assignment, LazyNeighbour neighbour, double value) { return (value <= 0 || neighbour.getModel().getTotalValue(assignment) <= getContext(assignment).getBound()); } @@ -262,7 +262,11 @@ protected void info(Solution solution) { iAcceptedMoves = iMoves = 0; } - protected boolean incIter(Solution solution) { + protected boolean canCheckBound(Solution solution) { + return !hasContextOverride() || solution.getAssignment().getIndex() <= 1; + } + + protected synchronized boolean incIter(Solution solution) { double best = solution.getModel().getBestValue(); if (iIter < 0) { iIter = 0; iLastImprovingIter = 0; @@ -276,21 +280,25 @@ protected boolean incIter(Solution solution) { if (sInfo && iIter % 100000 == 0) { info(solution); } - if (iIter % 1000 == 0 && iBound > Math.max(best + 2.0, Math.pow(iUpperBoundRate, 1 + iNrIdle) * best)) { - iBound = Math.max(best + 2.0, Math.pow(iUpperBoundRate, iNrIdle) * best); - if (solution.getModel().getTotalValue(solution.getAssignment()) > iBound) - solution.restoreBest(); - } - if (iBound < Math.pow(iLowerBoundRate, 1 + iNrIdle) * best) { - iNrIdle++; - sLog.info(" -<["+iNrIdle+"]>- "); - iBound = Math.max(best + 2.0, Math.pow(iUpperBoundRate, iNrIdle) * best); - iAlterBound = iAlterBoundOnReheat; - if (iNextHeuristicsOnReheat) return true; - } - if (iAlterBound) { - iBound = Math.max(best + 2.0, Math.pow(iUpperBoundRate, Math.max(1, iNrIdle)) * best); - iAlterBound = false; + if (canCheckBound(solution)) { + if (iIter % 1000 == 0 && iBound > Math.max(best + 2.0, Math.pow(iUpperBoundRate, 1 + iNrIdle) * best)) { + iBound = Math.max(best + 2.0, Math.pow(iUpperBoundRate, iNrIdle) * best); + if (solution.getModel().getTotalValue(solution.getAssignment()) > iBound) + solution.restoreBest(); + } + if (iBound < Math.pow(iLowerBoundRate, 1 + iNrIdle) * best) { + iNrIdle++; + sLog.info(" -<["+iNrIdle+"]>- "); + iBound = Math.max(best + 2.0, Math.pow(iUpperBoundRate, iNrIdle) * best); + iAlterBound = iAlterBoundOnReheat; + if (iNextHeuristicsOnReheat) return true; + } + if (iAlterBound) { + iBound = Math.max(best + 2.0, Math.pow(iUpperBoundRate, Math.max(1, iNrIdle)) * best); + //if (solution.getModel().getTotalValue(solution.getAssignment()) > iBound) + // solution.restoreBest(); + iAlterBound = false; + } } return false; } diff --git a/src/net/sf/cpsolver/itc/heuristics/search/ItcHillClimber.java b/src/net/sf/cpsolver/itc/heuristics/search/ItcHillClimber.java index bc043df..3b6ae08 100644 --- a/src/net/sf/cpsolver/itc/heuristics/search/ItcHillClimber.java +++ b/src/net/sf/cpsolver/itc/heuristics/search/ItcHillClimber.java @@ -12,6 +12,8 @@ import org.cpsolver.ifs.assignment.context.AssignmentContext; import org.cpsolver.ifs.assignment.context.NeighbourSelectionWithContext; import org.cpsolver.ifs.heuristics.NeighbourSelection; +import org.cpsolver.ifs.model.LazyNeighbour; +import org.cpsolver.ifs.model.LazyNeighbour.LazyNeighbourAcceptanceCriterion; import org.cpsolver.ifs.model.Model; import org.cpsolver.ifs.model.Neighbour; import org.cpsolver.ifs.model.Value; @@ -21,8 +23,7 @@ import org.cpsolver.ifs.solver.Solver; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.ifs.util.ToolBox; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour.LazyNeighbourAcceptanceCriterion; + import net.sf.cpsolver.itc.heuristics.neighbour.selection.ItcNotConflictingMove; import net.sf.cpsolver.itc.heuristics.neighbour.selection.ItcSwapMove; @@ -161,8 +162,8 @@ public Neighbour selectNeighbour(Solution solution) { } Neighbour n = ns.selectNeighbour(solution); if (n!=null) { - if (n instanceof ItcLazyNeighbour) { - ((ItcLazyNeighbour)n).setAcceptanceCriterion(this); + if (n instanceof LazyNeighbour) { + ((LazyNeighbour)n).setAcceptanceCriterion(this); return n; } else if (n.value(solution.getAssignment())<=0.0) return n; } @@ -172,7 +173,7 @@ public Neighbour selectNeighbour(Solution solution) { } /** Implementation of {@link net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour.LazyNeighbourAcceptanceCriterion} interface */ - public boolean accept(Assignment assignment, ItcLazyNeighbour neighbour, double value) { + public boolean accept(Assignment assignment, LazyNeighbour neighbour, double value) { return value<=0; } @@ -297,7 +298,7 @@ public class Context implements AssignmentContext { private int iLastImprovingIter = 0; private int iIter = 0; private long iT0 = -1; - + public void reset() { iIter = 0; iLastImprovingIter = 0; @@ -308,7 +309,7 @@ protected void setT0() { if (iT0 < 0) iT0 = System.currentTimeMillis(); } - protected boolean incIter(Solution solution) { + protected synchronized boolean incIter(Solution solution) { iIter ++; if (iIter % 10000 == 0) { if (sInfo) { diff --git a/src/net/sf/cpsolver/itc/heuristics/search/ItcSimulatedAnnealing.java b/src/net/sf/cpsolver/itc/heuristics/search/ItcSimulatedAnnealing.java index 8bf0c63..3a5398d 100644 --- a/src/net/sf/cpsolver/itc/heuristics/search/ItcSimulatedAnnealing.java +++ b/src/net/sf/cpsolver/itc/heuristics/search/ItcSimulatedAnnealing.java @@ -13,17 +13,18 @@ import org.cpsolver.ifs.assignment.context.AssignmentContext; import org.cpsolver.ifs.assignment.context.NeighbourSelectionWithContext; import org.cpsolver.ifs.heuristics.NeighbourSelection; +import org.cpsolver.ifs.model.LazyNeighbour; import org.cpsolver.ifs.model.Model; import org.cpsolver.ifs.model.Neighbour; import org.cpsolver.ifs.model.Value; import org.cpsolver.ifs.model.Variable; +import org.cpsolver.ifs.model.LazyNeighbour.LazyNeighbourAcceptanceCriterion; import org.cpsolver.ifs.solution.Solution; import org.cpsolver.ifs.solution.SolutionListener; import org.cpsolver.ifs.solver.Solver; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.ifs.util.ToolBox; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour.LazyNeighbourAcceptanceCriterion; + import net.sf.cpsolver.itc.heuristics.neighbour.selection.ItcRandomMove; import net.sf.cpsolver.itc.heuristics.neighbour.selection.ItcSwapMove; import net.sf.cpsolver.itc.heuristics.search.ItcHillClimber.NeighbourSelector; @@ -227,8 +228,8 @@ private Neighbour genMove(Solution solution) { } private boolean accept(Solution solution, Neighbour neighbour) { - if (neighbour instanceof ItcLazyNeighbour) { - ((ItcLazyNeighbour)neighbour).setAcceptanceCriterion(this); + if (neighbour instanceof LazyNeighbour) { + ((LazyNeighbour)neighbour).setAcceptanceCriterion(this); return true; } Model m = solution.getModel(); @@ -243,7 +244,7 @@ private boolean accept(Solution solution, Neighbour neighbour) { } /** Implementation of {@link net.sf.cpsolver.itc.heuristics.neighbour.ItcLazyNeighbour.LazyNeighbourAcceptanceCriterion} interface */ - public boolean accept(Assignment assignment, ItcLazyNeighbour neighbour, double value) { + public boolean accept(Assignment assignment, LazyNeighbour neighbour, double value) { double prob = getContext(assignment).prob(value); if (prob>=1.0 || ToolBox.random() solution) { iTemperature *= iCoolingRate; if (sInfo) { @@ -314,20 +315,26 @@ private void reheat(Solution solution) { iLastReheatIter=iIter; } - private boolean incIter(Solution solution) { + protected boolean canCoolOrRehear(Solution solution) { + return !hasContextOverride() || solution.getAssignment().getIndex() <= 1; + } + + private synchronized boolean incIter(Solution solution) { if (iT0 < 0) { iT0 = System.currentTimeMillis(); iTemperature = iInitialTemperature; } iIter++; - if (iIter > iLastImprovingIter + iRestoreBestLength) { - restoreBest(solution); - } - if (iIter > Math.max(iLastReheatIter, iLastImprovingIter) + iReheatLength) { - reheat(solution); - return iNextHeuristicsOnReheat; + if (canCoolOrRehear(solution)) { + if (iIter > iLastImprovingIter + iRestoreBestLength) { + restoreBest(solution); + } + if (iIter > Math.max(iLastReheatIter, iLastImprovingIter) + iReheatLength) { + reheat(solution); + return iNextHeuristicsOnReheat; + } + if (iIter > iLastCoolingIter + iTemperatureLength) cool(solution); } - if (iIter > iLastCoolingIter + iTemperatureLength) cool(solution); return false; } diff --git a/src/net/sf/cpsolver/itc/heuristics/search/ItcTabuSearch.java b/src/net/sf/cpsolver/itc/heuristics/search/ItcTabuSearch.java index 7fbc96d..137d883 100644 --- a/src/net/sf/cpsolver/itc/heuristics/search/ItcTabuSearch.java +++ b/src/net/sf/cpsolver/itc/heuristics/search/ItcTabuSearch.java @@ -6,9 +6,11 @@ import java.util.Set; import java.util.TreeSet; -import org.apache.log4j.Logger; +import net.sf.cpsolver.itc.heuristics.neighbour.ItcSimpleNeighbour; +import org.apache.log4j.Logger; import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.assignment.InheritedAssignment; import org.cpsolver.ifs.assignment.context.AssignmentContext; import org.cpsolver.ifs.assignment.context.NeighbourSelectionWithContext; import org.cpsolver.ifs.extension.ConflictStatistics; @@ -17,7 +19,6 @@ import org.cpsolver.ifs.heuristics.ValueSelection; import org.cpsolver.ifs.model.Model; import org.cpsolver.ifs.model.Neighbour; -import org.cpsolver.ifs.model.SimpleNeighbour; import org.cpsolver.ifs.model.Value; import org.cpsolver.ifs.model.Variable; import org.cpsolver.ifs.solution.Solution; @@ -112,6 +113,10 @@ public void init(Solver solver) { iStat = (ConflictStatistics)extension; } + public void setValueWeight(double weight) { + iValueWeight = weight; + } + /** * An element that is to be used to populate (and check) tabu list * @param value given value (to be assigned to its variable) @@ -199,7 +204,7 @@ public Neighbour selectNeighbour(Solution solution) { if (tabu.size() > 0) tabu.add(tabuElement(bestVal)); - return new SimpleNeighbour(bestVal.variable(), bestVal); + return new ItcSimpleNeighbour(solution.getAssignment(), bestVal.variable(), bestVal); } /** @@ -268,6 +273,12 @@ public T selectValue(Solution solution, V variable) { sLog.debug(" [tabu] "+bestVal+" ("+(old==null?"":"was="+old+", ")+"val="+bestEval+(conflicts.isEmpty()?"":", conf="+(wconf+conflicts.size())+"/"+conflicts)+")"); } + if (solution.getAssignment() instanceof InheritedAssignment && iStat != null) { + for (T conflict: model.conflictValues(assignment, bestVal)) { + iStat.variableUnassigned(solution.getIteration(), conflict, bestVal); + } + } + if (tabu.size() > 0) tabu.add(tabuElement(bestVal)); return bestVal; diff --git a/src/net/sf/cpsolver/itc/tim/model/TTComp02Model.java b/src/net/sf/cpsolver/itc/tim/model/TTComp02Model.java index 00a4fa9..7c4b138 100644 --- a/src/net/sf/cpsolver/itc/tim/model/TTComp02Model.java +++ b/src/net/sf/cpsolver/itc/tim/model/TTComp02Model.java @@ -7,12 +7,16 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.cpsolver.ifs.assignment.Assignment; +import org.cpsolver.ifs.assignment.InheritedAssignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; +import org.cpsolver.ifs.solution.Solution; import net.sf.cpsolver.itc.ItcModel; @@ -46,7 +50,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class TTComp02Model extends ItcModel { +public class TTComp02Model extends ItcModel implements CanInheritContext{ /** Room constraints */ protected List iRooms = null; /** Student constraints */ @@ -259,6 +263,10 @@ public TTComp02Context(Assignment assignment) { assigned(assignment, location); } } + + public TTComp02Context(Assignment assignment, TTComp02Context parent) { + iScore = Arrays.copyOf(parent.iScore, 3); + } /** * Update counters on assignment of an event @@ -326,4 +334,14 @@ public AssignmentConstraintContext createAssignmentContex public Assignment createAssignment(int index, Assignment assignment) { return new TimAssignment(this, index, assignment); } + + @Override + public InheritedAssignment createInheritedAssignment(Solution solution, int index) { + return new TimInheritedAssignment(solution, index); + } + + @Override + public TTComp02Context inheritAssignmentContext(Assignment assignment, TTComp02Context parentContext) { + return new TTComp02Context(assignment, parentContext); + } } \ No newline at end of file diff --git a/src/net/sf/cpsolver/itc/tim/model/TimAssignment.java b/src/net/sf/cpsolver/itc/tim/model/TimAssignment.java index d2189df..3283d6c 100644 --- a/src/net/sf/cpsolver/itc/tim/model/TimAssignment.java +++ b/src/net/sf/cpsolver/itc/tim/model/TimAssignment.java @@ -7,6 +7,7 @@ import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.AssignmentAbstract; +import org.cpsolver.ifs.assignment.context.AssignmentContextHolder; import org.cpsolver.ifs.assignment.context.DefaultParallelAssignmentContextHolder; import org.cpsolver.ifs.model.Model; @@ -34,17 +35,28 @@ * http://www.gnu.org/licenses/. */ public class TimAssignment extends AssignmentAbstract { - private Model iModel; - private int iIndex; - private int iNrAssigned = 0; + protected Model iModel; + protected int iIndex; + protected int iNrAssigned = 0; + protected TimLocation[] iAssignment = null; + protected TimAssignment iParent = null; public TimAssignment(Model model, int index, Assignment assignment) { super(new DefaultParallelAssignmentContextHolder(index)); iIndex = index; iModel = model; - if (assignment != null) - for (TimLocation value: assignment.assignedValues()) - setValueInternal(0, value.variable(), value); + iParent = (TimAssignment) assignment; + if (iParent == null) { + iAssignment = new TimLocation[model.variables().size()]; + iNrAssigned = 0; + } else { + iAssignment = Arrays.copyOf(((TimAssignment)iParent).toArray(), model.variables().size()); + iNrAssigned = iParent.nrAssignedVariables(); + } + } + + protected TimAssignment(AssignmentContextHolder contextHolder) { + super(contextHolder); } @Override @@ -98,35 +110,14 @@ public Collection assignedVariables() { return assigned; } - protected TimLocation[] getAssignments(TimEvent variable) { - synchronized (variable) { - TimLocation[] assignments = (TimLocation[])variable.getExtra(); - if (assignments == null) { - assignments = (TimLocation[])new TimLocation[Math.max(10, 1 + iIndex)]; - variable.setExtra(assignments); - } else if (assignments.length <= iIndex) { - assignments = Arrays.copyOf(assignments, 10 + iIndex); - variable.setExtra(assignments); - } - return assignments; - } - } - @Override - @SuppressWarnings("deprecation") protected TimLocation getValueInternal(TimEvent variable) { - if (iIndex == 1) - return variable.getAssignment(); - return getAssignments(variable)[iIndex]; + return iAssignment[variable.getIndex()]; } @Override - @SuppressWarnings("deprecation") protected void setValueInternal(long iteration, TimEvent variable, TimLocation value) { - if (iIndex == 1) - variable.setAssignment(value); - else - getAssignments(variable)[iIndex] = value; + iAssignment[variable.getIndex()] = value; if (value == null) iNrAssigned --; else @@ -142,4 +133,12 @@ public int getIndex() { public int nrAssignedVariables() { return iNrAssigned; } + + public TimLocation[] toArray() { + return iAssignment; + } + + public Assignment getParentAssignment() { + return iParent; + } } diff --git a/src/net/sf/cpsolver/itc/tim/model/TimEvent.java b/src/net/sf/cpsolver/itc/tim/model/TimEvent.java index 8460668..2d50d47 100644 --- a/src/net/sf/cpsolver/itc/tim/model/TimEvent.java +++ b/src/net/sf/cpsolver/itc/tim/model/TimEvent.java @@ -7,13 +7,13 @@ import java.util.Set; import org.apache.log4j.Logger; - import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.AssignmentComparable; +import org.cpsolver.ifs.model.LazySwap; import org.cpsolver.ifs.model.Neighbour; import org.cpsolver.ifs.model.Variable; import org.cpsolver.ifs.util.ToolBox; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazySwap; + import net.sf.cpsolver.itc.heuristics.neighbour.ItcSwap.Swapable; /** @@ -270,7 +270,7 @@ public Neighbour findSwap(Assignment(assignment, nl1, nl2); + return new LazySwap(nl1, nl2); } /** Minimal possible starting time (first available slot combined with min start time of all predecessors) */ diff --git a/src/net/sf/cpsolver/itc/tim/model/TimInheritedAssignment.java b/src/net/sf/cpsolver/itc/tim/model/TimInheritedAssignment.java new file mode 100644 index 0000000..1c64d9f --- /dev/null +++ b/src/net/sf/cpsolver/itc/tim/model/TimInheritedAssignment.java @@ -0,0 +1,56 @@ +package net.sf.cpsolver.itc.tim.model; + +import java.util.Arrays; +import java.util.concurrent.locks.Lock; + +import org.cpsolver.ifs.assignment.InheritedAssignment; +import org.cpsolver.ifs.assignment.context.InheritedAssignmentContextHolder; +import org.cpsolver.ifs.solution.Solution; + +/** + * Post Enrollment based Course Timetabling (tim) inherited assignment model. + * + * @version + * ITC2007 1.0
+ * Copyright (C) 2014 Tomas Muller
+ * muller@unitime.org
+ * http://muller.unitime.org
+ *
+ * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *

+ * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not see + * http://www.gnu.org/licenses/. + */ +public class TimInheritedAssignment extends TimAssignment implements InheritedAssignment { + private long iVersion = -1; + + public TimInheritedAssignment(Solution solution, int index) { + super(new InheritedAssignmentContextHolder(index, solution.getIteration())); + iIndex = index; + iVersion = solution.getIteration(); + iModel = (TimModel) solution.getModel(); + iParent = (TimAssignment) solution.getAssignment(); + Lock lock = solution.getLock().readLock(); + lock.lock(); + try { + iNrAssigned = iParent.nrAssignedVariables(); + iAssignment = Arrays.copyOf(((TimAssignment)iParent).toArray(), iModel.variables().size()); + } finally { + lock.unlock(); + } + } + + @Override + public long getVersion() { + return iVersion; + } +} diff --git a/src/net/sf/cpsolver/itc/tim/model/TimModel.java b/src/net/sf/cpsolver/itc/tim/model/TimModel.java index 782c619..a07c93c 100644 --- a/src/net/sf/cpsolver/itc/tim/model/TimModel.java +++ b/src/net/sf/cpsolver/itc/tim/model/TimModel.java @@ -382,6 +382,12 @@ public class TimContext extends TTComp02Context { public TimContext(Assignment assignment) { super(assignment); } + + public TimContext(Assignment assignment, TimContext parent) { + super(assignment, parent); + iPrecedenceViolations = parent.iPrecedenceViolations; + iNoRoomViolations = parent.iNoRoomViolations; + } /** * Update counters on assignment of an event @@ -421,4 +427,9 @@ public int noRoomViolations() { public AssignmentConstraintContext createAssignmentContext(Assignment assignment) { return new TimContext(assignment); } + + @Override + public TTComp02Context inheritAssignmentContext(Assignment assignment, TTComp02Context parentContext) { + return new TimContext(assignment, (TimContext)parentContext); + } } \ No newline at end of file diff --git a/src/net/sf/cpsolver/itc/tim/model/TimRoom.java b/src/net/sf/cpsolver/itc/tim/model/TimRoom.java index 9fa2d4f..60872c9 100644 --- a/src/net/sf/cpsolver/itc/tim/model/TimRoom.java +++ b/src/net/sf/cpsolver/itc/tim/model/TimRoom.java @@ -1,10 +1,12 @@ package net.sf.cpsolver.itc.tim.model; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.assignment.context.ConstraintWithContext; import org.cpsolver.ifs.model.ConstraintListener; @@ -35,7 +37,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class TimRoom extends ConstraintWithContext { +public class TimRoom extends ConstraintWithContext implements CanInheritContext { private int iSize; /** @@ -129,6 +131,10 @@ public Context(Assignment assignment) { iTable[location.time()] = location; } } + + public Context(Assignment assignment, Context parent) { + iTable = Arrays.copyOf(parent.iTable, 45); + } @Override public void assigned(Assignment assignment, TimLocation location) { @@ -151,4 +157,9 @@ public TimLocation getLocation(int time) { public Context createAssignmentContext(Assignment assignment) { return new Context(assignment); } + + @Override + public Context inheritAssignmentContext(Assignment assignment, Context parentContext) { + return new Context(assignment, parentContext); + } } diff --git a/src/net/sf/cpsolver/itc/tim/model/TimStudent.java b/src/net/sf/cpsolver/itc/tim/model/TimStudent.java index 98990ed..b032a14 100644 --- a/src/net/sf/cpsolver/itc/tim/model/TimStudent.java +++ b/src/net/sf/cpsolver/itc/tim/model/TimStudent.java @@ -1,10 +1,12 @@ package net.sf.cpsolver.itc.tim.model; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; +import org.cpsolver.ifs.assignment.context.CanInheritContext; import org.cpsolver.ifs.assignment.context.ConstraintWithContext; import org.cpsolver.ifs.model.Constraint; import org.cpsolver.ifs.model.ConstraintListener; @@ -34,7 +36,7 @@ * License along with this library; if not see * http://www.gnu.org/licenses/. */ -public class TimStudent extends ConstraintWithContext { +public class TimStudent extends ConstraintWithContext implements CanInheritContext { /** * Constructor @@ -132,6 +134,10 @@ public Context(Assignment assignment) { iTable[location.time()] = location; } } + + public Context(Assignment assignment, Context parent) { + iTable = Arrays.copyOf(parent.iTable, 45); + } @Override public void assigned(Assignment assignment, TimLocation location) { @@ -185,4 +191,9 @@ public int score(boolean oneDay, boolean lastTime, boolean threeMore) { public Context createAssignmentContext(Assignment assignment) { return new Context(assignment); } + + @Override + public Context inheritAssignmentContext(Assignment assignment, Context parentContext) { + return new Context(assignment, parentContext); + } } diff --git a/src/net/sf/cpsolver/itc/tim/neighbours/TimSwapMove.java b/src/net/sf/cpsolver/itc/tim/neighbours/TimSwapMove.java index 7061d44..34decc1 100644 --- a/src/net/sf/cpsolver/itc/tim/neighbours/TimSwapMove.java +++ b/src/net/sf/cpsolver/itc/tim/neighbours/TimSwapMove.java @@ -2,12 +2,13 @@ import org.cpsolver.ifs.assignment.Assignment; import org.cpsolver.ifs.heuristics.NeighbourSelection; +import org.cpsolver.ifs.model.LazySwap; import org.cpsolver.ifs.model.Neighbour; import org.cpsolver.ifs.solution.Solution; import org.cpsolver.ifs.solver.Solver; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.ifs.util.ToolBox; -import net.sf.cpsolver.itc.heuristics.neighbour.ItcLazySwap; + import net.sf.cpsolver.itc.heuristics.neighbour.ItcSimpleNeighbour; import net.sf.cpsolver.itc.heuristics.search.ItcHillClimber.HillClimberSelection; import net.sf.cpsolver.itc.tim.model.TTComp02Model; @@ -92,7 +93,7 @@ public Neighbour selectNeighbour(Solution(assignment, + return new LazySwap( new TimLocation(event, time, room), new TimLocation(confEvt, location.time(), location.room())); } diff --git a/src/tim.properties b/src/tim.properties index 73b9a6f..9b7d5d8 100644 --- a/src/tim.properties +++ b/src/tim.properties @@ -49,4 +49,7 @@ Oscillation.UpdateInterval=5000 Oscillation.WeightMax=5000 #Other -General.SaveBestUnassigned=0 \ No newline at end of file +General.SaveBestUnassigned=0 + +ParallelSolver.SingleSolution=false +ParallelSolver.SingleSolutionNeighbourCheck=true \ No newline at end of file diff --git a/src/ttcomp02.properties b/src/ttcomp02.properties index 3f9b9ad..9a80185 100644 --- a/src/ttcomp02.properties +++ b/src/ttcomp02.properties @@ -45,4 +45,7 @@ SimulatedAnnealing.ReheatLengthCoef=7 Extensions.Classes=org.cpsolver.ifs.extension.ConflictStatistics #Other -General.SaveBestUnassigned=0 \ No newline at end of file +General.SaveBestUnassigned=0 + +ParallelSolver.SingleSolution=false +ParallelSolver.SingleSolutionNeighbourCheck=true \ No newline at end of file