All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cpsolver.coursett.model.TimetableModel Maven / Gradle / Ivy

package org.cpsolver.coursett.model;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.cpsolver.coursett.Constants;
import org.cpsolver.coursett.constraint.ClassLimitConstraint;
import org.cpsolver.coursett.constraint.DepartmentSpreadConstraint;
import org.cpsolver.coursett.constraint.FlexibleConstraint;
import org.cpsolver.coursett.constraint.GroupConstraint;
import org.cpsolver.coursett.constraint.InstructorConstraint;
import org.cpsolver.coursett.constraint.JenrlConstraint;
import org.cpsolver.coursett.constraint.RoomConstraint;
import org.cpsolver.coursett.constraint.SpreadConstraint;
import org.cpsolver.coursett.criteria.BackToBackInstructorPreferences;
import org.cpsolver.coursett.criteria.BrokenTimePatterns;
import org.cpsolver.coursett.criteria.DepartmentBalancingPenalty;
import org.cpsolver.coursett.criteria.DistributionPreferences;
import org.cpsolver.coursett.criteria.FlexibleConstraintCriterion;
import org.cpsolver.coursett.criteria.Perturbations;
import org.cpsolver.coursett.criteria.RoomPreferences;
import org.cpsolver.coursett.criteria.RoomViolations;
import org.cpsolver.coursett.criteria.SameSubpartBalancingPenalty;
import org.cpsolver.coursett.criteria.StudentCommittedConflict;
import org.cpsolver.coursett.criteria.StudentConflict;
import org.cpsolver.coursett.criteria.StudentDistanceConflict;
import org.cpsolver.coursett.criteria.StudentHardConflict;
import org.cpsolver.coursett.criteria.StudentOverlapConflict;
import org.cpsolver.coursett.criteria.StudentWorkdayConflict;
import org.cpsolver.coursett.criteria.TimePreferences;
import org.cpsolver.coursett.criteria.TimeViolations;
import org.cpsolver.coursett.criteria.TooBigRooms;
import org.cpsolver.coursett.criteria.UselessHalfHours;
import org.cpsolver.coursett.criteria.additional.InstructorConflict;
import org.cpsolver.coursett.criteria.placement.DeltaTimePreference;
import org.cpsolver.coursett.criteria.placement.HardConflicts;
import org.cpsolver.coursett.criteria.placement.PotentialHardConflicts;
import org.cpsolver.coursett.criteria.placement.WeightedHardConflicts;
import org.cpsolver.ifs.assignment.Assignment;
import org.cpsolver.ifs.constant.ConstantModel;
import org.cpsolver.ifs.criteria.Criterion;
import org.cpsolver.ifs.model.Constraint;
import org.cpsolver.ifs.model.GlobalConstraint;
import org.cpsolver.ifs.model.InfoProvider;
import org.cpsolver.ifs.model.WeakeningConstraint;
import org.cpsolver.ifs.solution.Solution;
import org.cpsolver.ifs.termination.TerminationCondition;
import org.cpsolver.ifs.util.DataProperties;
import org.cpsolver.ifs.util.DistanceMetric;


