214 lines
7.6 KiB
Python
214 lines
7.6 KiB
Python
# Copyright 2010-2011 Google
|
|
# 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.
|
|
|
|
"""Simple meeting scheduler.
|
|
|
|
In this model, we try to schedule a meeting. The meeting will occur
|
|
in the following day. Time is divided in quarters or hours. It
|
|
starts at 8AM and ends at 7PM -> 44 timeslots.
|
|
|
|
There are p persons involved, m are mandatory. Each has his own set of
|
|
meetings. There are r rooms. Each has its own capacity.
|
|
|
|
Each person must have an 1 hour lunch break between 11h30 AM and 2h30 PM.
|
|
The goal is to find a meeting and a room such that all m mandatory people are
|
|
in the meeting and a maximum of non mandatory people are also in the meeting.
|
|
"""
|
|
|
|
|
|
|
|
from google.apputils import app
|
|
import gflags
|
|
from constraint_solver import pywrapcp
|
|
|
|
FLAGS = gflags.FLAGS
|
|
|
|
|
|
def main(unused_argv):
|
|
# Create the solver.
|
|
solver = pywrapcp.Solver('simple meeting scheduler')
|
|
|
|
# All participants (mandatory and non mandatory).
|
|
attendees = 12
|
|
all_people = range(1, attendees + 1)
|
|
|
|
# Mandatory people.
|
|
all_mandatory_people = [1, 2, 5, 9]
|
|
|
|
# Time range.
|
|
max_time = 44
|
|
|
|
# Lunch description.
|
|
lunch_start_quarter = 14 # 11:30 AM
|
|
lunch_end_quarter = 25 # 2:30 PM
|
|
lunch_duration = 4 # 1 hour
|
|
|
|
# Predefined meetings.
|
|
existing_meetings = [{'start': 2, 'duration': 12, 'person': 1},
|
|
{'start': 4, 'duration': 8, 'person': 4}]
|
|
|
|
rooms_count = 5
|
|
all_rooms = range(1, rooms_count + 1)
|
|
|
|
room_sizes = {1: 7, 2: 8, 3: 10, 4: 3, 5: 8}
|
|
|
|
room_reservations = [{'start': 3, 'duration': 4, 'room': 1},
|
|
{'start': 20, 'duration': 22, 'room': 3},
|
|
{'start': 1, 'duration': 18, 'room': 2}]
|
|
|
|
for t in existing_meetings:
|
|
m = solver.FixedInterval(t['start'], t['duration'], 'existing meeting')
|
|
t['meeting'] = m
|
|
|
|
for t in room_reservations:
|
|
m = solver.FixedInterval(t['start'], t['duration'], 'room reservation')
|
|
t['reservation'] = m
|
|
|
|
# Meeting requirements, variables and copies.
|
|
meeting_duration = 3
|
|
|
|
meeting = solver.FixedDurationIntervalVar(1,
|
|
max_time,
|
|
meeting_duration,
|
|
False,
|
|
'meeting')
|
|
|
|
people_meeting_copies = {}
|
|
for p in all_people:
|
|
name = 'people meeting copy %d' % p
|
|
people_meeting_copy = solver.FixedDurationIntervalVar(1,
|
|
max_time,
|
|
meeting_duration,
|
|
True,
|
|
name)
|
|
people_meeting_copies[p] = people_meeting_copy
|
|
|
|
room_meeting_copies = {}
|
|
for r in all_rooms:
|
|
name = 'room meeting copy %d' % r
|
|
room_meeting_copy = solver.FixedDurationIntervalVar(1,
|
|
max_time,
|
|
meeting_duration,
|
|
True,
|
|
name)
|
|
room_meeting_copies[r] = room_meeting_copy
|
|
|
|
meeting_location = solver.IntVar(all_rooms, 'meeting location')
|
|
|
|
all_people_calendars = {}
|
|
all_people_presence = {}
|
|
|
|
for p in all_people:
|
|
lunch = solver.FixedDurationIntervalVar(lunch_start_quarter,
|
|
lunch_end_quarter - lunch_duration,
|
|
lunch_duration,
|
|
False,
|
|
'lunch for %d' % p)
|
|
calendar = [people_meeting_copies[p], lunch]
|
|
calendar.extend([t['meeting']
|
|
for t in existing_meetings if t['person'] == p])
|
|
all_people_calendars[p] = solver.SequenceVar(calendar,
|
|
'people %d calendar' % p)
|
|
solver.Add(solver.DisjunctiveConstraint(calendar))
|
|
all_people_presence[p] = solver.BoolVar('presence of people %d' % p)
|
|
|
|
people_count = solver.IntVar(0, attendees, 'people count')
|
|
|
|
all_rooms_calendars = {}
|
|
all_rooms_presence = {}
|
|
for r in all_rooms:
|
|
calendar = [room_meeting_copies[r]]
|
|
calendar.extend([t['reservation']
|
|
for t in room_reservations if t['room'] == r])
|
|
all_rooms_calendars[r] = solver.SequenceVar(calendar,
|
|
'room %d calendar' % r)
|
|
solver.Add(solver.DisjunctiveConstraint(calendar))
|
|
all_rooms_presence[r] = solver.BoolVar('presence of room %d' %r)
|
|
|
|
# Objective: maximum number of people.
|
|
objective = solver.Maximize(people_count, 1)
|
|
|
|
# Constraints:
|
|
|
|
# Maintains persons_count.
|
|
solver.Add(people_count == solver.Sum([all_people_presence[p]
|
|
for p in all_people]))
|
|
# Mandatory persons.
|
|
for p in all_mandatory_people:
|
|
solver.Add(all_people_presence[p] == True)
|
|
|
|
# All copies are in sync with the original meeting.
|
|
for p in all_people:
|
|
solver.Add(meeting.StaysInSync(people_meeting_copies[p]))
|
|
|
|
for r in all_rooms:
|
|
solver.Add(meeting.StaysInSync(room_meeting_copies[r]))
|
|
|
|
# Synchronize persons_presence and meetings.
|
|
for p in all_people:
|
|
solver.Add(all_people_presence[p] ==
|
|
people_meeting_copies[p].PerformedExpr())
|
|
|
|
# Synchronize all_presences and meetings.
|
|
for r in all_rooms:
|
|
solver.Add(all_rooms_presence[r] ==
|
|
room_meeting_copies[r].PerformedExpr())
|
|
|
|
# Map meeting_location to room_meeting presence.
|
|
solver.Add((meeting_location - 1).MapTo([all_rooms_presence[p]
|
|
for p in all_rooms]))
|
|
# Persons must fit in the room.
|
|
sizes = [room_sizes[r] for r in all_rooms]
|
|
solver.Add(people_count <= (meeting_location - 1).IndexOf(sizes))
|
|
|
|
# Add sequence constraints.
|
|
all_calendars = []
|
|
all_calendars.extend([all_people_calendars[p] for p in all_people])
|
|
all_calendars.extend([all_rooms_calendars[r] for r in all_rooms])
|
|
|
|
# Build decision builder.
|
|
# Chosse the location.
|
|
vars_phase = solver.Phase([meeting_location, people_count],
|
|
solver.INT_VAR_SIMPLE,
|
|
solver.INT_VALUE_SIMPLE)
|
|
|
|
# Solve people calendars conflicts.
|
|
sequence_phase = solver.Phase(all_calendars, solver.SEQUENCE_DEFAULT)
|
|
# Schedule meeting at the earliest possible time.
|
|
meeting_phase = solver.Phase([meeting], solver.INTERVAL_DEFAULT)
|
|
# And compose.
|
|
main_phase = solver.Compose([sequence_phase, vars_phase, meeting_phase])
|
|
|
|
solution = solver.Assignment()
|
|
solution.Add(meeting_location)
|
|
solution.Add(people_count)
|
|
solution.Add([all_people_presence[p] for p in all_people])
|
|
solution.Add(meeting)
|
|
|
|
collector = solver.LastSolutionCollector(solution)
|
|
|
|
search_log = solver.SearchLog(100, people_count)
|
|
|
|
solver.Solve(main_phase, [collector, search_log, objective])
|
|
|
|
if collector.SolutionCount() > 0:
|
|
|
|
print ('we could schedule %d persons in room %d starting at quarter %d' %
|
|
(collector.Value(0, people_count),
|
|
collector.Value(0, meeting_location),
|
|
collector.StartValue(0, meeting)))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
app.run()
|