    001package org.cpsolver.exam.criteria;
    002
    003import java.util.Collection;
    004import java.util.HashSet;
    005import java.util.Set;
    006
    007import org.cpsolver.exam.criteria.additional.DistributionViolation;
    008import org.cpsolver.exam.model.Exam;
    009import org.cpsolver.exam.model.ExamDistributionConstraint;
    010import org.cpsolver.exam.model.ExamModel;
    011import org.cpsolver.exam.model.ExamPlacement;
    012import org.cpsolver.ifs.assignment.Assignment;
    013import org.cpsolver.ifs.model.Model;
    014import org.cpsolver.ifs.util.DataProperties;
    015
    016
    017/**
    018 * Distribution penalty. I.e., sum weights of violated distribution
    019 * constraints.
    020 * <br><br>
    021 * A weight of violated distribution soft constraints (see
    022 * {@link ExamDistributionConstraint}) can be set by problem property
    023 * Exams.RoomDistributionWeight, or in the input xml file, property
    024 * roomDistributionWeight.
    025 *
    026 * <br>
    027 *
    028 * @author  Tomáš Müller
    029 * @version ExamTT 1.3 (Examination Timetabling)<br>
    030 *          Copyright (C) 2008 - 2014 Tomáš Müller<br>
    031 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    032 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    033 * <br>
    034 *          This library is free software; you can redistribute it and/or modify
    035 *          it under the terms of the GNU Lesser General Public License as
    036 *          published by the Free Software Foundation; either version 3 of the
    037 *          License, or (at your option) any later version. <br>
    038 * <br>
    039 *          This library is distributed in the hope that it will be useful, but
    040 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    041 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    042 *          Lesser General Public License for more details. <br>
    043 * <br>
    044 *          You should have received a copy of the GNU Lesser General Public
    045 *          License along with this library; if not see
    046 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
    047 */
    048public class DistributionPenalty extends ExamCriterion {
    049    protected Integer iSoftDistributions = null;
    050
    051    public DistributionPenalty() {
    052        setValueUpdateType(ValueUpdateType.NoUpdate);
    053    }
    054
    055    @Override
    056    public void setModel(Model<Exam, ExamPlacement> model) {
    057        super.setModel(model);
    058        iSoftDistributions = ((ExamModel)model).getProperties().getPropertyInteger("Exam.SoftDistributions", null);
    059        if (iSoftDistributions != null && model.getCriterion(DistributionViolation.class) == null) {
    060            DistributionViolation dv = new DistributionViolation();
    061            model.addCriterion(dv);
    062        }
    063    }
    064
    065    @Override
    066    public String getWeightName() {
    067        return "Exams.DistributionWeight";
    068    }
    069
    070    @Override
    071    public String getXmlWeightName() {
    072        return "distributionWeight";
    073    }
    074
    075    @Override
    076    public double getWeightDefault(DataProperties config) {
    077        return 1.0;
    078    }
    079
    080    @Override
    081    public double getValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value, Set<ExamPlacement> conflicts) {
    082        int penalty = 0;
    083        ExamPlacement original = assignment.getValue(value.variable());
    084        for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) {
    085            if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()))
    086                continue;
    087            penalty += dc.countViolations(assignment, value) * dc.getWeight();
    088            if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight();
    089        }
    090        return penalty;
    091    }
    092
    093    @Override
    094    public boolean isRoomCriterion() { return true; }
    095
    096    /**
    097     * Room related distribution penalty, i.e., sum weights of violated
    098     * distribution constraints
    099     */
    100    @Override
    101    public double getRoomValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) {
    102        int penalty = 0;
    103        ExamPlacement original = assignment.getValue(value.variable());
    104        for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) {
    105            if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isRoomRelated())
    106                continue;
    107            penalty += dc.countViolations(assignment, value) * dc.getWeight();
    108            if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight();
    109        }
    110        return penalty;
    111    }
    112
    113    @Override
    114    public double getValue(Assignment<Exam, ExamPlacement> assignment, Collection<Exam> variables) {
    115        int penalty = 0;
    116        Set<ExamDistributionConstraint> added = new HashSet<ExamDistributionConstraint>();
    117        for (Exam exam: variables) {
    118            for (ExamDistributionConstraint dc : exam.getDistributionConstraints()) {
    119                if (added.add(dc)) {
    120                    if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()))
    121                        continue;
    122                    penalty += dc.countViolations(assignment) * dc.getWeight();
    123                }
    124            }
    125        }
    126        return penalty;
    127    }
    128
    129    @Override
    130    public boolean isPeriodCriterion() { return true; }
    131
    132    public void inc(Assignment<Exam, ExamPlacement> assignment, double value, ExamDistributionConstraint dc) {
    133        if (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) {
    134            getModel().getCriterion(DistributionViolation.class).inc(assignment, value / dc.getWeight());
    135        } else {
    136            super.inc(assignment, value);
    137        }
    138    }
    139
    140    /**
    141     * Period related distribution penalty, i.e., sum weights of violated
    142     * distribution constraints
    143     */
    144    @Override
    145    public double getPeriodValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) {
    146        int penalty = 0;
    147        ExamPlacement original = assignment.getValue(value.variable());
    148        for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) {
    149            if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isPeriodRelated())
    150                continue;
    151            penalty += dc.countViolations(assignment, value) * dc.getWeight();
    152            if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight();
    153        }
    154        return penalty;
    155    }
    156
    157    @Override
    158    protected double[] computeBounds(Assignment<Exam, ExamPlacement> assignment) {
    159        double[] bounds = new double[] { 0.0, 0.0 };
    160        for (ExamDistributionConstraint dc : ((ExamModel)getModel()).getDistributionConstraints()) {
    161            if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()))
    162                continue;
    163            bounds[1] += dc.getWeight() * dc.variables().size() * (dc.variables().size() - 1) / 2;
    164        }
    165        return bounds;
    166    }
    167
    168    @Override
    169    public String toString(Assignment<Exam, ExamPlacement> assignment) {
    170        return (getValue(assignment) <= 0.0 ? "" : "DP:" + sDoubleFormat.format(getValue(assignment)));
    171    }
    172}