Skip to content

Commit

Permalink
Conference scheduling example: Timeslot
Browse files Browse the repository at this point in the history
  • Loading branch information
ge0ffrey committed Dec 16, 2017
1 parent 9c09f9c commit c465583
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 64 deletions.
Expand Up @@ -18,7 +18,6 @@


import java.util.List; import java.util.List;


import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty; import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore; import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution; import org.optaplanner.core.api.domain.solution.PlanningSolution;
Expand All @@ -27,19 +26,19 @@
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore; import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.examples.common.domain.AbstractPersistable; import org.optaplanner.examples.common.domain.AbstractPersistable;


@XStreamAlias("ConferenceSolution")
@PlanningSolution @PlanningSolution
public class ConferenceSolution extends AbstractPersistable { public class ConferenceSolution extends AbstractPersistable {


private String name; private String name;


@ValueRangeProvider(id = "timeslotRange")
@ProblemFactCollectionProperty
private List<Timeslot> timeslotList;

@ValueRangeProvider(id = "roomRange") @ValueRangeProvider(id = "roomRange")
@ProblemFactCollectionProperty @ProblemFactCollectionProperty
private List<Room> roomList; private List<Room> roomList;


// @ProblemFactCollectionProperty
// private List<Track> trackList;

@ProblemFactCollectionProperty @ProblemFactCollectionProperty
private List<Speaker> speakerList; private List<Speaker> speakerList;


Expand All @@ -66,6 +65,14 @@ public void setName(String name) {
this.name = name; this.name = name;
} }


public List<Timeslot> getTimeslotList() {
return timeslotList;
}

public void setTimeslotList(List<Timeslot> timeslotList) {
this.timeslotList = timeslotList;
}

public List<Room> getRoomList() { public List<Room> getRoomList() {
return roomList; return roomList;
} }
Expand All @@ -74,14 +81,6 @@ public void setRoomList(List<Room> roomList) {
this.roomList = roomList; this.roomList = roomList;
} }


// public List<Track> getTrackList() {
// return trackList;
// }
//
// public void setTrackList(List<Track> trackList) {
// this.trackList = trackList;
// }

public List<Speaker> getSpeakerList() { public List<Speaker> getSpeakerList() {
return speakerList; return speakerList;
} }
Expand Down
Expand Up @@ -18,10 +18,8 @@


import java.util.Set; import java.util.Set;


import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.optaplanner.examples.common.domain.AbstractPersistable; import org.optaplanner.examples.common.domain.AbstractPersistable;