/**
 * Timetable model.
 * 
 * @author  Tomas Muller
 * @version CourseTT 1.3 (University Course Timetabling)
* Copyright (C) 2006 - 2014 Tomas Muller
* [email protected]
* 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 TimetableModel extends ConstantModel { private static org.apache.logging.log4j.Logger sLogger = org.apache.logging.log4j.LogManager.getLogger(TimetableModel.class); private static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00", new java.text.DecimalFormatSymbols(Locale.US)); private List iInstructorConstraints = new ArrayList(); private List iJenrlConstraints = new ArrayList(); private List iRoomConstraints = new ArrayList(); private List iDepartmentSpreadConstraints = new ArrayList(); private List iSpreadConstraints = new ArrayList(); private List iGroupConstraints = new ArrayList(); private List iClassLimitConstraints = new ArrayList(); private List iFlexibleConstraints = new ArrayList(); private DataProperties iProperties = null; private int iYear = -1; private List iWeeks = null; private boolean iOnFlySectioning = false; private int iStudentWorkDayLimit = -1; private boolean iAllowBreakHard = false; private HashSet iAllStudents = new HashSet(); private DistanceMetric iDistanceMetric = null; private StudentSectioning iStudentSectioning = null; private List iStudentGroups = new ArrayList(); private boolean iUseCriteria = true; private List iStudentConflictCriteria = null; @SuppressWarnings("unchecked") public TimetableModel(DataProperties properties) { super(); iProperties = properties; iDistanceMetric = new DistanceMetric(properties); if (properties.getPropertyBoolean("OnFlySectioning.Enabled", false)) { addModelListener(new OnFlySectioning(this)); iOnFlySectioning = true; } iStudentWorkDayLimit = properties.getPropertyInt("StudentConflict.WorkDayLimit", -1); iAllowBreakHard = properties.getPropertyBoolean("General.AllowBreakHard", false); String criteria = properties.getProperty("General.Criteria", // Objectives StudentConflict.class.getName() + ";" + StudentDistanceConflict.class.getName() + ";" + StudentHardConflict.class.getName() + ";" + StudentCommittedConflict.class.getName() + ";" + StudentOverlapConflict.class.getName() + ";" + UselessHalfHours.class.getName() + ";" + BrokenTimePatterns.class.getName() + ";" + TooBigRooms.class.getName() + ";" + TimePreferences.class.getName() + ";" + RoomPreferences.class.getName() + ";" + DistributionPreferences.class.getName() + ";" + SameSubpartBalancingPenalty.class.getName() + ";" + DepartmentBalancingPenalty.class.getName() + ";" + BackToBackInstructorPreferences.class.getName() + ";" + Perturbations.class.getName() + ";" + // Additional placement selection criteria // AssignmentCount.class.getName() + ";" + DeltaTimePreference.class.getName() + ";" + HardConflicts.class.getName() + ";" + PotentialHardConflicts.class.getName() + ";" + FlexibleConstraintCriterion.class.getName() + ";" + WeightedHardConflicts.class.getName()); if (iStudentWorkDayLimit > 0) criteria += ";" + StudentWorkdayConflict.class.getName(); // Interactive mode -- count time / room violations if (properties.getPropertyBoolean("General.InteractiveMode", false)) criteria += ";" + TimeViolations.class.getName() + ";" + RoomViolations.class.getName(); else if (properties.getPropertyBoolean("General.AllowProhibitedRooms", false)) { criteria += ";" + RoomViolations.class.getName(); iAllowBreakHard = true; } // Additional (custom) criteria criteria += ";" + properties.getProperty("General.AdditionalCriteria", ""); for (String criterion: criteria.split("\\;")) { if (criterion == null || criterion.isEmpty()) continue; try { Class> clazz = (Class>)Class.forName(criterion); Criterion c = clazz.newInstance(); c.configure(properties); addCriterion(c); } catch (Exception e) { sLogger.error("Unable to use " + criterion + ": " + e.getMessage()); } } if (properties.getPropertyBoolean("General.SoftInstructorConstraints", false)) { InstructorConflict ic = new InstructorConflict(); ic.configure(properties); addCriterion(ic); } try { String studentSectioningClassName = properties.getProperty("StudentSectioning.Class", DefaultStudentSectioning.class.getName()); Class studentSectioningClass = Class.forName(studentSectioningClassName); iStudentSectioning = (StudentSectioning)studentSectioningClass.getConstructor(TimetableModel.class).newInstance(this); } catch (Exception e) { sLogger.error("Failed to load custom student sectioning class: " + e.getMessage()); iStudentSectioning = new DefaultStudentSectioning(this); } if (iStudentSectioning instanceof InfoProvider) { getInfoProviders().add((InfoProvider)iStudentSectioning); } String constraints = properties.getProperty("General.GlobalConstraints", ""); for (String constraint: constraints.split("\\;")) { if (constraint == null || constraint.isEmpty()) continue; try { Class> clazz = (Class>)Class.forName(constraint); GlobalConstraint c = clazz.newInstance(); addGlobalConstraint(c); } catch (Exception e) { sLogger.error("Unable to use " + constraint + ": " + e.getMessage()); } } iUseCriteria = properties.getPropertyBoolean("SctSectioning.UseCriteria", true); } public DistanceMetric getDistanceMetric() { return iDistanceMetric; } public int getStudentWorkDayLimit() { return iStudentWorkDayLimit; } /** * Returns interface to the student sectioning functions needed during course timetabling. * Defaults to an instance of {@link DefaultStudentSectioning}, can be changed using the StudentSectioning.Class parameter. * @return student sectioning */ public StudentSectioning getStudentSectioning() { return iStudentSectioning; } public DataProperties getProperties() { return iProperties; } /** * Student final sectioning (switching students between sections of the same * class in order to minimize overall number of student conflicts) * @param assignment current assignment * @param termination optional termination condition */ public void switchStudents(Assignment assignment, TerminationCondition termination) { getStudentSectioning().switchStudents(new Solution(this, assignment), termination); } /** * Student final sectioning (switching students between sections of the same * class in order to minimize overall number of student conflicts) * @param assignment current assignment */ public void switchStudents(Assignment assignment) { getStudentSectioning().switchStudents(new Solution(this, assignment), null); } public Map getBounds(Assignment assignment) { Map ret = new HashMap(); ret.put("Room preferences min", "" + getCriterion(RoomPreferences.class).getBounds(assignment)[0]); ret.put("Room preferences max", "" + getCriterion(RoomPreferences.class).getBounds(assignment)[1]); ret.put("Time preferences min", "" + getCriterion(TimePreferences.class).getBounds(assignment)[0]); ret.put("Time preferences max", "" + getCriterion(TimePreferences.class).getBounds(assignment)[1]); ret.put("Distribution preferences min", "" + getCriterion(DistributionPreferences.class).getBounds(assignment)[0]); ret.put("Distribution preferences max", "" + getCriterion(DistributionPreferences.class).getBounds(assignment)[1]); if (getProperties().getPropertyBoolean("General.UseDistanceConstraints", false)) { ret.put("Back-to-back instructor preferences max", "" + getCriterion(BackToBackInstructorPreferences.class).getBounds(assignment)[1]); } ret.put("Too big rooms max", "" + getCriterion(TooBigRooms.class).getBounds(assignment)[0]); ret.put("Useless half-hours", "" + getCriterion(UselessHalfHours.class).getBounds(assignment)[0]); return ret; } /** Global info */ @Override public Map getInfo(Assignment assignment) { Map ret = super.getInfo(assignment); ret.put("Memory usage", getMem()); Criterion rp = getCriterion(RoomPreferences.class); Criterion rv = getCriterion(RoomViolations.class); ret.put("Room preferences", getPerc(rp.getValue(assignment), rp.getBounds(assignment)[0], rp.getBounds(assignment)[1]) + "% (" + Math.round(rp.getValue(assignment)) + ")" + (rv != null && rv.getValue(assignment) >= 0.5 ? " [hard:" + Math.round(rv.getValue(assignment)) + "]" : "")); Criterion tp = getCriterion(TimePreferences.class); Criterion tv = getCriterion(TimeViolations.class); ret.put("Time preferences", getPerc(tp.getValue(assignment), tp.getBounds(assignment)[0], tp.getBounds(assignment)[1]) + "% (" + sDoubleFormat.format(tp.getValue(assignment)) + ")" + (tv != null && tv.getValue(assignment) >= 0.5 ? " [hard:" + Math.round(tv.getValue(assignment)) + "]" : "")); Criterion dp = getCriterion(DistributionPreferences.class); ret.put("Distribution preferences", getPerc(dp.getValue(assignment), dp.getBounds(assignment)[0], dp.getBounds(assignment)[1]) + "% (" + sDoubleFormat.format(dp.getValue(assignment)) + ")"); Criterion sc = getCriterion(StudentConflict.class); Criterion shc = getCriterion(StudentHardConflict.class); Criterion sdc = getCriterion(StudentDistanceConflict.class); Criterion scc = getCriterion(StudentCommittedConflict.class); ret.put("Student conflicts", Math.round(scc.getValue(assignment) + sc.getValue(assignment)) + " [committed:" + Math.round(scc.getValue(assignment)) + ", distance:" + Math.round(sdc.getValue(assignment)) + ", hard:" + Math.round(shc.getValue(assignment)) + "]"); if (!getSpreadConstraints().isEmpty()) { Criterion ip = getCriterion(BackToBackInstructorPreferences.class); ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(assignment), ip.getBounds(assignment)[0], ip.getBounds(assignment)[1]) + "% (" + Math.round(ip.getValue(assignment)) + ")"); } if (!getDepartmentSpreadConstraints().isEmpty()) { Criterion dbp = getCriterion(DepartmentBalancingPenalty.class); ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue(assignment))); } Criterion sbp = getCriterion(SameSubpartBalancingPenalty.class); ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue(assignment))); Criterion tbr = getCriterion(TooBigRooms.class); ret.put("Too big rooms", getPercRev(tbr.getValue(assignment), tbr.getBounds(assignment)[1], tbr.getBounds(assignment)[0]) + "% (" + Math.round(tbr.getValue(assignment)) + ")"); Criterion uh = getCriterion(UselessHalfHours.class); Criterion bt = getCriterion(BrokenTimePatterns.class); ret.put("Useless half-hours", getPercRev(uh.getValue(assignment) + bt.getValue(assignment), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds(assignment)[0]) + "% (" + Math.round(uh.getValue(assignment)) + " + " + Math.round(bt.getValue(assignment)) + ")"); return ret; } @Override public Map getInfo(Assignment assignment, Collection variables) { Map ret = super.getInfo(assignment, variables); ret.put("Memory usage", getMem()); Criterion rp = getCriterion(RoomPreferences.class); ret.put("Room preferences", getPerc(rp.getValue(assignment, variables), rp.getBounds(assignment, variables)[0], rp.getBounds(assignment, variables)[1]) + "% (" + Math.round(rp.getValue(assignment, variables)) + ")"); Criterion tp = getCriterion(TimePreferences.class); ret.put("Time preferences", getPerc(tp.getValue(assignment, variables), tp.getBounds(assignment, variables)[0], tp.getBounds(assignment, variables)[1]) + "% (" + sDoubleFormat.format(tp.getValue(assignment, variables)) + ")"); Criterion dp = getCriterion(DistributionPreferences.class); ret.put("Distribution preferences", getPerc(dp.getValue(assignment, variables), dp.getBounds(assignment, variables)[0], dp.getBounds(assignment, variables)[1]) + "% (" + sDoubleFormat.format(dp.getValue(assignment, variables)) + ")"); Criterion sc = getCriterion(StudentConflict.class); Criterion shc = getCriterion(StudentHardConflict.class); Criterion sdc = getCriterion(StudentDistanceConflict.class); Criterion scc = getCriterion(StudentCommittedConflict.class); ret.put("Student conflicts", Math.round(scc.getValue(assignment, variables) + sc.getValue(assignment, variables)) + " [committed:" + Math.round(scc.getValue(assignment, variables)) + ", distance:" + Math.round(sdc.getValue(assignment, variables)) + ", hard:" + Math.round(shc.getValue(assignment, variables)) + "]"); if (!getSpreadConstraints().isEmpty()) { Criterion ip = getCriterion(BackToBackInstructorPreferences.class); ret.put("Back-to-back instructor preferences", getPerc(ip.getValue(assignment, variables), ip.getBounds(assignment, variables)[0], ip.getBounds(assignment, variables)[1]) + "% (" + Math.round(ip.getValue(assignment, variables)) + ")"); } if (!getDepartmentSpreadConstraints().isEmpty()) { Criterion dbp = getCriterion(DepartmentBalancingPenalty.class); ret.put("Department balancing penalty", sDoubleFormat.format(dbp.getValue(assignment, variables))); } Criterion sbp = getCriterion(SameSubpartBalancingPenalty.class); ret.put("Same subpart balancing penalty", sDoubleFormat.format(sbp.getValue(assignment, variables))); Criterion tbr = getCriterion(TooBigRooms.class); ret.put("Too big rooms", getPercRev(tbr.getValue(assignment, variables), tbr.getBounds(assignment, variables)[1], tbr.getBounds(assignment, variables)[0]) + "% (" + Math.round(tbr.getValue(assignment, variables)) + ")"); Criterion uh = getCriterion(UselessHalfHours.class); Criterion bt = getCriterion(BrokenTimePatterns.class); ret.put("Useless half-hours", getPercRev(uh.getValue(assignment, variables) + bt.getValue(assignment, variables), 0, Constants.sPreferenceLevelStronglyDiscouraged * bt.getBounds(assignment, variables)[0]) + "% (" + Math.round(uh.getValue(assignment, variables)) + " + " + Math.round(bt.getValue(assignment, variables)) + ")"); return ret; } @Override public void addConstraint(Constraint constraint) { super.addConstraint(constraint); if (constraint instanceof InstructorConstraint) { iInstructorConstraints.add((InstructorConstraint) constraint); } else if (constraint instanceof JenrlConstraint) { iJenrlConstraints.add((JenrlConstraint) constraint); } else if (constraint instanceof RoomConstraint) { iRoomConstraints.add((RoomConstraint) constraint); } else if (constraint instanceof DepartmentSpreadConstraint) { iDepartmentSpreadConstraints.add((DepartmentSpreadConstraint) constraint); } else if (constraint instanceof SpreadConstraint) { iSpreadConstraints.add((SpreadConstraint) constraint); } else if (constraint instanceof ClassLimitConstraint) { iClassLimitConstraints.add((ClassLimitConstraint) constraint); } else if (constraint instanceof GroupConstraint) { iGroupConstraints.add((GroupConstraint) constraint); } else if (constraint instanceof FlexibleConstraint) { iFlexibleConstraints.add((FlexibleConstraint) constraint); } } @Override public void removeConstraint(Constraint constraint) { super.removeConstraint(constraint); if (constraint instanceof InstructorConstraint) { iInstructorConstraints.remove(constraint); } else if (constraint instanceof JenrlConstraint) { iJenrlConstraints.remove(constraint); } else if (constraint instanceof RoomConstraint) { iRoomConstraints.remove(constraint); } else if (constraint instanceof DepartmentSpreadConstraint) { iDepartmentSpreadConstraints.remove(constraint); } else if (constraint instanceof SpreadConstraint) { iSpreadConstraints.remove(constraint); } else if (constraint instanceof ClassLimitConstraint) { iClassLimitConstraints.remove(constraint); } else if (constraint instanceof GroupConstraint) { iGroupConstraints.remove(constraint); } else if (constraint instanceof FlexibleConstraint) { iFlexibleConstraints.remove(constraint); } } /** The list of all instructor constraints * @return list of instructor constraints **/ public List getInstructorConstraints() { return iInstructorConstraints; } /** The list of all group constraints * @return list of group (distribution) constraints **/ public List getGroupConstraints() { return iGroupConstraints; } /** The list of all jenrl constraints * @return list of join enrollment constraints **/ public List getJenrlConstraints() { return iJenrlConstraints; } /** The list of all room constraints * @return list of room constraints **/ public List getRoomConstraints() { return iRoomConstraints; } /** The list of all departmental spread constraints * @return list of department spread constraints **/ public List getDepartmentSpreadConstraints() { return iDepartmentSpreadConstraints; } public List getSpreadConstraints() { return iSpreadConstraints; } public List getClassLimitConstraints() { return iClassLimitConstraints; } public List getFlexibleConstraints() { return iFlexibleConstraints; } @Override public double getTotalValue(Assignment assignment) { double ret = 0; for (Criterion criterion: getCriteria()) ret += criterion.getWeightedValue(assignment); return ret; } @Override public double getTotalValue(Assignment assignment, Collection variables) { double ret = 0; for (Criterion criterion: getCriteria()) ret += criterion.getWeightedValue(assignment, variables); return ret; } public int getYear() { return iYear; } public void setYear(int year) { iYear = year; } public Set getAllStudents() { return iAllStudents; } public void addStudent(Student student) { iAllStudents.add(student); } public void removeStudent(Student student) { iAllStudents.remove(student); } /** * Returns amount of allocated memory. * * @return amount of allocated memory to be written in the log */ public static synchronized String getMem() { Runtime rt = Runtime.getRuntime(); return sDoubleFormat.format(((double) (rt.totalMemory() - rt.freeMemory())) / 1048576) + "M"; } /** * Returns the set of conflicting variables with this value, if it is * assigned to its variable. Conflicts with constraints that implement * {@link WeakeningConstraint} are ignored. * @param assignment current assignment * @param value placement that is being considered * @return computed conflicting assignments */ public Set conflictValuesSkipWeakeningConstraints(Assignment assignment, Placement value) { Set conflictValues = new HashSet(); for (Constraint constraint : value.variable().hardConstraints()) { if (constraint instanceof WeakeningConstraint) continue; if (constraint instanceof GroupConstraint) ((GroupConstraint)constraint).computeConflictsNoForwardCheck(assignment, value, conflictValues); else constraint.computeConflicts(assignment, value, conflictValues); } for (GlobalConstraint constraint : globalConstraints()) { if (constraint instanceof WeakeningConstraint) continue; constraint.computeConflicts(assignment, value, conflictValues); } return conflictValues; } /** * The method creates date patterns (bitsets) which represent the weeks of a * semester. * * @return a list of BitSets which represents the weeks of a semester. */ public List getWeeks() { if (iWeeks == null) { String defaultDatePattern = getProperties().getProperty("DatePattern.CustomDatePattern", null); if (defaultDatePattern == null){ defaultDatePattern = getProperties().getProperty("DatePattern.Default"); } BitSet fullTerm = null; if (defaultDatePattern == null) { // Take the date pattern that is being used most often Map counter = new HashMap(); int max = 0; String name = null; Long id = null; for (Lecture lecture: variables()) { if (lecture.isCommitted()) continue; for (TimeLocation time: lecture.timeLocations()) { if (time.getWeekCode() != null && time.getDatePatternId() != null) { int count = 1; if (counter.containsKey(time.getDatePatternId())) count += counter.get(time.getDatePatternId()); counter.put(time.getDatePatternId(), count); if (count > max) { max = count; fullTerm = time.getWeekCode(); name = time.getDatePatternName(); id = time.getDatePatternId(); } } } } sLogger.info("Using date pattern " + name + " (id " + id + ") as the default."); } else { // Create default date pattern fullTerm = new BitSet(defaultDatePattern.length()); for (int i = 0; i < defaultDatePattern.length(); i++) { if (defaultDatePattern.charAt(i) == 49) { fullTerm.set(i); } } } if (fullTerm == null) return null; iWeeks = new ArrayList(); if (getProperties().getPropertyBoolean("DatePattern.ShiftWeeks", false)) { // Cut date pattern into weeks (each week takes 7 consecutive bits, starting on the next positive bit) for (int i = fullTerm.nextSetBit(0); i < fullTerm.length(); ) { if (!fullTerm.get(i)) { i++; continue; } BitSet w = new BitSet(i + 7); for (int j = 0; j < 7; j++) if (fullTerm.get(i + j)) w.set(i + j); iWeeks.add(w); i += 7; } } else { // Cut date pattern into weeks (each week takes 7 consecutive bits starting on the first bit of the default date pattern, no pauses between weeks) for (int i = fullTerm.nextSetBit(0); i < fullTerm.length(); ) { BitSet w = new BitSet(i + 7); for (int j = 0; j < 7; j++) if (fullTerm.get(i + j)) w.set(i + j); iWeeks.add(w); i += 7; } } } return iWeeks; } public List getStudentGroups() { return iStudentGroups; } public void addStudentGroup(StudentGroup group) { iStudentGroups.add(group); } Map> iBestEnrollment = null; @Override public void saveBest(Assignment assignment) { super.saveBest(assignment); if (iOnFlySectioning) { if (iBestEnrollment == null) iBestEnrollment = new HashMap>(); else iBestEnrollment.clear(); for (Student student: getAllStudents()) iBestEnrollment.put(student, new HashSet(student.getLectures())); } } /** * Increment {@link JenrlConstraint} between the given two classes by the given student */ protected void incJenrl(Assignment assignment, Student student, Lecture l1, Lecture l2) { if (l1.equals(l2)) return; JenrlConstraint jenrl = l1.jenrlConstraint(l2); if (jenrl == null) { jenrl = new JenrlConstraint(); jenrl.addVariable(l1); jenrl.addVariable(l2); addConstraint(jenrl); } jenrl.incJenrl(assignment, student); } /** * Decrement {@link JenrlConstraint} between the given two classes by the given student */ protected void decJenrl(Assignment assignment, Student student, Lecture l1, Lecture l2) { if (l1.equals(l2)) return; JenrlConstraint jenrl = l1.jenrlConstraint(l2); if (jenrl != null) { jenrl.decJenrl(assignment, student); } } @Override public void restoreBest(Assignment assignment) { if (iOnFlySectioning && iBestEnrollment != null) { // unassign changed classes for (Lecture lecture: variables()) { Placement placement = assignment.getValue(lecture); if (placement != null && !placement.equals(lecture.getBestAssignment())) assignment.unassign(0, lecture); } for (Map.Entry> entry: iBestEnrollment.entrySet()) { Student student = entry.getKey(); Set lectures = entry.getValue(); Set configs = new HashSet(); for (Lecture lecture: lectures) if (lecture.getConfiguration() != null) configs.add(lecture.getConfiguration()); // drop student from classes that are not in the best enrollment for (Lecture lecture: new ArrayList(student.getLectures())) { if (lectures.contains(lecture)) continue; // included in best for (Lecture other: student.getLectures()) decJenrl(assignment, student, lecture, other); lecture.removeStudent(assignment, student); student.removeLecture(lecture); if (lecture.getConfiguration() != null && !configs.contains(lecture.getConfiguration())) student.removeConfiguration(lecture.getConfiguration()); } // add student to classes that are in the best enrollment for (Lecture lecture: lectures) { if (student.getLectures().contains(lecture)) continue; // already in for (Lecture other: student.getLectures()) incJenrl(assignment, student, lecture, other); lecture.addStudent(assignment, student); student.addLecture(lecture); student.addConfiguration(lecture.getConfiguration()); } } // remove empty joint enrollments for (Iterator i = iJenrlConstraints.iterator(); i.hasNext(); ) { JenrlConstraint jenrl = i.next(); if (jenrl.getNrStudents() == 0) { jenrl.getContext(assignment).unassigned(assignment, null); Object[] vars = jenrl.variables().toArray(); for (int k = 0; k < vars.length; k++) jenrl.removeVariable((Lecture) vars[k]); i.remove(); } } for (Iterator> i = constraints().iterator(); i.hasNext(); ) { Constraint c = i.next(); if (c instanceof JenrlConstraint && ((JenrlConstraint)c).getNrStudents() == 0) { removeReference((JenrlConstraint)c); i.remove(); } } /* for (JenrlConstraint jenrl: new ArrayList(getJenrlConstraints())) { if (jenrl.getNrStudents() == 0) { jenrl.getContext(assignment).unassigned(assignment, null); Object[] vars = jenrl.variables().toArray(); for (int k = 0; k < vars.length; k++) jenrl.removeVariable((Lecture) vars[k]); removeConstraint(jenrl); } } */ } super.restoreBest(assignment); } public boolean isAllowBreakHard() { return iAllowBreakHard; } public boolean isOnFlySectioningEnabled() { return iOnFlySectioning; } public void setOnFlySectioningEnabled(boolean onFlySectioning) { iOnFlySectioning = onFlySectioning; } @Override public void addCriterion(Criterion criterion) { super.addCriterion(criterion); iStudentConflictCriteria = null; } @Override public void removeCriterion(Criterion criterion) { super.removeCriterion(criterion); iStudentConflictCriteria = null; } /** * List of student conflict criteria */ public List getStudentConflictCriteria() { if (!iUseCriteria) return null; if (iStudentConflictCriteria == null) { iStudentConflictCriteria = new ArrayList(); for (Criterion criterion: getCriteria()) if (criterion instanceof StudentConflict) iStudentConflictCriteria.add((StudentConflict)criterion); } return iStudentConflictCriteria; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy