001package org.cpsolver.coursett.criteria; 002 003import java.util.Collection; 004import java.util.HashMap; 005import java.util.HashSet; 006import java.util.Map; 007import java.util.Set; 008 009import org.cpsolver.coursett.constraint.FlexibleConstraint; 010import org.cpsolver.coursett.constraint.FlexibleConstraint.FlexibleConstraintType; 011import org.cpsolver.coursett.model.Lecture; 012import org.cpsolver.coursett.model.Placement; 013import org.cpsolver.coursett.model.TimetableModel; 014import org.cpsolver.ifs.assignment.Assignment; 015import org.cpsolver.ifs.util.DataProperties; 016 017 018/** 019 * The class encapsulates various flexible constraints concerning compact timetables of 020 * instructors. 021 * 022 * <br> 023 * @version CourseTT 1.3 (University Course Timetabling)<br> 024 * Copyright (C) 2012 Matej Lukac<br> 025 * <br> 026 * This library is free software; you can redistribute it and/or modify 027 * it under the terms of the GNU Lesser General Public License as 028 * published by the Free Software Foundation; either version 3 of the 029 * License, or (at your option) any later version. <br> 030 * <br> 031 * This library is distributed in the hope that it will be useful, but 032 * WITHOUT ANY WARRANTY; without even the implied warranty of 033 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 034 * Lesser General Public License for more details. <br> 035 * <br> 036 * You should have received a copy of the GNU Lesser General Public 037 * License along with this library; if not see 038 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 039 */ 040public class FlexibleConstraintCriterion extends TimetablingCriterion { 041 042 private boolean iDebug; 043 044 public FlexibleConstraintCriterion(){ 045 setValueUpdateType(ValueUpdateType.NoUpdate); 046 } 047 048 @Override 049 public void configure(DataProperties properties) { 050 super.configure(properties); 051 iWeight = properties.getPropertyDouble("FlexibleConstraint.Weight", 1.0d); 052 iDebug = properties.getPropertyBoolean("FlexibleConstraint.Debug", true); 053 } 054 055 @Override 056 public String getPlacementSelectionWeightName() { 057 return "Placement.FlexibleConstrPreferenceWeight"; 058 } 059 060 @Override 061 public void getInfo(Assignment<Lecture, Placement> assignment, Map<String, String> info) { 062 TimetableModel m = (TimetableModel)getModel(); 063 if (m.getFlexibleConstraints().isEmpty()) return; 064 065 for (FlexibleConstraintType type: FlexibleConstraintType.values()) { 066 StringBuilder debug = null; 067 int violated = 0, constraints = 0; 068 double penalty = 0.0; 069 070 for (FlexibleConstraint c : m.getFlexibleConstraints()) { 071 if (type.equals(c.getType())) { 072 constraints ++; 073 if (c.getContext(assignment).getPreference() > 0) { 074 violated++; 075 penalty += c.getContext(assignment).getPreference(); 076 if (iDebug) { 077 if (debug == null) 078 debug = new StringBuilder(c.getOwner() + " (" + sDoubleFormat.format(c.getNrViolations(assignment, new HashSet<Placement>(), null)) + ")"); 079 else 080 debug.append("; " + c.getOwner() + " (" + sDoubleFormat.format(c.getNrViolations(assignment, new HashSet<Placement>(), null)) + ")"); 081 } 082 } 083 } 084 } 085 086 if (constraints > 0) { 087 info.put(type.getName() + " Constraints", getPerc(violated, 0, constraints) + "%" + (violated == 0 ? "" : " (" + (constraints - violated) + "/" + constraints + ", average penalty:" + sDoubleFormat.format(penalty/violated) + ")")); 088 if (iDebug && violated > 0) info.put(type.getName() + " Violations", debug.toString()); 089 } 090 } 091 } 092 093 @Override 094 public void getInfo(Assignment<Lecture, Placement> assignment, Map<String, String> info, Collection<Lecture> variables) { 095 for (FlexibleConstraintType type: FlexibleConstraintType.values()) { 096 097 Set<FlexibleConstraint> constraints = new HashSet<FlexibleConstraint>(); 098 for (Lecture lecture : variables) { 099 for (FlexibleConstraint c : lecture.getFlexibleGroupConstraints()) { 100 if (type.equals(c.getType())) constraints.add(c); 101 } 102 } 103 104 if (!constraints.isEmpty()) { 105 int violated = 0; 106 double penalty = 0.0; 107 StringBuilder debug = null; 108 for (FlexibleConstraint c : constraints) { 109 if (c.getContext(assignment).getPreference() > 0) { 110 violated++; 111 penalty += c.getContext(assignment).getPreference(); 112 if (iDebug) { 113 if (debug == null) 114 debug = new StringBuilder(c.getOwner()); 115 else 116 debug.append("; " + c.getOwner()); 117 } 118 } 119 } 120 info.put(type.getName() + " Constraints", getPerc(violated, 0, constraints.size()) + "%" + (violated == 0 ? "" : " (" + (constraints.size() - violated) + " of " + constraints.size() + ", average penalty:" + sDoubleFormat.format(penalty/violated) + ")")); 121 if (iDebug && violated > 0) info.put(type.getName() + " Violations", debug.toString()); 122 } 123 } 124 } 125 126 @Override 127 public double getValue(Assignment<Lecture, Placement> assignment, Collection<Lecture> variables) { 128 Set<FlexibleConstraint> flexibleConstraints = new HashSet<FlexibleConstraint>(); 129 for (Lecture lecture: variables){ 130 flexibleConstraints.addAll(lecture.getFlexibleGroupConstraints()); 131 } 132 int ret = 0; 133 for (FlexibleConstraint gc: flexibleConstraints){ 134 ret += gc.getContext(assignment).getPreference(); 135 } 136 return ret; 137 } 138 139 @Override 140 public double getValue(Assignment<Lecture, Placement> assignment, Placement value, Set<Placement> conflicts) { 141 HashMap<Lecture, Placement> assignments = new HashMap<Lecture, Placement>(); 142 assignments.put(value.variable(), value); 143 144 double ret = 0.0; 145 for (FlexibleConstraint gc : value.variable().getFlexibleGroupConstraints()) 146 ret += gc.getCurrentPreference(assignment, conflicts, assignments); 147 148 assignments.put(value.variable(), null); 149 for (FlexibleConstraint gc : value.variable().getFlexibleGroupConstraints()) 150 ret -= gc.getCurrentPreference(assignment, conflicts, assignments); 151 152 return ret; 153 } 154}