001package org.cpsolver.coursett.constraint; 002 003import java.util.Collection; 004import java.util.Comparator; 005import java.util.List; 006import java.util.Set; 007import java.util.TreeSet; 008 009import org.cpsolver.coursett.model.Lecture; 010import org.cpsolver.coursett.model.Placement; 011import org.cpsolver.ifs.assignment.Assignment; 012import org.cpsolver.ifs.model.Constraint; 013 014 015/** 016 * Class limit constraint. 017 * 018 * @version CourseTT 1.3 (University Course Timetabling)<br> 019 * Copyright (C) 2006 - 2014 Tomáš Müller<br> 020 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 021 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 022 * <br> 023 * This library is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU Lesser General Public License as 025 * published by the Free Software Foundation; either version 3 of the 026 * License, or (at your option) any later version. <br> 027 * <br> 028 * This library is distributed in the hope that it will be useful, but 029 * WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 031 * Lesser General Public License for more details. <br> 032 * <br> 033 * You should have received a copy of the GNU Lesser General Public 034 * License along with this library; if not see 035 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 036 */ 037 038public class ClassLimitConstraint extends Constraint<Lecture, Placement> { 039 private int iClassLimit = 0; 040 private Lecture iParent = null; 041 private String iName = null; 042 boolean iEnabled = true; 043 044 private int iClassLimitDelta = 0; 045 046 public ClassLimitConstraint(int classLimit, String name) { 047 iClassLimit = classLimit; 048 iName = name; 049 } 050 051 public ClassLimitConstraint(Lecture parent, String name) { 052 iParent = parent; 053 iName = name; 054 } 055 056 public int getClassLimitDelta() { 057 return iClassLimitDelta; 058 } 059 060 public void setClassLimitDelta(int classLimitDelta) { 061 iClassLimitDelta = classLimitDelta; 062 } 063 064 public int classLimit() { 065 return (iParent == null ? iClassLimit + iClassLimitDelta : iParent.minClassLimit() + iClassLimitDelta); 066 } 067 068 public Lecture getParentLecture() { 069 return iParent; 070 } 071 072 public int currentClassLimit(Assignment<Lecture, Placement> assignment, Placement value, Set<Placement> conflicts) { 073 int limit = 0; 074 for (Lecture lecture : variables()) { 075 limit += lecture.classLimit(assignment, value, conflicts); 076 } 077 return limit; 078 } 079 080 @Override 081 public void computeConflicts(Assignment<Lecture, Placement> assignment, Placement value, Set<Placement> conflicts) { 082 if (!iEnabled) 083 return; 084 int currentLimit = currentClassLimit(assignment, value, conflicts); 085 int classLimit = classLimit(); 086 if (currentLimit < classLimit) { 087 // System.out.println(getName()+"> "+currentLimit+"<"+classLimit+" ("+value+")"); 088 TreeSet<Placement> adepts = new TreeSet<Placement>(new ClassLimitComparator()); 089 computeAdepts(assignment, adepts, variables(), value, conflicts); 090 addParentAdepts(assignment, adepts, iParent, value, conflicts); 091 // System.out.println(" -- found "+adepts.size()+" adepts"); 092 for (Placement adept : adepts) { 093 // System.out.println(" -- selected "+adept); 094 conflicts.add(adept); 095 currentLimit = currentClassLimit(assignment, value, conflicts); 096 // System.out.println(" -- new current limit "+currentLimit); 097 if (currentLimit >= classLimit) 098 break; 099 } 100 // System.out.println(" -- done (currentLimit="+currentLimit+", classLimit="+classLimit+")"); 101 } 102 103 if (currentLimit < classLimit) 104 conflicts.add(value); 105 106 if (iParent != null && iParent.getClassLimitConstraint() != null) 107 iParent.getClassLimitConstraint().computeConflicts(assignment, value, conflicts); 108 } 109 110 public void computeAdepts(Assignment<Lecture, Placement> assignment, Collection<Placement> adepts, List<Lecture> variables, Placement value, 111 Set<Placement> conflicts) { 112 for (Lecture lecture : variables) { 113 if (lecture.isCommitted()) continue; 114 Placement placement = assignment.getValue(lecture); 115 if (placement != null && !placement.equals(value) && !conflicts.contains(placement)) { 116 adepts.add(placement); 117 } 118 if (lecture.hasAnyChildren()) { 119 for (Long subpartId: lecture.getChildrenSubpartIds()) { 120 computeAdepts(assignment, adepts, lecture.getChildren(subpartId), value, conflicts); 121 } 122 } 123 124 } 125 } 126 127 public void addParentAdepts(Assignment<Lecture, Placement> assignment, Collection<Placement> adepts, Lecture parent, Placement value, Set<Placement> conflicts) { 128 if (parent == null || parent.isCommitted() || parent.minClassLimit() == parent.maxClassLimit()) 129 return; 130 Placement placement = assignment.getValue(parent); 131 if (placement != null && !placement.equals(value) && !conflicts.contains(placement)) { 132 adepts.add(placement); 133 } 134 addParentAdepts(assignment, adepts, parent.getParent(), value, conflicts); 135 } 136 137 @Override 138 public boolean inConflict(Assignment<Lecture, Placement> assignment, Placement value) { 139 if (!iEnabled) 140 return false; 141 int currentLimit = currentClassLimit(assignment, value, null); 142 int classLimit = classLimit(); 143 if (currentLimit < classLimit) 144 return true; 145 146 if (iParent != null && iParent.getClassLimitConstraint() != null) 147 return iParent.getClassLimitConstraint().inConflict(assignment, value); 148 149 return false; 150 } 151 152 @Override 153 public String getName() { 154 return iName; 155 } 156 157 private static class ClassLimitComparator implements Comparator<Placement> { 158 @Override 159 public int compare(Placement p1, Placement p2) { 160 Lecture l1 = p1.variable(); 161 Lecture l2 = p2.variable(); 162 int cl1 = Math.min(l1.maxClassLimit(), (int) Math.floor(p1.getRoomSize() / l1.roomToLimitRatio())); 163 int cl2 = Math.min(l2.maxClassLimit(), (int) Math.floor(p2.getRoomSize() / l2.roomToLimitRatio())); 164 int cmp = -Double.compare(l1.maxAchievableClassLimit() - cl1, l2.maxAchievableClassLimit() - cl2); 165 if (cmp != 0) 166 return cmp; 167 return l1.getClassId().compareTo(l2.getClassId()); 168 } 169 } 170 171 public void setEnabled(boolean enabled) { 172 iEnabled = enabled; 173 } 174 175 public boolean isEnabled() { 176 return iEnabled; 177 } 178 179 @Override 180 public String toString() { 181 return "Class-limit " + getName(); 182 } 183 184}