converge_optimization/scheduling/
mod.rs1use crate::Result;
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Interval {
18 pub id: usize,
20 pub earliest_start: i64,
22 pub latest_end: i64,
24 pub duration: i64,
26 pub demand: i64,
28}
29
30impl Interval {
31 pub fn new(id: usize, earliest_start: i64, latest_end: i64, duration: i64) -> Self {
33 Self {
34 id,
35 earliest_start,
36 latest_end,
37 duration,
38 demand: 1,
39 }
40 }
41
42 pub fn with_demand(mut self, demand: i64) -> Self {
44 self.demand = demand;
45 self
46 }
47
48 pub fn latest_start(&self) -> i64 {
50 self.latest_end - self.duration
51 }
52
53 pub fn earliest_end(&self) -> i64 {
55 self.earliest_start + self.duration
56 }
57
58 pub fn is_feasible(&self) -> bool {
60 self.earliest_start + self.duration <= self.latest_end
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ScheduledInterval {
67 pub interval: Interval,
69 pub start: i64,
71}
72
73impl ScheduledInterval {
74 pub fn end(&self) -> i64 {
76 self.start + self.interval.duration
77 }
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct SchedulingProblem {
83 pub intervals: Vec<Interval>,
85 pub capacity: i64,
87 pub disjunctive: bool,
89}
90
91impl SchedulingProblem {
92 pub fn disjunctive(intervals: Vec<Interval>) -> Self {
94 Self {
95 intervals,
96 capacity: 1,
97 disjunctive: true,
98 }
99 }
100
101 pub fn cumulative(intervals: Vec<Interval>, capacity: i64) -> Self {
103 Self {
104 intervals,
105 capacity,
106 disjunctive: false,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct SchedulingSolution {
114 pub schedule: Vec<ScheduledInterval>,
116 pub makespan: i64,
118}
119
120pub fn list_schedule(problem: &SchedulingProblem) -> Result<SchedulingSolution> {
122 let mut schedule = Vec::new();
123 let mut current_time = 0i64;
124
125 let mut intervals = problem.intervals.clone();
127 intervals.sort_by_key(|i| i.earliest_start);
128
129 for interval in intervals {
130 let start = current_time.max(interval.earliest_start);
132
133 if start + interval.duration > interval.latest_end {
134 return Err(crate::Error::infeasible(format!(
135 "Interval {} cannot be scheduled within time window",
136 interval.id
137 )));
138 }
139
140 schedule.push(ScheduledInterval {
141 start,
142 interval: interval.clone(),
143 });
144
145 if problem.disjunctive {
146 current_time = start + interval.duration;
147 }
148 }
149
150 let makespan = schedule.iter().map(|s| s.end()).max().unwrap_or(0);
151
152 Ok(SchedulingSolution { schedule, makespan })
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_simple_disjunctive() {
161 let intervals = vec![
162 Interval::new(0, 0, 100, 10),
163 Interval::new(1, 0, 100, 20),
164 Interval::new(2, 0, 100, 15),
165 ];
166
167 let problem = SchedulingProblem::disjunctive(intervals);
168 let solution = list_schedule(&problem).unwrap();
169
170 assert_eq!(solution.schedule.len(), 3);
171 assert_eq!(solution.makespan, 45); }
173
174 #[test]
175 fn test_interval_feasibility() {
176 let interval = Interval::new(0, 0, 10, 5);
177 assert!(interval.is_feasible());
178
179 let infeasible = Interval::new(0, 0, 3, 5);
180 assert!(!infeasible.is_feasible());
181 }
182}