@XStreamAlias("Room")
public class Room extends AbstractPersistable { public class Room extends AbstractPersistable {


private String name; private String name;
Expand Down
Expand Up @@ -18,13 +18,11 @@


import java.util.List; import java.util.List;


import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.optaplanner.core.api.domain.entity.PlanningEntity; import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.PlanningVariable; import org.optaplanner.core.api.domain.variable.PlanningVariable;
import org.optaplanner.examples.common.domain.AbstractPersistable; import org.optaplanner.examples.common.domain.AbstractPersistable;
import org.optaplanner.examples.conferencescheduling.domain.solver.MovableTalkFilter; import org.optaplanner.examples.conferencescheduling.domain.solver.MovableTalkFilter;


@XStreamAlias("Talk")
@PlanningEntity(movableEntitySelectionFilter = MovableTalkFilter.class) @PlanningEntity(movableEntitySelectionFilter = MovableTalkFilter.class)
public class Talk extends AbstractPersistable { public class Talk extends AbstractPersistable {


Expand All @@ -34,6 +32,9 @@ public class Talk extends AbstractPersistable {


private boolean lockedByUser = false; private boolean lockedByUser = false;


@PlanningVariable(valueRangeProviderRefs = "timeslotRange")
private Timeslot timeslot;

@PlanningVariable(valueRangeProviderRefs = "roomRange") @PlanningVariable(valueRangeProviderRefs = "roomRange")
private Room room; private Room room;


Expand Down Expand Up @@ -78,6 +79,14 @@ public void setLockedByUser(boolean lockedByUser) {
this.lockedByUser = lockedByUser; this.lockedByUser = lockedByUser;
} }


public Timeslot getTimeslot() {
return timeslot;
}

public void setTimeslot(Timeslot timeslot) {
this.timeslot = timeslot;
}

public Room getRoom() { public Room getRoom() {
return room; return room;
} }
Expand Down
@@ -0,0 +1,82 @@
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.optaplanner.examples.conferencescheduling.domain;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Set;

import org.optaplanner.examples.common.domain.AbstractPersistable;

public class Timeslot extends AbstractPersistable {

private LocalDateTime startDateTime;
private LocalDateTime endDateTime;

private Set<String> timeslotTagSet;

public LocalDate getDate() {
return startDateTime.toLocalDate();
}

public long getDurationInMinutes() {
return Duration.between(startDateTime, endDateTime).toMinutes();
}

public boolean overlaps(Timeslot other) {
if (this == other) {
return true;
}
return startDateTime.compareTo(other.endDateTime) < 0
&& other.startDateTime.compareTo(endDateTime) < 0;
}

@Override
public String toString() {
return startDateTime + "-" + endDateTime.toLocalTime();
}

// ************************************************************************
// Simple getters and setters
// ************************************************************************

public LocalDateTime getStartDateTime() {
return startDateTime;
}

public void setStartDateTime(LocalDateTime startDateTime) {
this.startDateTime = startDateTime;
}

public LocalDateTime getEndDateTime() {
return endDateTime;
}

public void setEndDateTime(LocalDateTime endDateTime) {
this.endDateTime = endDateTime;
}

public Set<String> getTimeslotTagSet() {
return timeslotTagSet;
}

public void setTimeslotTagSet(Set<String> timeslotTagSet) {
this.timeslotTagSet = timeslotTagSet;
}

}
Expand Up @@ -16,10 +16,8 @@


package org.optaplanner.examples.conferencescheduling.domain; package org.optaplanner.examples.conferencescheduling.domain;


import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.optaplanner.examples.common.domain.AbstractPersistable; import org.optaplanner.examples.common.domain.AbstractPersistable;


@XStreamAlias("Track")
public class Track extends AbstractPersistable { public class Track extends AbstractPersistable {


private String name; private String name;
Expand Down
Expand Up @@ -17,6 +17,10 @@
package org.optaplanner.examples.conferencescheduling.persistence; package org.optaplanner.examples.conferencescheduling.persistence;


import java.io.File; import java.io.File;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
Expand All @@ -27,23 +31,25 @@
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.optaplanner.examples.common.app.CommonApp; import org.optaplanner.examples.common.app.CommonApp;
import org.optaplanner.examples.common.app.LoggingMain; import org.optaplanner.examples.common.app.LoggingMain;
import org.optaplanner.examples.common.persistence.AbstractSolutionImporter;
import org.optaplanner.examples.common.persistence.StringDataGenerator; import org.optaplanner.examples.common.persistence.StringDataGenerator;
import org.optaplanner.examples.conferencescheduling.app.ConferenceSchedulingApp; import org.optaplanner.examples.conferencescheduling.app.ConferenceSchedulingApp;
import org.optaplanner.examples.conferencescheduling.domain.ConferenceSolution; import org.optaplanner.examples.conferencescheduling.domain.ConferenceSolution;
import org.optaplanner.examples.conferencescheduling.domain.Room; import org.optaplanner.examples.conferencescheduling.domain.Room;
import org.optaplanner.examples.conferencescheduling.domain.Speaker; import org.optaplanner.examples.conferencescheduling.domain.Speaker;
import org.optaplanner.examples.conferencescheduling.domain.Talk; import org.optaplanner.examples.conferencescheduling.domain.Talk;
import org.optaplanner.examples.conferencescheduling.domain.Timeslot;
import org.optaplanner.persistence.common.api.domain.solution.SolutionFileIO; import org.optaplanner.persistence.common.api.domain.solution.SolutionFileIO;


public class ConferenceSchedulingGenerator extends LoggingMain { public class ConferenceSchedulingGenerator extends LoggingMain {


public static void main(String[] args) { public static void main(String[] args) {
ConferenceSchedulingGenerator generator = new ConferenceSchedulingGenerator(); ConferenceSchedulingGenerator generator = new ConferenceSchedulingGenerator();
generator.writeConferenceSolution(50, 5); generator.writeConferenceSolution(1, 5);
generator.writeConferenceSolution(100, 10); generator.writeConferenceSolution(2, 5);
// generator.writeMeetingSchedule(200, 5); generator.writeConferenceSolution(2, 10);
// generator.writeMeetingSchedule(400, 5); generator.writeConferenceSolution(3, 10);
// generator.writeMeetingSchedule(800, 5); generator.writeConferenceSolution(3, 20);
} }
private final StringDataGenerator conferenceNameGenerator = new StringDataGenerator() private final StringDataGenerator conferenceNameGenerator = new StringDataGenerator()
.addPart(true, 0, .addPart(true, 0,
Expand All @@ -59,9 +65,24 @@ public static void main(String[] args) {
"2024", "2024",
"2025"); "2025");


private static final String TIMESLOT_LAB_TAG = "Lab";

private final LocalDate timeslotFirstDay = LocalDate.of(2018, 10, 1);

private final List<Pair<LocalTime, LocalTime>> timeslotOptions = Arrays.asList(
// Pair.of(LocalTime.of(8, 30), LocalTime.of(9, 30)), // General session
Pair.of(LocalTime.of(10, 15), LocalTime.of(11, 0)),
Pair.of(LocalTime.of(11, 30), LocalTime.of(12, 15)),
// Pair.of(LocalTime.of(13, 45), LocalTime.of(15, 0)), // General session
Pair.of(LocalTime.of(15, 30), LocalTime.of(16, 15)),
Pair.of(LocalTime.of(16, 30), LocalTime.of(17, 15)),
Pair.of(LocalTime.of(10, 15), LocalTime.of(12, 15)), // Lab
Pair.of(LocalTime.of(13, 0), LocalTime.of(15, 0)) // Lab
);

private final List<Pair<String, Double>> roomTagProbabilityList = Arrays.asList( private final List<Pair<String, Double>> roomTagProbabilityList = Arrays.asList(
Pair.of("Large", 0.20), Pair.of("Large", 0.20),
Pair.of("Lab", 0.10), Pair.of("Power outlets", 0.10),
Pair.of("Recorded", 0.50) Pair.of("Recorded", 0.50)
); );


Expand Down Expand Up @@ -113,13 +134,6 @@ public static void main(String[] args) {
"out of the box", "out of the box",
"for programmers"); "for programmers");


private final int[] durationInMinutesOptions = {
30, // 30 mins
60, // 1 hour
120, // 2 hours
};


protected final SolutionFileIO<ConferenceSolution> solutionFileIO; protected final SolutionFileIO<ConferenceSolution> solutionFileIO;
protected final File outputDir; protected final File outputDir;
protected Random random; protected Random random;
Expand All @@ -129,43 +143,67 @@ public ConferenceSchedulingGenerator() {
outputDir = new File(CommonApp.determineDataDir(ConferenceSchedulingApp.DATA_DIR_NAME), "unsolved"); outputDir = new File(CommonApp.determineDataDir(ConferenceSchedulingApp.DATA_DIR_NAME), "unsolved");
} }


private void writeConferenceSolution(int talkListSize, int roomListSize) { private void writeConferenceSolution(int dayListSize, int roomListSize) {
int timeslotListSize = dayListSize * timeslotOptions.size();
int talkListSize = timeslotListSize * roomListSize;
int speakerListSize = talkListSize * 2 / 3; int speakerListSize = talkListSize * 2 / 3;


String fileName = talkListSize + "talks-" + roomListSize + "rooms"; String fileName = talkListSize + "talks-" + timeslotListSize + "timeslots-" + roomListSize + "rooms";
File outputFile = new File(outputDir, fileName + "." + solutionFileIO.getOutputFileExtension()); File outputFile = new File(outputDir, fileName + "." + solutionFileIO.getOutputFileExtension());
ConferenceSolution solution = createConferenceSolution(fileName, roomListSize, speakerListSize, talkListSize); ConferenceSolution solution = createConferenceSolution(fileName, timeslotListSize, roomListSize, speakerListSize, talkListSize);
solutionFileIO.write(solution, outputFile); solutionFileIO.write(solution, outputFile);
} }


public ConferenceSolution createConferenceSolution(String fileName, int roomListSize, int speakerListSize, int talkListSize) { public ConferenceSolution createConferenceSolution(String fileName, int timeslotListSize, int roomListSize, int speakerListSize, int talkListSize) {
random = new Random(37); random = new Random(37);
ConferenceSolution solution = new ConferenceSolution(); ConferenceSolution solution = new ConferenceSolution();
solution.setId(0L); solution.setId(0L);
solution.setName(conferenceNameGenerator.generateNextValue()); solution.setName(conferenceNameGenerator.generateNextValue());


createTimeslotList(solution, timeslotListSize);
createRoomList(solution, roomListSize); createRoomList(solution, roomListSize);
createSpeakerList(solution, speakerListSize); createSpeakerList(solution, speakerListSize);
createTalkList(solution, talkListSize); createTalkList(solution, talkListSize);




// createMeetingListAndAttendanceList(solution, meetingListSize); BigInteger possibleSolutionSize = BigInteger.valueOf((long) timeslotListSize * roomListSize)
// createTimeGrainList(solution, timeGrainListSize); .pow(talkListSize);
// createPersonList(solution); logger.info("Conference {} has {} talks, {} timeslots and {} rooms with a search space of {}.",
// linkAttendanceListToPersons(solution); fileName,
// createMeetingAssignmentList(solution); talkListSize,

timeslotListSize,
// BigInteger possibleSolutionSize = BigInteger.valueOf((long) timeGrainListSize * roomListSize) roomListSize,
// .pow(solution.getMeetingAssignmentList().size()); AbstractSolutionImporter.getFlooredPossibleSolutionSize(possibleSolutionSize));
// logger.info("MeetingSchedule {} has {} meetings, {} timeGrains and {} rooms with a search space of {}.",
// fileName,
// meetingListSize,
// timeGrainListSize,
// roomListSize,
// AbstractSolutionImporter.getFlooredPossibleSolutionSize(possibleSolutionSize));
return solution; return solution;
} }


private void createTimeslotList(ConferenceSolution solution, int timeslotListSize) {
List<Timeslot> timeslotList = new ArrayList<>(timeslotListSize);
int timeslotOptionsIndex = 0;
LocalDate day = timeslotFirstDay;
for (int i = 0; i < timeslotListSize; i++) {
Timeslot timeslot = new Timeslot();
timeslot.setId((long) i);
if (timeslotOptionsIndex >= timeslotOptions.size()) {
timeslotOptionsIndex = 0;
day = day.plusDays(1);
}
Pair<LocalTime, LocalTime> pair = timeslotOptions.get(timeslotOptionsIndex);
timeslot.setStartDateTime(LocalDateTime.of(day, pair.getLeft()));
timeslot.setEndDateTime(LocalDateTime.of(day, pair.getRight()));
timeslotOptionsIndex++;
Set<String> timeslotTagSet = new LinkedHashSet<>(2);
if (timeslot.getDurationInMinutes() >= 120) {
timeslotTagSet.add(TIMESLOT_LAB_TAG);
}
timeslot.setTimeslotTagSet(timeslotTagSet);
logger.trace("Created timeslot ({}) with tags ({}).",
timeslot, timeslotTagSet);
timeslotList.add(timeslot);
}
solution.setTimeslotList(timeslotList);
}

private void createRoomList(ConferenceSolution solution, int roomListSize) { private void createRoomList(ConferenceSolution solution, int roomListSize) {
final int roomsPerFloor = 12; final int roomsPerFloor = 12;
List<Room> roomList = new ArrayList<>(roomListSize); List<Room> roomList = new ArrayList<>(roomListSize);
Expand Down

0 comments on commit c465583

Please sign in to comment.