001package org.cpsolver.coursett.sectioning; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Iterator; 006import java.util.LinkedList; 007import java.util.List; 008import java.util.Queue; 009 010import org.cpsolver.coursett.model.Configuration; 011import org.cpsolver.coursett.model.Lecture; 012import org.cpsolver.coursett.model.Placement; 013import org.cpsolver.coursett.model.Student; 014import org.cpsolver.coursett.model.TimetableModel; 015import org.cpsolver.ifs.assignment.Assignment; 016import org.cpsolver.ifs.heuristics.NeighbourSelection; 017import org.cpsolver.ifs.model.Neighbour; 018import org.cpsolver.ifs.solution.Solution; 019import org.cpsolver.ifs.solver.Solver; 020import org.cpsolver.ifs.util.ToolBox; 021 022/** 023 * Generate student swaps. Rather than generating swaps at random, the class iterates between 024 * all classes and all conflicting students of each class and generate a random swap for 025 * each such student. 026 * 027 * @version CourseTT 1.3 (University Course Timetabling)<br> 028 * Copyright (C) 2017 Tomáš Müller<br> 029 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 030 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 031 * <br> 032 * This library is free software; you can redistribute it and/or modify 033 * it under the terms of the GNU Lesser General Public License as 034 * published by the Free Software Foundation; either version 3 of the 035 * License, or (at your option) any later version. <br> 036 * <br> 037 * This library is distributed in the hope that it will be useful, but 038 * WITHOUT ANY WARRANTY; without even the implied warranty of 039 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 040 * Lesser General Public License for more details. <br> 041 * <br> 042 * You should have received a copy of the GNU Lesser General Public 043 * License along with this library; if not see 044 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 045 */ 046public class StudentSwapGenerator implements NeighbourSelection<Lecture, Placement> { 047 Iterator<Lecture> iLectures = null; 048 Lecture iLecture = null; 049 Iterator<Student> iStudents = null; 050 051 @Override 052 public void init(Solver<Lecture, Placement> solver) { 053 } 054 055 @Override 056 public Neighbour<Lecture, Placement> selectNeighbour(Solution<Lecture, Placement> solution) { 057 TimetableModel model = (TimetableModel)solution.getModel(); 058 if (model.getAllStudents().isEmpty()) return null; 059 060 boolean next = false; 061 while (iLecture == null || iStudents == null || !iStudents.hasNext()) { 062 if (iLectures == null || !iLectures.hasNext()) { 063 if (next) return null; 064 iLectures = model.variables().iterator(); 065 next = true; 066 } 067 iLecture = iLectures.next(); 068 if (iLecture.getConfiguration() == null || (iLecture.getConfiguration().getAltConfigurations().isEmpty() && iLecture.isSingleSection())) continue; 069 iStudents = iLecture.conflictStudents(solution.getAssignment()).iterator(); 070 } 071 return generateSwap(model, solution.getAssignment(), iStudents.next(), iLecture.getConfiguration()); 072 } 073 074 public Neighbour<Lecture, Placement> selectNeighbour(Assignment<Lecture, Placement> assignment, Lecture lecture) { 075 return generateSwap((TimetableModel)lecture.getModel(), assignment, ToolBox.random(lecture.students()), lecture.getConfiguration()); 076 077 } 078 079 public Neighbour<Lecture, Placement> generateSwap(TimetableModel model, Assignment<Lecture, Placement> assignment, Student student, Configuration config) { 080 if (student == null || config == null) return null; 081 if (!config.getAltConfigurations().isEmpty()) { 082 int idx = ToolBox.random(config.getAltConfigurations().size() + 2); 083 if (idx > 1) config = config.getAltConfigurations().get(idx - 2); 084 } 085 Long subpartId = ToolBox.random(config.getTopSubpartIds()); 086 int nrAttempts = ToolBox.random(10); 087 for (int i = 0; i < nrAttempts; i++) { 088 Lecture lecture = ToolBox.random(config.getTopLectures(subpartId)); 089 Student other = ToolBox.random(lecture.students()); 090 if (other != null && !student.equals(other)) { 091 StudentSwap swap = new StudentSwap(model, assignment, student, other, config.getOfferingId()); 092 if (swap.isAllowed()) return swap; 093 } 094 } 095 List<Lecture> lectures = new ArrayList<Lecture>(); 096 Queue<Collection<Lecture>> queue = new LinkedList<Collection<Lecture>>(config.getTopLectures().values()); 097 while (!queue.isEmpty()) { 098 List<Lecture> adepts = new ArrayList<Lecture>(); 099 for (Lecture adept: queue.poll()) 100 if (student.canEnroll(adept)) adepts.add(adept); 101 if (adepts.isEmpty()) return null; 102 Lecture lect = ToolBox.random(adepts); 103 lectures.add(lect); 104 if (lect.hasAnyChildren()) 105 queue.addAll(lect.getChildren().values()); 106 } 107 StudentSwap swap = new StudentSwap(model, assignment, student, lectures); 108 return (swap.isAllowed() ? swap : null); 109 } 110 111}