Mathematical Formulation
This page documents the mixed-integer linear programming (MILP) model implemented in Scheduler._build_model.
For usage-oriented movement configuration examples, see Building Movement and Staggered Starts.
Code-Aligned Notation
This table maps the mathematical symbols to the exact Pyomo components in the code.
Math symbol |
Code component |
Meaning |
|---|---|---|
\(\mathcal{S}\) |
|
Visitor set |
\(\mathcal{F}\) |
|
Faculty set with non-empty availability |
\(\mathcal{T}\) |
|
Time-slot index set |
\(\mathcal{K}\) |
|
Building set |
\(\mathcal{F}_k\) |
|
Faculty assigned to building \(k\) |
\(\mathcal{B}\) |
|
Candidate break slots |
\(w_{sf}\) |
|
Utility weight for visitor-faculty match |
\(p\) |
|
Group penalty coefficient |
\(y_{sft}\) |
|
Meeting assignment decision variable |
\(z_{ft}\) |
|
Visitors beyond one in a meeting |
\(q_f\) |
|
Overload indicator |
\(\beta_{skt}\) |
|
Visitor in building \(k\) at slot \(t\) (travel policies) |
\(r_{ft}\) |
|
Faculty break indicator |
\(\underline{L}_s,\overline{U}_s\) |
|
Effective visitor min/max meetings |
\(\underline{L}_f,\overline{U}_f\) |
|
Effective faculty min/max meetings |
Worked Example Dataset
The repository includes a formulation-focused dataset:
examples/faculty_formulation.yamlexamples/config_formulation.yamlexamples/data_formulation_visitors.csv
Dataset size:
6 faculty (
Prof. AthroughProf. F)4 time slots
10 visitors
buildings:
ABCandXYZtopic areas:
Energy,Bio,Theorybreak window: slots 2 and 3
Runner script:
scripts/run_formulation_example.py
Faculty availability conflicts in this example:
Faculty |
Available slots |
Unavailable slots |
|---|---|---|
Prof. A |
1, 2, 3, 4 |
None |
Prof. B |
1, 2, 4 |
3 |
Prof. C |
1, 3, 4 |
2 |
Prof. D |
2, 3, 4 |
1 |
Prof. E |
1, 2, 3 |
4 |
Prof. F |
1, 3, 4 |
2 |
Student Preference Summary
Visitor |
Prof1 |
Prof2 |
Prof3 |
Prof4 |
Area1 |
Area2 |
|---|---|---|---|---|---|---|
Visitor 01 |
Prof. A |
Prof. C |
Prof. E |
Prof. F |
Energy |
Theory |
Visitor 02 |
Prof. B |
Prof. D |
Prof. F |
Prof. C |
Bio |
Energy |
Visitor 03 |
Prof. C |
Prof. A |
Prof. E |
Prof. B |
Theory |
Energy |
Visitor 04 |
Prof. D |
Prof. F |
Prof. B |
Prof. A |
Energy |
Bio |
Visitor 05 |
Prof. E |
Prof. C |
Prof. A |
Prof. D |
Bio |
Theory |
Visitor 06 |
Prof. F |
Prof. B |
Prof. D |
Prof. E |
Theory |
Bio |
Visitor 07 |
Prof. A |
Prof. E |
Prof. C |
Prof. D |
Energy |
Bio |
Visitor 08 |
Prof. B |
Prof. F |
Prof. D |
Prof. A |
Bio |
Theory |
Visitor 09 |
Prof. C |
Prof. E |
Prof. A |
Prof. B |
Theory |
Energy |
Visitor 10 |
Prof. D |
Prof. B |
Prof. F |
Prof. C |
Energy |
Theory |
Sets and Indices
\(s \in \mathcal{S}\): visitors
\(f \in \mathcal{F}\): faculty with at least one available time slot
\(t \in \mathcal{T} = \{1, \dots, T\}\): time slots
\(k \in \mathcal{K}\): buildings
\(\mathcal{F}_k \subseteq \mathcal{F}\): faculty located in building \(k\)
\(\mathcal{B} \subseteq \mathcal{T}\): configured break slot options
Parameters
\(w_{sf}\): utility weight for assigning visitor \(s\) to faculty \(f\)
\(p \ge 0\): group meeting penalty (
group_penalty)\(L_f\): minimum visitors per faculty (
min_visitors)\(U_f\): maximum visitors per faculty (
max_visitors)\(L_s\): minimum faculty meetings per visitor (
min_faculty)\(\underline{L}_s,\overline{U}_s\): effective visitor bounds after optional per-visitor overrides
\(\underline{L}_f,\overline{U}_f\): effective faculty bounds after optional per-faculty overrides
\(G\): maximum group size in a faculty-time meeting (
max_group)\(a_{ft} \in \{0,1\}\): 1 if faculty \(f\) is available at slot \(t\)
Decision Variables
\(y_{sft} \in \{0,1\}\): 1 if visitor \(s\) meets faculty \(f\) at time \(t\)
\(z_{ft} \ge 0\): number of visitors beyond one in faculty-time meeting \((f,t)\)
\(q_f \in \{0,1\}\): overload indicator for faculty \(f\)
\(\beta_{skt} \in \{0,1\}\): visitor \(s\) is in building \(k\) at slot \(t\) (when
movement.policyistravel_timeornonoverlap_time)\(r_{ft} \in \{0,1\}\): faculty \(f\) is on break at candidate break slot \(t \in \mathcal{B}\)
Objective
Maximize preference satisfaction minus group/overload penalties:
Interpretation:
First term rewards satisfying visitor preferences.
Second term discourages group meetings larger than one visitor.
Third term discourages faculty loads beyond the soft threshold used in the implementation.
Constraints
1. Faculty availability
Meetings can only occur when faculty are available.
2. Faculty minimum and maximum total meetings (with optional overrides)
3. Visitor cannot attend simultaneous meetings
4. Visitor minimum and optional maximum total meetings
The default lower bound is \(\underline{L}_s=\min\left(L_s,\left|\mathcal{T}_s^{\text{avail}}\right|\right)\), then overridden by set_visitor_meeting_bounds when provided.
When a visitor-specific maximum is configured:
5. Visitor meets same faculty at most once
6. Excess visitors per faculty-time slot
This linearizes the “beyond one visitor” quantity used in the objective penalty.
7. Faculty overload indicator
This activates \(q_f\) when total meetings exceed the soft threshold \(U_f - 2\).
8. Maximum group size per meeting
9. Building phase constraints (movement.phase_slot)
Each building \(k\) has an earliest allowed meeting slot \(\phi_k\):
This supports staggered schedules such as “Building A first” or “Building B first” without requiring a dedicated mode.
10. Travel-time occupancy and lag constraints (movement.policy = travel_time or nonoverlap_time)
When travel-time constraints are enabled, occupancy indicators are linked by:
For each building pair \((k,\ell)\) with lag \(\tau_{k\ell} > 0\), transitions within the lag window are forbidden:
When travel_slots: auto or policy: nonoverlap_time is used, \(\tau_{k\ell}\)
is derived from absolute slot timestamps so overlapping real-time transitions
are disallowed (optionally with additional min_buffer_minutes).
11. Visitor break requirement
Applied when automatic student break constraints are enabled (movement legacy
NO_OFFSET compatibility, student_breaks > 0, or legacy
enforce_breaks=True):
Here \(b_s\) is the configured student_breaks count. Each visitor must have at
least that many breaks during the configured break window.
12. Faculty break variables and requirement
Applied when automatic faculty break constraints are enabled
(faculty_breaks > 0, movement legacy NO_OFFSET compatibility, or legacy
enforce_breaks=True):
If \(r_{ft}=1\), faculty \(f\) has no meeting at break slot \(t\).
Here \(b\) is the configured faculty_breaks count after applying legacy alias
resolution, and \(u_f\) counts faculty-unavailable slots outside the
configured break window that already count as breaks. Unavailable slots inside
the break window are fixed to \(r_{ft}=1\) in the implementation. Legacy
enforce_breaks=True maps to faculty_breaks=1 and student_breaks=1.
14. User-specified visitor/faculty hard constraints (optional)
The APIs forbid_meeting, require_meeting, and require_break add explicit hard constraints:
All-slot forbid (forbid_meeting(s,f)):
Slot-specific forbid (forbid_meeting(s,f,t^\*)):
All-slot require (require_meeting(s,f)):
Slot-specific require (require_meeting(s,f,t^\*)):
Required breaks (require_break(s,\mathcal{T}',b)):
where \(b\) is min_breaks.
Top-N No-Good Cuts (Optional Multi-Solution Extension)
When generating ranked alternatives, the model adds one no-good cut after each feasible solution to exclude the exact same binary assignment vector in later iterations.
Let \(\mathcal{I}\) be all assignment indices \((s,f,t)\) and let \(\mathcal{A}^{(k)} \subseteq \mathcal{I}\) be the active assignment set in rank-\(k\) solution. The no-good cut for solution \(k\) is:
This enforces that at least one binary variable differs from solution \(k\).
Notes on Implementation
The model is assembled in
Scheduler._build_modeland solved inScheduler._solve_model.Feasibility checks and diagnostics are available through
has_feasible_solutionandinfeasibility_report.Constraint activation depends on movement policy plus the resolved
faculty_breaks/student_breaksvalues exactly as documented above.Legacy mode-specific movement constraints are superseded by the
movement.phase_slotandmovement.travel_slotsformulation above.When advanced user hard constraints or per-entity bounds are configured, pre-solve consistency checks run via
_run_presolve_hard_constraint_checks. These checks catch obvious contradictions before solver execution and raise clearValueErrormessages.debug_infeasible=False(default): fail fast before model build.debug_infeasible=True: build model first, then raise pre-solve errors so advanced users can inspects.model(for example before IIS workflows).
Example Solutions
The table below was generated by solving the worked dataset with:
solver=Solver.HIGHSgroup_penalty=0.2min_visitors=2max_visitors=8min_faculty=1max_group=2faculty_breaks=1student_breaks=1
Solver metrics:
total utility:
113.5total excess-visitors penalty term:
0.2 * 13 = 2.6faculty overload indicators:
0objective value:
110.9
Solver-Generated Visitor Schedule
Visitor |
Slot 1 |
Slot 2 |
Slot 3 |
Slot 4 |
|---|---|---|---|---|
Visitor 01 |
Prof. E |
Prof. A |
Break |
Prof. C |
Visitor 02 |
Prof. B |
Break |
Prof. D |
Prof. F |
Visitor 03 |
Prof. E |
Prof. A |
Break |
Prof. C |
Visitor 04 |
Prof. F |
Prof. B |
Break |
Prof. D |
Visitor 05 |
Prof. C |
Prof. E |
Break |
Prof. A |
Visitor 06 |
Prof. F |
Prof. D |
Break |
Prof. B |
Visitor 07 |
Prof. A |
Prof. E |
Break |
Prof. D |
Visitor 08 |
Prof. A |
Break |
Prof. F |
Prof. B |
Visitor 09 |
Prof. C |
Break |
Prof. E |
Prof. A |
Visitor 10 |
Prof. B |
Prof. D |
Break |
Prof. F |
Solver-Generated Faculty Load Summary
Faculty |
Total meetings |
Slot 1 |
Slot 2 |
Slot 3 |
Slot 4 |
|---|---|---|---|---|---|
Prof. A |
6 |
2 |
2 |
0 |
2 |
Prof. B |
5 |
2 |
1 |
0 |
2 |
Prof. C |
4 |
2 |
0 |
0 |
2 |
Prof. D |
5 |
0 |
2 |
1 |
2 |
Prof. E |
5 |
2 |
2 |
1 |
0 |
Prof. F |
5 |
2 |
0 |
1 |
2 |