From 629e8514000148bb4be4f0f13aaaad98db18d452 Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Wed, 8 May 2024 13:19:31 +0800 Subject: [PATCH 1/3] Update docs Signed-off-by: Arjo Chakravarty --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index c41e38c..fbde7c0 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,8 @@ This is a library that provides resource optimization constraints for multi-robot applications. More specifcally, we provide a very simple formulation for resource optimization and scheduling. A robot may request the use of a -resource like a charger for a fixed duration of time. The system will then assign the robot to said resource. +resource like a charger for a fixed duration of time within a given time range. The system will then assign the robot +to said resource. This is the type of resource constraint scheduling that this library can solve: ``` @@ -34,7 +35,6 @@ Alternatives: The requests can come in asynchronously. We can solve both optimally and suboptimally depoending on the complexity of the problem. A variety of algorithms have been implmented in this library including SAT based algorithms and greedy algorithms. -More information can be found in the tutorial documentation. For more details take a look at the tutorial: From f342da3b5fb17bf1122ac610d87e4dc6c7fbd3db Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Wed, 8 May 2024 13:21:33 +0800 Subject: [PATCH 2/3] Better struct renaming Signed-off-by: Arjo Chakravarty --- examples/benchmark.rs | 4 +- examples/discrete_vs_nodiscrete.rs | 13 +-- src/algorithms/greedy_solver.rs | 21 ++-- src/algorithms/kuhn_munkres.rs | 33 +++--- src/algorithms/sat.rs | 6 +- src/algorithms/sat_flexible_time_model.rs | 117 ++++++++++++++-------- src/database/mod.rs | 16 ++- src/discretization/fixed_timestep.rs | 13 ++- src/discretization/mod.rs | 6 +- src/lib.rs | 54 ++++------ src/scenario_generation/mod.rs | 12 ++- 11 files changed, 164 insertions(+), 131 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 86ba7c3..16c88c3 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -1,7 +1,7 @@ /// Benchmark -/// +/// /// This benchmark compares the speed at which we can calculate suboptimal solutions, optimal solutions using the assignment heuristic and -/// +/// use std::hint; use std::io::Write; use std::sync::atomic::AtomicBool; diff --git a/examples/discrete_vs_nodiscrete.rs b/examples/discrete_vs_nodiscrete.rs index 9bac871..e124b59 100644 --- a/examples/discrete_vs_nodiscrete.rs +++ b/examples/discrete_vs_nodiscrete.rs @@ -18,7 +18,7 @@ use rmf_reservations::database::ClockSource; use rmf_reservations::discretization::fixed_timestep::FixedTimestep; use rmf_reservations::discretization::DescretizationStrategy; use rmf_reservations::ReservationParameters; -use rmf_reservations::ReservationRequest; +use rmf_reservations::ReservationRequestAlternative; use rmf_reservations::StartTimeRange; #[derive(Default, Clone)] @@ -31,12 +31,11 @@ impl ClockSource for FakeClock { } } - fn generate_reservation_requests( time_range: chrono::Duration, alts: usize, reqs: usize, -) -> (Vec>, Vec) { +) -> (Vec>, Vec) { let mut result = vec![]; let mut rng = rand::thread_rng(); @@ -51,7 +50,7 @@ fn generate_reservation_requests( for req in 0..reqs { let mut alternatives = vec![]; for alternative in 0..alts { - let x = ReservationRequest { + let x = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: resource[alternative].clone(), duration: Some(Duration::minutes(200)), @@ -70,7 +69,10 @@ fn generate_reservation_requests( (result, resource) } -fn discretize(res: &Vec>, clock_source: FakeClock) -> Vec> { +fn discretize( + res: &Vec>, + clock_source: FakeClock, +) -> Vec> { let mut latest = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); for r in res { for v in r { @@ -83,7 +85,6 @@ fn discretize(res: &Vec>, clock_source: FakeClock) -> Ve remapper.discretize(res) } - fn main() { //for x in 6..10 { for time_length in 2..20 { diff --git a/src/algorithms/greedy_solver.rs b/src/algorithms/greedy_solver.rs index 7e4862f..d6fef08 100644 --- a/src/algorithms/greedy_solver.rs +++ b/src/algorithms/greedy_solver.rs @@ -14,7 +14,7 @@ use rand::Rng; use crate::{ cost_function::static_cost::StaticCost, utils::multimap::UniqueMultiHashMap, - ReservationRequest, ReservationSchedule, + ReservationRequestAlternative, ReservationSchedule, }; use super::SolverAlgorithm; @@ -29,7 +29,7 @@ pub struct ConflictTracker { base_resources: Vec, last_request_id: usize, resource_name_to_id: HashMap, - requests: HashMap>, + requests: HashMap>, // Maps requests by (request_id, resource_id) -> index in requests table request_reservation_idx: HashMap<(usize, usize), usize>, } @@ -51,7 +51,10 @@ impl ConflictTracker { } // Requires that ReservationRequests have a well defined start time - pub fn request_resources(&mut self, request: Vec) -> Option { + pub fn request_resources( + &mut self, + request: Vec, + ) -> Option { let req_id = self.last_request_id; for r_id in 0..request.len() { let resource = request[r_id].parameters.resource_name.clone(); @@ -69,7 +72,7 @@ impl ConflictTracker { pub fn generate_literals_and_remap_requests(&self) -> Problem { let mut fake_resources = vec![]; let mut fake_resource_mapping: HashMap = HashMap::new(); - let mut fake_requests: HashMap> = HashMap::new(); + let mut fake_requests: HashMap> = HashMap::new(); let mut id_to_res = HashMap::new(); let mut res_to_id = HashMap::new(); @@ -278,7 +281,7 @@ pub struct Problem { res_to_id: HashMap, id_to_res: HashMap<(usize, usize), String>, conflict_sets: HashMap>, - fake_requests: HashMap>, + fake_requests: HashMap>, } impl Problem { @@ -666,7 +669,7 @@ fn test_conflict_checker() { use crate::{cost_function::static_cost::StaticCost, ReservationParameters, StartTimeRange}; let resources = vec!["station1".to_string(), "station2".to_string()]; - let alternative1 = ReservationRequest { + let alternative1 = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: resources[0].clone(), start_time: StartTimeRange { @@ -678,7 +681,7 @@ fn test_conflict_checker() { cost_function: Arc::new(StaticCost::new(3.0)), }; - let alternative2 = ReservationRequest { + let alternative2 = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: resources[1].clone(), start_time: StartTimeRange { @@ -690,7 +693,7 @@ fn test_conflict_checker() { cost_function: Arc::new(StaticCost::new(6.0)), }; - let alternative1_cheaper = ReservationRequest { + let alternative1_cheaper = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: resources[0].clone(), start_time: StartTimeRange { @@ -719,7 +722,7 @@ fn test_conflict_checker() { assert!((solution.cost.0 - 8.0).abs() < 1.0); } -fn validate_optimal_solution(soln: &(Vec>, Vec)) {} +fn validate_optimal_solution(soln: &(Vec>, Vec)) {} #[cfg(test)] #[test] diff --git a/src/algorithms/kuhn_munkres.rs b/src/algorithms/kuhn_munkres.rs index e6d3aba..11b0b16 100644 --- a/src/algorithms/kuhn_munkres.rs +++ b/src/algorithms/kuhn_munkres.rs @@ -8,7 +8,7 @@ use ordered_float::OrderedFloat; use pathfinding::{kuhn_munkres, prelude::Weights}; use term_table::{row::Row, table_cell::TableCell, Table}; -use crate::{ReservationParameters, ReservationRequest, StartTimeRange}; +use crate::{ReservationParameters, ReservationRequestAlternative, StartTimeRange}; struct SparseAxisMasker { masked_idx_remapping: Vec>, @@ -100,7 +100,7 @@ fn test_sparse_axis_masker() { pub struct ReservationsKuhnMunkres { resources: Vec, resource_name_to_id: HashMap, - requests: HashMap>, + requests: HashMap>, // Maps requests by (request_id, resource_id) -> index in requests table request_reservation_idx: HashMap<(usize, usize), usize>, last_request_id: usize, @@ -196,7 +196,10 @@ impl ReservationsKuhnMunkres { } } - pub fn request_resources(&mut self, request: Vec) -> Option { + pub fn request_resources( + &mut self, + request: Vec, + ) -> Option { let req_id = self.last_request_id; for r_id in 0..request.len() { let resource = request[r_id].parameters.resource_name.clone(); @@ -309,7 +312,7 @@ fn test_kuhn_munkres_correctness() { let mut res_sys = ReservationsKuhnMunkres::create_with_resources(&resources); let req1 = vec![ - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 1".to_string(), duration: None, @@ -317,7 +320,7 @@ fn test_kuhn_munkres_correctness() { }, cost_function: Arc::new(StaticCost::new(10.0)), }, - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 2".to_string(), duration: None, @@ -327,7 +330,7 @@ fn test_kuhn_munkres_correctness() { }, ]; let req2 = vec![ - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 2".to_string(), duration: None, @@ -335,7 +338,7 @@ fn test_kuhn_munkres_correctness() { }, cost_function: Arc::new(StaticCost::new(10.0)), }, - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 3".to_string(), duration: None, @@ -375,7 +378,7 @@ fn test_resource_constraint() { let mut res_sys = ReservationsKuhnMunkres::create_with_resources(&resources); let req1 = vec![ - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 1".to_string(), duration: None, @@ -383,7 +386,7 @@ fn test_resource_constraint() { }, cost_function: Arc::new(StaticCost::new(10.0)), }, - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 2".to_string(), duration: None, @@ -393,7 +396,7 @@ fn test_resource_constraint() { }, ]; let req2 = vec![ - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 2".to_string(), duration: None, @@ -401,7 +404,7 @@ fn test_resource_constraint() { }, cost_function: Arc::new(StaticCost::new(10.0)), }, - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 3".to_string(), duration: None, @@ -443,7 +446,7 @@ fn test_rquest_constraint() { let mut res_sys = ReservationsKuhnMunkres::create_with_resources(&resources); let req1 = vec![ - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 1".to_string(), duration: None, @@ -451,7 +454,7 @@ fn test_rquest_constraint() { }, cost_function: Arc::new(StaticCost::new(10.0)), }, - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 2".to_string(), duration: None, @@ -461,7 +464,7 @@ fn test_rquest_constraint() { }, ]; let req2 = vec![ - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 2".to_string(), duration: None, @@ -469,7 +472,7 @@ fn test_rquest_constraint() { }, cost_function: Arc::new(StaticCost::new(10.0)), }, - ReservationRequest { + ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "Parking Spot 3".to_string(), duration: None, diff --git a/src/algorithms/sat.rs b/src/algorithms/sat.rs index f19b2ff..c0b5777 100644 --- a/src/algorithms/sat.rs +++ b/src/algorithms/sat.rs @@ -10,7 +10,7 @@ use varisat::{ExtendFormula, Lit, Solver}; use crate::{ cost_function::{self, static_cost::StaticCost}, - ReservationRequest, + ReservationRequestAlternative, }; use super::{greedy_solver::Problem, AlgorithmState, SolverAlgorithm}; @@ -446,7 +446,7 @@ impl SATSolver { pub fn generate_sat_devil( n_resources: usize, n_alt: usize, -) -> (Vec>, Vec) { +) -> (Vec>, Vec) { let mut sat_devil_resources: Vec<_> = (0..n_resources).map(|i| format!("{:?}", i)).collect(); let time_step = Duration::seconds(100); let start_time = Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).unwrap(); @@ -454,7 +454,7 @@ pub fn generate_sat_devil( for i in 0..n_resources { for j in 0..n_alt { - let req = ReservationRequest { + let req = ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: sat_devil_resources[(i + j) % n_resources].clone(), duration: Some(Duration::seconds(100)), diff --git a/src/algorithms/sat_flexible_time_model.rs b/src/algorithms/sat_flexible_time_model.rs index 3cdefb8..9f19a05 100644 --- a/src/algorithms/sat_flexible_time_model.rs +++ b/src/algorithms/sat_flexible_time_model.rs @@ -10,18 +10,39 @@ use varisat::{CnfFormula, ExtendFormula, Lit, Solver, Var}; use chrono::{prelude::*, Duration}; use crate::database::ClockSource; -use crate::ReservationRequest; +use crate::ReservationRequestAlternative; use super::{AlgorithmState, SolverAlgorithm}; +/// Snapshot of requests that need to be solved. +/// +/// This is how you specify a problem set that needs solving. #[derive(Debug, Clone)] pub struct Problem { - pub requests: Vec>, + /// A vector of requests. A solved problem will satisfy at least one "alternative" + /// within a request. + pub requests: Vec>, } +impl Problem { + /// Request one alternative out of a few + /// Returns the index of the request. This is useful for checking the assignment later on. + pub fn request_one_of(&mut self, alternatives: Vec) -> usize { + self.requests.push(alternatives); + return self.requests.len() - 1; + } +} + +/// Snapshot of a solution. A solved schedule contains a list of assingments for each resource #[derive(Debug, Clone)] pub struct Assignment { + /// For a given solution this refers to the alternative. + /// The first index refers to the request ID you retrieved from `request_one_of` or + /// the index of the alternatives. The second index refers to which alternative/resource + /// needs to be used. pub id: (usize, usize), + + /// Start time of a said assignment. pub start_time: chrono::DateTime, } @@ -52,8 +73,10 @@ fn check_consistency(assignments: &Vec, problem: &Problem) -> bool { return true; } -fn shrink_reservation_request(reservation_req: &ReservationRequest, time_window: DateTime) -> Option -{ +fn shrink_reservation_request( + reservation_req: &ReservationRequestAlternative, + time_window: DateTime, +) -> Option { if let Some(earliest_start) = reservation_req.parameters.start_time.earliest_start { if earliest_start > time_window { return None; @@ -66,27 +89,22 @@ fn shrink_reservation_request(reservation_req: &ReservationRequest, time_window: } } - Some(ReservationRequest { + Some(ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: reservation_req.parameters.resource_name.clone(), duration: reservation_req.parameters.duration.clone(), start_time: crate::StartTimeRange { earliest_start: reservation_req.parameters.start_time.earliest_start.clone(), - latest_start: Some(time_window) - } + latest_start: Some(time_window), + }, }, cost_function: reservation_req.cost_function.clone(), }) } -#[cfg(test)] -#[test] -fn test_shrink_reservation() -{ - -} - - +/// Solver for scenarios where there is a starting time range instead of a fixed starting time. +/// Note: The solvers in this class currently ignore thestarting time range. +/// You need to implement a clock source. The reason is that we needto have the current time as a starting point. pub struct SATFlexibleTimeModel { pub clock_source: CS, } @@ -116,13 +134,19 @@ impl SolverAlgo } impl SATFlexibleTimeModel { - + /// This class of solvers tries to pack all the alternatives into the shortest possible time window + /// It ignores the cost function. This is useful if you want to pack more items + /// - `problem` - A reservation problem you want to solve. + /// - `sender` - A channel by which the solver communicates its latest "best" solution. This is useful + /// for scenarios where the solver is taking too long and you need a feasible solution soon. You can listen on this + /// channel without calling `feasbility_analysis`. + /// - `stop` - A boolean by which you can tell the solver to stop solving. pub fn time_optimality_solver( &self, problem: &Problem, sender: Sender, - stop: std::sync::Arc) - { + stop: std::sync::Arc, + ) { let mut resources = HashMap::new(); let mut id_to_resource = vec![]; let mut var_list = HashMap::new(); @@ -313,9 +337,7 @@ impl SATFlexibl final_schedule.clear(); - - if let Some(time_window) = time_window - { + if let Some(time_window) = time_window { let mut formula = varisat::CnfFormula::new(); for (_, alternatives) in var_by_resource.iter() { for i in 0..alternatives.len() { @@ -323,14 +345,19 @@ impl SATFlexibl let alt_ij = alternatives[i]; let alt_km = alternatives[j]; - let alt_ij_shrink = shrink_reservation_request(&problem.requests[alt_ij.0][alt_ij.1], time_window); - let alt_km_shrink = shrink_reservation_request(&problem.requests[alt_km.0][alt_km.1], time_window); + let alt_ij_shrink = shrink_reservation_request( + &problem.requests[alt_ij.0][alt_ij.1], + time_window, + ); + let alt_km_shrink = shrink_reservation_request( + &problem.requests[alt_km.0][alt_km.1], + time_window, + ); if alt_ij_shrink.is_none() { // Ban the entire alternative let x_ij = var_list.get(&alt_ij).expect("Something went wrong"); formula.add_clause(&[Lit::from_var(*x_ij, false)]); - } if alt_ij_shrink.is_none() { @@ -339,7 +366,7 @@ impl SATFlexibl formula.add_clause(&[Lit::from_var(*x_km, false)]); } - if let Some(alt_ij_shrink) = alt_ij_shrink{ + if let Some(alt_ij_shrink) = alt_ij_shrink { if let Some(alt_km_shrink) = alt_km_shrink { let Some(list_ij) = comes_after_vars.get(&alt_ij) else { panic!("For some reason"); @@ -351,12 +378,16 @@ impl SATFlexibl }; let X_kmij = list_km.get(&alt_ij).expect(""); - if !alt_ij_shrink.can_be_scheduled_after(&alt_km_shrink.parameters) { + if !alt_ij_shrink + .can_be_scheduled_after(&alt_km_shrink.parameters) + { // ij cannot be after km formula.add_clause(&[Lit::from_var(*X_ijkm, false)]); } - if !alt_km_shrink.can_be_scheduled_after(&alt_ij_shrink.parameters) { + if !alt_km_shrink + .can_be_scheduled_after(&alt_ij_shrink.parameters) + { // ij cannot be after km formula.add_clause(&[Lit::from_var(*X_kmij, false)]); } @@ -522,18 +553,26 @@ impl SATFlexibl } if ok { - time_window = final_schedule.iter().filter(|(_resource, assignment)| { - assignment.len() != 0 - }).map(|(_resource, assignment)| { - let assignment = &assignment[assignment.len()-1]; - assignment.start_time - }).max(); + time_window = final_schedule + .iter() + .filter(|(_resource, assignment)| assignment.len() != 0) + .map(|(_resource, assignment)| { + let assignment = &assignment[assignment.len() - 1]; + assignment.start_time + }) + .max(); } else { println!("Could not solve"); } } } + /// Checks if a set of requests is feasible. Given a problem we see if there is a way to schedule the solution while ignoring the + /// cost function. + /// - `problem` - Takes in th eproblem that needs to be optimized. + /// - `stop` - Takes in an atomic boolean that allows you to stop the computation from another thread. + /// Returns a result containing an example feasible schedule if OK. Otherwise, returns an error. + /// The feasible schedule is a HashMap where the key of the hashmap is the resource and thevalue of the hashmap is the assigned alternative. pub fn feasbility_analysis( &self, problem: &Problem, @@ -906,7 +945,7 @@ fn test_flexible_one_item_sat_solver() { let current_time = chrono::Utc::now(); - let req1 = vec![ReservationRequest { + let req1 = vec![ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "Resource1".to_string(), duration: Some(chrono::Duration::seconds(100)), @@ -944,7 +983,7 @@ fn test_flexible_two_items_sat_solver() { let current_time = chrono::Utc::now(); - let req1 = vec![ReservationRequest { + let req1 = vec![ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "Resource1".to_string(), duration: Some(chrono::Duration::seconds(100)), @@ -957,7 +996,7 @@ fn test_flexible_two_items_sat_solver() { }]; let req2 = vec![ - ReservationRequest { + ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "Resource1".to_string(), duration: Some(chrono::Duration::seconds(100)), @@ -968,7 +1007,7 @@ fn test_flexible_two_items_sat_solver() { }, cost_function: Arc::new(static_cost::StaticCost::new(1.0)), }, - ReservationRequest { + ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "Resource1".to_string(), duration: Some(chrono::Duration::seconds(100)), @@ -1015,7 +1054,7 @@ fn test_flexible_n_items_sat_solver() { let task_dur = Duration::seconds(100); let mut requests = vec![]; for i in 0..n { - requests.push(vec![ReservationRequest { + requests.push(vec![ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "Resource1".to_string(), duration: Some(task_dur), @@ -1060,7 +1099,7 @@ fn test_flexible_no_soln_sat_solver() { let task_dur = Duration::seconds(100); let mut requests = vec![]; for i in 0..n { - requests.push(vec![ReservationRequest { + requests.push(vec![ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "Resource1".to_string(), duration: Some(task_dur), diff --git a/src/database/mod.rs b/src/database/mod.rs index 9b17c87..6aad475 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -14,10 +14,9 @@ use crate::{ self, wait_points::{WaitPointInfo, WaitPointRequest, WaitPointSystem}, }, - ReservationRequest, StartTimeRange, + ReservationRequestAlternative, StartTimeRange, }; - /// Ticket issued when making a request, #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Ticket { @@ -46,11 +45,10 @@ pub(crate) struct Snapshot { pub(crate) metadata: T, } - /// A reservation system that pub struct FixedTimeReservationSystem { resources: Vec, - record: HashMap>, + record: HashMap>, claims: HashMap, max_id: usize, async_executor: AsyncExecutor, @@ -73,7 +71,7 @@ impl FixedTimeReservationSystem { pub fn request_resources( &mut self, - alternatives: Vec, + alternatives: Vec, ) -> Result { for alt in &alternatives { if alt.parameters.start_time.earliest_start != alt.parameters.start_time.latest_start @@ -160,7 +158,7 @@ struct FlexibleTimeReservationSystemMetadata { } pub struct FlexibleTimeReservationSystem { - record: HashMap>, + record: HashMap>, claims: HashMap, max_id: usize, async_executor: AsyncExecutor< @@ -215,7 +213,7 @@ impl, + alternatives: Vec, ) -> Result { self.record.insert(self.max_id, alternatives); let result = Ticket { count: self.max_id }; @@ -415,7 +413,7 @@ fn test_sat_flexible_time_model() { let mut flexible_ressys: FlexibleTimeReservationSystem = FlexibleTimeReservationSystem::default(); - let alternatives1 = vec![ReservationRequest { + let alternatives1 = vec![ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "res1".to_string(), duration: Some(Duration::minutes(10)), @@ -428,7 +426,7 @@ fn test_sat_flexible_time_model() { }]; let ticket1 = flexible_ressys.request_resources(alternatives1).unwrap(); - let alternatives2 = vec![ReservationRequest { + let alternatives2 = vec![ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: "res2".to_string(), duration: Some(Duration::minutes(10)), diff --git a/src/discretization/fixed_timestep.rs b/src/discretization/fixed_timestep.rs index a78def0..9cdc03a 100644 --- a/src/discretization/fixed_timestep.rs +++ b/src/discretization/fixed_timestep.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use chrono::{DateTime, Duration, Utc}; use crate::{ - cost_function::static_cost::StaticCost, database::ClockSource, ReservationRequest, + cost_function::static_cost::StaticCost, database::ClockSource, ReservationRequestAlternative, StartTimeRange, }; @@ -30,8 +30,8 @@ impl FixedTimestep { impl DescretizationStrategy for FixedTimestep { fn discretize( &mut self, - requests: &Vec>, - ) -> Vec> { + requests: &Vec>, + ) -> Vec> { let mut result = vec![]; for request_id in 0..requests.len() { @@ -64,14 +64,17 @@ impl DescretizationStrategy for FixedTimestep { .cost_function .cost(¶ms, ¤t_time); - let new_reservation = ReservationRequest { + let new_reservation = ReservationRequestAlternative { parameters: params, cost_function: Arc::new(StaticCost::new(cost)), }; broken_down_options.push(new_reservation); current_time += self.timestep; - self.remapping.insert((result.len(), broken_down_options.len() -1), (request_id, res)); + self.remapping.insert( + (result.len(), broken_down_options.len() - 1), + (request_id, res), + ); } } result.push(broken_down_options); diff --git a/src/discretization/mod.rs b/src/discretization/mod.rs index 70082bf..cac1680 100644 --- a/src/discretization/mod.rs +++ b/src/discretization/mod.rs @@ -1,4 +1,4 @@ -use crate::ReservationRequest; +use crate::ReservationRequestAlternative; pub mod fixed_timestep; @@ -8,8 +8,8 @@ pub trait DescretizationStrategy { /// Discretize the objects and produce an equivalent fn discretize( &mut self, - requests: &Vec>, - ) -> Vec>; + requests: &Vec>, + ) -> Vec>; /// Remap the new problem to the old problem. fn remap(&self, ticket_id: &(usize, usize)) -> Option<(usize, usize)>; diff --git a/src/lib.rs b/src/lib.rs index cc31de8..dfb1bc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ impl StartTimeRange { } } - /// Create a time range which starts exactly at some given time. + /// Create a time range which starts exactly at some given time. pub fn exactly_at(time: &DateTime) -> Self { Self { earliest_start: Some(time.clone()), @@ -109,13 +109,13 @@ pub trait CostFunction { /// Reservation Request represents one possible alternative reservation #[derive(Clone)] -pub struct ReservationRequest { +pub struct ReservationRequestAlternative { /// The parameters pub parameters: ReservationParameters, /// Cost function to use pub cost_function: Arc, } -impl fmt::Debug for ReservationRequest { +impl fmt::Debug for ReservationRequestAlternative { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ReservationRequest") .field("parameters", &self.parameters) @@ -123,7 +123,7 @@ impl fmt::Debug for ReservationRequest { } } -impl ReservationRequest { +impl ReservationRequestAlternative { /// Check if a certain set of parameters satisfies this request fn satisfies_request(&self, start_time: &DateTime, duration: Option) -> bool { if let Some(earliest_start_time) = self.parameters.start_time.earliest_start { @@ -189,14 +189,6 @@ struct ReservationSchedule { schedule: BTreeMap, Assignment>, } - - -#[derive(Clone, Debug, PartialEq, Hash, Eq)] -enum SchedChange { - Remove(DateTime), - Add(DateTime, Assignment), -} - impl ReservationSchedule { fn new() -> Self { Self { @@ -219,11 +211,10 @@ impl ReservationSchedule { self.schedule = self.schedule.split_off(&time); } - /// Returns a list of assignments that may have a potential conflict with the relevant ReservationRequest fn check_potential_conflict( &self, - reservation: &ReservationRequest, + reservation: &ReservationRequestAlternative, ) -> std::collections::btree_map::Range<'_, DateTime, Assignment> { let earliest_time = reservation.parameters.start_time.earliest_start; @@ -294,23 +285,14 @@ impl ReservationSchedule { self.schedule.range(..) } } - -} - - - -struct NoCost {} - -impl CostFunction for NoCost { - fn cost(&self, _parameters: &ReservationParameters, _instant: &DateTime) -> f64 { - 0f64 - } } #[cfg(test)] #[test] fn test_satisfies_request() { - let req = ReservationRequest { + use crate::cost_function::static_cost::StaticCost; + + let req = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "test".to_string(), duration: Some(Duration::minutes(30)), @@ -319,7 +301,7 @@ fn test_satisfies_request() { latest_start: None, }, }, - cost_function: Arc::new(NoCost {}), + cost_function: Arc::new(StaticCost::new(0.0)), }; let start_time = Utc.with_ymd_and_hms(2023, 7, 8, 6, 10, 11).unwrap(); @@ -327,7 +309,7 @@ fn test_satisfies_request() { assert!(req.satisfies_request(&start_time, Some(Duration::minutes(20))) == false); assert!(req.satisfies_request(&start_time, None) == false); - let req = ReservationRequest { + let req = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "test".to_string(), duration: Some(Duration::minutes(30)), @@ -336,7 +318,7 @@ fn test_satisfies_request() { latest_start: Some(Utc.with_ymd_and_hms(2023, 7, 8, 8, 10, 11).unwrap()), }, }, - cost_function: Arc::new(NoCost {}), + cost_function: Arc::new(StaticCost::new(0.0)), }; // Too early @@ -355,10 +337,12 @@ fn test_satisfies_request() { #[cfg(test)] #[test] fn test_conflict_checker() { - let cost_func = Arc::new(NoCost {}); + use crate::cost_function::static_cost::StaticCost; + + let cost_func = Arc::new(StaticCost::new(0.0)); // Check for an indefinite reservation with no specification on - let indefinite_no_constraints = ReservationRequest { + let indefinite_no_constraints = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "resource1".to_string(), start_time: StartTimeRange { @@ -370,7 +354,7 @@ fn test_conflict_checker() { cost_function: cost_func.clone(), }; - let indefinite_with_constraints = ReservationRequest { + let indefinite_with_constraints = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "resource1".to_string(), start_time: StartTimeRange { @@ -383,7 +367,7 @@ fn test_conflict_checker() { }; // Definite requet with fixed time bound - let definite_request_starting_with_specified_start_time = ReservationRequest { + let definite_request_starting_with_specified_start_time = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "resource1".to_string(), start_time: StartTimeRange { @@ -395,7 +379,7 @@ fn test_conflict_checker() { cost_function: cost_func.clone(), }; - let definite_request_with_no_earliest = ReservationRequest { + let definite_request_with_no_earliest = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "resource1".to_string(), start_time: StartTimeRange { @@ -407,7 +391,7 @@ fn test_conflict_checker() { cost_function: cost_func.clone(), }; - let definite_request_with_no_latest = ReservationRequest { + let definite_request_with_no_latest = ReservationRequestAlternative { parameters: ReservationParameters { resource_name: "resource1".to_string(), start_time: StartTimeRange { diff --git a/src/scenario_generation/mod.rs b/src/scenario_generation/mod.rs index 003b6d2..70f56fa 100644 --- a/src/scenario_generation/mod.rs +++ b/src/scenario_generation/mod.rs @@ -3,14 +3,16 @@ use std::{collections::HashMap, sync::Arc}; use chrono::{Duration, TimeZone, Utc}; use rand::Rng; -use crate::{cost_function::static_cost::StaticCost, ReservationRequest, ReservationSchedule}; +use crate::{ + cost_function::static_cost::StaticCost, ReservationRequestAlternative, ReservationSchedule, +}; // TODO(arjo) there is bug in this that generates rubbish occassionally. pub fn generate_test_scenario_with_known_best( num_resources: usize, max_requests_per_resource: usize, num_conflicts: usize, -) -> (Vec>, Vec) { +) -> (Vec>, Vec) { let resources = Vec::from_iter((0..num_resources).map(|m| format!("Station {}", m))); let mut rng = rand::thread_rng(); @@ -27,7 +29,7 @@ pub fn generate_test_scenario_with_known_best( for i in 0..res_size { last_start_time += Duration::minutes(rng.gen_range(0..60)); let duration = Duration::minutes(rng.gen_range(20..90)); - let request = ReservationRequest { + let request = ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: res.clone(), duration: Some(duration), @@ -71,7 +73,7 @@ pub fn generate_test_scenario_with_known_best( for s in &schedule.schedule { if idx == res_id { // Lets create a reservation to disrupt the previous two reservations - let req = ReservationRequest { + let req = ReservationRequestAlternative { parameters: crate::ReservationParameters { resource_name: resource_name.clone(), duration: Some((*s.0 + s.1 .2.unwrap()) - prev_time), @@ -88,4 +90,4 @@ pub fn generate_test_scenario_with_known_best( } (results_vec, resources) -} \ No newline at end of file +} From 6ad32ce78ca62dc35cd2315fa69971c04874faf2 Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Wed, 8 May 2024 13:21:51 +0800 Subject: [PATCH 3/3] Update toolchain to nightly Signed-off-by: Arjo Chakravarty --- .github/workflows/rust.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9fd45e0..b3f9fab 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,6 +16,12 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + components: rustfmt, clippy - name: Build run: cargo build --verbose - name: Run tests