001package org.cpsolver.exam.criteria; 002 003import java.util.Map; 004import java.util.Set; 005 006import org.cpsolver.exam.model.Exam; 007import org.cpsolver.exam.model.ExamPlacement; 008import org.cpsolver.ifs.assignment.Assignment; 009import org.cpsolver.ifs.util.DataProperties; 010 011 012/** 013 * Rotation penalty. I.e., an exam that has been in later period last times tries 014 * to be in an earlier period. 015 * <br><br> 016 * A weight for exam rotation penalty can be set by problem property 017 * Exams.RotationWeight, or in the input xml file, property examRotationWeight. 018 * 019 * 020 * <br> 021 * 022 * @version ExamTT 1.3 (Examination Timetabling)<br> 023 * Copyright (C) 2008 - 2014 Tomáš Müller<br> 024 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 025 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 026 * <br> 027 * This library is free software; you can redistribute it and/or modify 028 * it under the terms of the GNU Lesser General Public License as 029 * published by the Free Software Foundation; either version 3 of the 030 * License, or (at your option) any later version. <br> 031 * <br> 032 * This library is distributed in the hope that it will be useful, but 033 * WITHOUT ANY WARRANTY; without even the implied warranty of 034 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 035 * Lesser General Public License for more details. <br> 036 * <br> 037 * You should have received a copy of the GNU Lesser General Public 038 * License along with this library; if not see 039 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 040 */ 041public class ExamRotationPenalty extends ExamCriterion { 042 043 @Override 044 public ValueContext createAssignmentContext(Assignment<Exam, ExamPlacement> assignment) { 045 return new RotationContext(assignment); 046 } 047 048 @Override 049 public String getWeightName() { 050 return "Exams.RotationWeight"; 051 } 052 053 @Override 054 public String getXmlWeightName() { 055 return "examRotationWeight"; 056 } 057 058 @Override 059 public double getWeightDefault(DataProperties config) { 060 return 0.001; 061 } 062 063 @Override 064 public double getValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value, Set<ExamPlacement> conflicts) { 065 if (value.variable().getAveragePeriod() < 0) return 0; 066 return (1 + value.getPeriod().getIndex()) * (1 + value.variable().getAveragePeriod()); 067 } 068 069 public int nrAssignedExamsWithAvgPeriod(Assignment<Exam, ExamPlacement> assignment) { 070 return ((RotationContext)getContext(assignment)).nrAssignedExamsWithAvgPeriod(); 071 } 072 073 public double averagePeriod(Assignment<Exam, ExamPlacement> assignment) { 074 return ((RotationContext)getContext(assignment)).averagePeriod(); 075 } 076 077 @Override 078 public void getInfo(Assignment<Exam, ExamPlacement> assignment, Map<String, String> info) { 079 if (getValue(assignment) != 0.0) { 080 info.put(getName(), sDoubleFormat.format(Math.sqrt(getValue(assignment) / nrAssignedExamsWithAvgPeriod(assignment)) - 1)); 081 } 082 } 083 084 @Override 085 public String toString(Assignment<Exam, ExamPlacement> assignment) { 086 return "@P:" + sDoubleFormat.format((Math.sqrt(getValue(assignment) / nrAssignedExamsWithAvgPeriod(assignment)) - 1)); 087 } 088 089 protected class RotationContext extends ValueContext { 090 private int iAssignedExamsWithAvgPeriod = 0; 091 private double iAveragePeriod = 0.0; 092 093 public RotationContext(Assignment<Exam, ExamPlacement> assignment) { 094 super(assignment); 095 for (Exam exam: getModel().variables()) 096 if (exam.getAveragePeriod() > 0) { 097 ExamPlacement placement = assignment.getValue(exam); 098 if (placement != null) { 099 iAssignedExamsWithAvgPeriod ++; 100 iAveragePeriod += exam.getAveragePeriod(); 101 } 102 } 103 } 104 105 @Override 106 public void assigned(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) { 107 super.assigned(assignment, value); 108 if (value.variable().getAveragePeriod() >= 0) { 109 iAssignedExamsWithAvgPeriod ++; 110 iAveragePeriod += value.variable().getAveragePeriod(); 111 } 112 } 113 114 @Override 115 public void unassigned(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) { 116 super.unassigned(assignment, value); 117 if (value.variable().getAveragePeriod() >= 0) { 118 iAssignedExamsWithAvgPeriod --; 119 iAveragePeriod -= value.variable().getAveragePeriod(); 120 } 121 } 122 123 public int nrAssignedExamsWithAvgPeriod() { 124 return iAssignedExamsWithAvgPeriod; 125 } 126 127 public double averagePeriod() { 128 return iAveragePeriod / iAssignedExamsWithAvgPeriod; 129 } 130 } 131}