-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from vcoppe/talentsched
- Loading branch information
Showing
21 changed files
with
865 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright 2020 Xavier Gillard | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
// this software and associated documentation files (the "Software"), to deal in | ||
// the Software without restriction, including without limitation the rights to | ||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
// the Software, and to permit persons to whom the Software is furnished to do so, | ||
// subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
|
||
use std::{num::ParseIntError, path::Path, fs::File, io::{BufReader, BufRead}}; | ||
|
||
/// This enumeration simply groups the kind of errors that might occur when parsing an | ||
/// instance from file. There can be io errors (file unavailable ?), format error | ||
/// (e.g. the file is not an instance but contains the text of your next paper), | ||
/// or parse int errors (which are actually a variant of the format errror since it tells | ||
/// you that the parser expected an integer number but got ... something else). | ||
#[derive(Debug, thiserror::Error)] | ||
pub enum Error { | ||
/// There was an io related error | ||
#[error("io error {0}")] | ||
Io(#[from] std::io::Error), | ||
/// The parser expected to read somehting that was an integer but got some garbage | ||
#[error("parse int {0}")] | ||
ParseInt(#[from] ParseIntError), | ||
/// The file was not properly formatted. | ||
#[error("ill formed instance")] | ||
Format, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct TalentSchedInstance { | ||
pub nb_scenes: usize, | ||
pub nb_actors: usize, | ||
pub cost: Vec<usize>, | ||
pub duration: Vec<usize>, | ||
pub actors: Vec<Vec<usize>>, | ||
} | ||
|
||
/// This function is used to read a talentsched instance from file. It returns either a | ||
/// talentsched instance if everything went on well or an error describing the problem. | ||
pub fn read_instance<P: AsRef<Path>>(fname: P) -> Result<TalentSchedInstance, Error> { | ||
let f = File::open(fname)?; | ||
let f = BufReader::new(f); | ||
|
||
let mut nb_scenes = 0; | ||
let mut nb_actors = 0; | ||
let mut cost = vec![]; | ||
let mut duration = vec![]; | ||
let mut actors = vec![]; | ||
|
||
let mut lines = f.lines(); | ||
lines.next(); // instance name | ||
|
||
let mut i = 0; | ||
|
||
for line in &mut lines { | ||
let line = line?; | ||
if line.is_empty() { | ||
continue; | ||
} | ||
|
||
let mut data = line.trim().split_ascii_whitespace(); | ||
if i == 0 { | ||
nb_scenes = data.next().ok_or(Error::Format)?.parse()?; | ||
match data.next() { | ||
Some(val) => { | ||
nb_actors = val.parse()?; | ||
i += 1; | ||
}, | ||
None => (), | ||
} | ||
Check warning on line 81 in ddo/examples/talentsched/io_utils.rs
|
||
} else if i == 1 { | ||
nb_actors = data.next().ok_or(Error::Format)?.parse()?; | ||
} else if i < nb_actors + 2 { | ||
let mut scenes = vec![]; | ||
for _ in 0..nb_scenes { | ||
scenes.push(data.next().ok_or(Error::Format)?.parse()?); | ||
} | ||
actors.push(scenes); | ||
cost.push(data.next().ok_or(Error::Format)?.parse()?); | ||
} else { | ||
for _ in 0..nb_scenes { | ||
duration.push(data.next().ok_or(Error::Format)?.parse()?); | ||
} | ||
} | ||
|
||
i += 1 | ||
} | ||
|
||
Ok(TalentSchedInstance { nb_scenes, nb_actors, cost, duration, actors }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright 2020 Xavier Gillard | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
// this software and associated documentation files (the "Software"), to deal in | ||
// the Software without restriction, including without limitation the rights to | ||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
// the Software, and to permit persons to whom the Software is furnished to do so, | ||
// subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
|
||
//! This example uses ddo to solve the Talent Scheduling problem | ||
//! Instances can be downloaded from https://people.eng.unimelb.edu.au/pstuckey/talent/ | ||
|
||
use std::{time::{Duration, Instant}}; | ||
|
||
use clap::Parser; | ||
use ddo::*; | ||
|
||
use crate::{io_utils::read_instance, model::{TalentSched, TalentSchedRelax, TalentSchedRanking}}; | ||
|
||
mod model; | ||
mod io_utils; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
/// This structure uses `clap-derive` annotations and define the arguments that can | ||
/// be passed on to the executable solver. | ||
#[derive(Parser, Debug)] | ||
#[command(author, version, about, long_about = None)] | ||
struct Args { | ||
/// The path to the instance file | ||
fname: String, | ||
/// The number of concurrent threads | ||
#[clap(short, long, default_value = "8")] | ||
threads: usize, | ||
/// The maximum amount of time you would like this solver to run | ||
#[clap(short, long)] | ||
duration: Option<u64>, | ||
/// The maximum number of nodes per layer | ||
#[clap(short, long)] | ||
width: Option<usize>, | ||
} | ||
|
||
/// An utility function to return an max width heuristic that can either be a fixed width | ||
/// policy (if w is fixed) or an adaptative policy returning the number of unassigned variables | ||
/// in the overall problem. | ||
fn max_width<P: Problem>(p: &P, w: Option<usize>) -> Box<dyn WidthHeuristic<P::State> + Send + Sync> { | ||
if let Some(w) = w { | ||
Box::new(FixedWidth(w)) | ||
} else { | ||
Box::new(NbUnassignedWitdh(p.nb_variables())) | ||
} | ||
} | ||
/// An utility function to return a cutoff heuristic that can either be a time budget policy | ||
/// (if timeout is fixed) or no cutoff policy. | ||
fn cutoff(timeout: Option<u64>) -> Box<dyn Cutoff + Send + Sync> { | ||
if let Some(t) = timeout { | ||
Box::new(TimeBudget::new(Duration::from_secs(t))) | ||
} else { | ||
Box::new(NoCutoff) | ||
} | ||
} | ||
|
||
/// This is your executable's entry point. It is the place where all the pieces are put together | ||
/// to create a fast an effective solver for the talentsched problem. | ||
fn main() { | ||
let args = Args::parse(); | ||
let fname = &args.fname; | ||
let instance = read_instance(fname).unwrap(); | ||
let problem = TalentSched::new(instance); | ||
let relaxation = TalentSchedRelax::new(problem.clone()); | ||
let ranking = TalentSchedRanking; | ||
|
||
let width = max_width(&problem, args.width); | ||
let dominance = EmptyDominanceChecker::default(); | ||
let cutoff = cutoff(args.duration); | ||
let mut fringe = NoDupFringe::new(MaxUB::new(&ranking)); | ||
|
||
let mut solver = DefaultBarrierSolver::custom( | ||
&problem, | ||
&relaxation, | ||
&ranking, | ||
width.as_ref(), | ||
&dominance, | ||
cutoff.as_ref(), | ||
&mut fringe, | ||
args.threads, | ||
); | ||
|
||
let start = Instant::now(); | ||
let Completion{ is_exact, best_value } = solver.maximize(); | ||
|
||
let duration = start.elapsed(); | ||
let upper_bound = - solver.best_upper_bound(); | ||
let lower_bound = - solver.best_lower_bound(); | ||
let gap = solver.gap(); | ||
let best_solution: Option<Vec<_>> = solver.best_solution() | ||
.map(|mut decisions|{ | ||
decisions.sort_unstable_by_key(|d| d.variable.id()); | ||
decisions.iter() | ||
.map(|d| d.value) | ||
.collect() | ||
}); | ||
let best_solution = best_solution.unwrap_or_default(); | ||
|
||
println!("Duration: {:.3} seconds", duration.as_secs_f32()); | ||
println!("Objective: {}", best_value.map(|v| -v).unwrap_or(-1)); | ||
println!("Upper Bnd: {}", upper_bound); | ||
println!("Lower Bnd: {}", lower_bound); | ||
println!("Gap: {:.3}", gap); | ||
println!("Aborted: {}", !is_exact); | ||
println!("Solution: {:?}", best_solution); | ||
} |
Oops, something went wrong.