001package org.cpsolver.studentsct.heuristics; 002 003import java.util.Collections; 004import java.util.Comparator; 005import java.util.HashMap; 006import java.util.Iterator; 007import java.util.List; 008import java.util.Set; 009 010import org.cpsolver.ifs.assignment.Assignment; 011import org.cpsolver.ifs.heuristics.BacktrackNeighbourSelection; 012import org.cpsolver.ifs.util.DataProperties; 013import org.cpsolver.studentsct.StudentSectioningModel; 014import org.cpsolver.studentsct.model.CourseRequest; 015import org.cpsolver.studentsct.model.Enrollment; 016import org.cpsolver.studentsct.model.Request; 017 018 019/** 020 * Randomized backtracking-based neighbour selection. This class extends 021 * {@link RandomizedBacktrackNeighbourSelection}, however, only a randomly 022 * selected subset of enrollments of each request is considered ( 023 * {@link CourseRequest#computeRandomEnrollments(Assignment, int)} with the given limit is 024 * used). 025 * 026 * <br> 027 * <br> 028 * Parameters: <br> 029 * <table border='1'><caption>Related Solver Parameters</caption> 030 * <tr> 031 * <th>Parameter</th> 032 * <th>Type</th> 033 * <th>Comment</th> 034 * </tr> 035 * <tr> 036 * <td>Neighbour.MaxValues</td> 037 * <td>{@link Integer}</td> 038 * <td>Limit on the number of enrollments to be visited of each 039 * {@link CourseRequest}.</td> 040 * </tr> 041 * </table> 042 * <br> 043 * <br> 044 * 045 * @author Tomáš Müller 046 * @version StudentSct 1.3 (Student Sectioning)<br> 047 * Copyright (C) 2007 - 2014 Tomáš Müller<br> 048 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 049 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 050 * <br> 051 * This library is free software; you can redistribute it and/or modify 052 * it under the terms of the GNU Lesser General Public License as 053 * published by the Free Software Foundation; either version 3 of the 054 * License, or (at your option) any later version. <br> 055 * <br> 056 * This library is distributed in the hope that it will be useful, but 057 * WITHOUT ANY WARRANTY; without even the implied warranty of 058 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 059 * Lesser General Public License for more details. <br> 060 * <br> 061 * You should have received a copy of the GNU Lesser General Public 062 * License along with this library; if not see 063 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 064 */ 065public class RandomizedBacktrackNeighbourSelection extends BacktrackNeighbourSelection<Request, Enrollment> { 066 private int iMaxValues = 100; 067 private boolean iPreferPriorityStudents = true; 068 069 /** 070 * Constructor 071 * 072 * @param properties 073 * configuration 074 * @throws Exception thrown when the initialization fails 075 */ 076 public RandomizedBacktrackNeighbourSelection(DataProperties properties) throws Exception { 077 super(properties); 078 iMaxValues = properties.getPropertyInt("Neighbour.MaxValues", iMaxValues); 079 iPreferPriorityStudents = properties.getPropertyBoolean("Sectioning.PriorityStudentsFirstSelection.AllIn", true); 080 } 081 082 /** 083 * List of values of a variable. 084 * {@link CourseRequest#computeRandomEnrollments(Assignment, int)} with the provided 085 * limit is used for a {@link CourseRequest}. 086 */ 087 @Override 088 protected Iterator<Enrollment> values(BacktrackNeighbourSelection<Request, Enrollment>.BacktrackNeighbourSelectionContext context, Request variable) { 089 if (variable instanceof CourseRequest) { 090 final CourseRequest request = (CourseRequest)variable; 091 final StudentSectioningModel model = (StudentSectioningModel)context.getModel(); 092 final Assignment<Request, Enrollment> assignment = context.getAssignment(); 093 final Enrollment current = assignment.getValue(request); 094 List<Enrollment> values = (iMaxValues > 0 ? request.computeRandomEnrollments(assignment, iMaxValues) : request.computeEnrollments(assignment)); 095 Collections.sort(values, new Comparator<Enrollment>() { 096 private HashMap<Enrollment, Double> iValues = new HashMap<Enrollment, Double>(); 097 private Double value(Enrollment e) { 098 Double value = iValues.get(e); 099 if (value == null) { 100 if (model.getStudentQuality() != null) 101 value = model.getStudentWeights().getWeight(assignment, e, model.getStudentQuality().conflicts(e)); 102 else 103 value = model.getStudentWeights().getWeight(assignment, e, 104 (model.getDistanceConflict() == null ? null : model.getDistanceConflict().conflicts(e)), 105 (model.getTimeOverlaps() == null ? null : model.getTimeOverlaps().conflicts(e))); 106 iValues.put(e, value); 107 } 108 return value; 109 } 110 @Override 111 public int compare(Enrollment e1, Enrollment e2) { 112 if (e1.equals(e2)) return 0; 113 if (e1.equals(current)) return -1; 114 if (e2.equals(current)) return 1; 115 Double v1 = value(e1), v2 = value(e2); 116 return v1.equals(v2) ? e1.compareTo(assignment, e2) : v2.compareTo(v1); 117 } 118 }); 119 return values.iterator(); 120 } else { 121 return variable.computeEnrollments(context.getAssignment()).iterator(); 122 } 123 } 124 125 /** 126 * Check if the given conflicting enrollment can be unassigned 127 * @param conflict given enrollment 128 * @return if running MPP, do not unassign initial enrollments 129 */ 130 public boolean canUnassign(Enrollment enrollment, Enrollment conflict, Assignment<Request, Enrollment> assignment) { 131 if (conflict.getRequest().isMPP() && conflict.equals(conflict.getRequest().getInitialAssignment()) && 132 !enrollment.equals(enrollment.getRequest().getInitialAssignment())) return false; 133 if (conflict.getRequest() instanceof CourseRequest && ((CourseRequest)conflict.getRequest()).getFixedValue() != null) return false; 134 if (conflict.getRequest().getStudent().hasMinCredit()) { 135 float credit = conflict.getRequest().getStudent().getAssignedCredit(assignment) - conflict.getCredit(); 136 if (credit < conflict.getRequest().getStudent().getMinCredit()) return false; 137 } 138 if (!conflict.getRequest().isAlternative() && conflict.getRequest().getRequestPriority().isHigher(enrollment.getRequest())) return false; 139 if (iPreferPriorityStudents || conflict.getRequest().getRequestPriority().isSame(enrollment.getRequest())) { 140 if (conflict.getStudent().getPriority().isHigher(enrollment.getStudent())) return false; 141 } 142 return true; 143 } 144 145 @Override 146 protected boolean checkBound(List<Request> variables2resolve, int idx, int depth, Enrollment value, Set<Enrollment> conflicts) { 147 for (Enrollment conflict: conflicts) 148 if (!canUnassign(value, conflict, getContext().getAssignment())) return false; 149 return super.checkBound(variables2resolve, idx, depth, value, conflicts); 150 } 151}