use std::{path::{Path, PathBuf}, fs::File, io::{BufReader, BufRead}, num::ParseIntError, sync::Arc};
use clap::Parser;
use ddo::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KnapsackState {
depth: usize,
capacity: usize
}
pub struct Knapsack {
capacity: usize,
profit: Vec<usize>,
weight: Vec<usize>,
}
const TAKE_IT: isize = 1;
const LEAVE_IT_OUT: isize = 0;
impl Problem for Knapsack {
type State = KnapsackState;
fn nb_variables(&self) -> usize {
self.profit.len()
}
fn for_each_in_domain(&self, variable: Variable, state: &Self::State, f: &mut dyn DecisionCallback)
{
if state.capacity >= self.weight[variable.id()] {
f.apply(Decision { variable, value: TAKE_IT });
f.apply(Decision { variable, value: LEAVE_IT_OUT });
} else {
f.apply(Decision { variable, value: LEAVE_IT_OUT });
}
}
fn initial_state(&self) -> Self::State {
KnapsackState{ depth: 0, capacity: self.capacity }
}
fn initial_value(&self) -> isize {
0
}
fn transition(&self, state: &Self::State, dec: Decision) -> Self::State {
let mut ret = *state;
ret.depth += 1;
if dec.value == TAKE_IT {
ret.capacity -= self.weight[dec.variable.id()]
}
ret
}
fn transition_cost(&self, _state: &Self::State, _: &Self::State, dec: Decision) -> isize {
self.profit[dec.variable.id()] as isize * dec.value
}
fn next_variable(&self, depth: usize, _: &mut dyn Iterator<Item = &Self::State>) -> Option<Variable> {
let n = self.nb_variables();
if depth < n {
Some(Variable(depth))
} else {
None
}
}
}
pub struct KPRelax<'a>{pub pb: &'a Knapsack}
impl Relaxation for KPRelax<'_> {
type State = KnapsackState;
fn merge(&self, states: &mut dyn Iterator<Item = &Self::State>) -> Self::State {
states.max_by_key(|node| node.capacity).copied().unwrap()
}
fn relax(&self, _source: &Self::State, _dest: &Self::State, _merged: &Self::State, _decision: Decision, cost: isize) -> isize {
cost
}
fn fast_upper_bound(&self, state: &Self::State) -> isize {
let mut tot = 0;
for var in state.depth..self.pb.nb_variables() {
if self.pb.weight[var] <= state.capacity {
tot += self.pb.profit[var];
}
}
tot as isize
}
}
pub struct KPranking;
impl StateRanking for KPranking {
type State = KnapsackState;
fn compare(&self, a: &Self::State, b: &Self::State) -> std::cmp::Ordering {
a.capacity.cmp(&b.capacity)
}
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
fname: String,
#[clap(short, long, default_value = "8")]
threads: usize,
#[clap(short, long, default_value = "30")]
duration: u64,
#[clap(short, long)]
width: Option<usize>,
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("io error {0}")]
Io(#[from] std::io::Error),
#[error("parse int {0}")]
ParseInt(#[from] ParseIntError),
#[error("ill formed instance")]
Format,
}
pub fn read_instance<P: AsRef<Path>>(fname: P) -> Result<Knapsack, Error> {
let f = File::open(fname)?;
let f = BufReader::new(f);
let mut is_first = true;
let mut n = 0;
let mut count = 0;
let mut capa = 0;
let mut profit = vec![];
let mut weight = vec![];
for line in f.lines() {
let line = line?;
if line.starts_with('c') {
continue;
}
if is_first {
is_first = false;
let mut ab = line.split(' ');
n = ab.next().ok_or(Error::Format)?.parse()?;
capa = ab.next().ok_or(Error::Format)?.parse()?;
} else {
if count >= n {
break;
}
let mut ab = line.split(' ');
profit.push(ab.next().ok_or(Error::Format)?.parse()?);
weight.push(ab.next().ok_or(Error::Format)?.parse()?);
count += 1;
}
}
Ok(Knapsack { capacity: capa, profit, weight })
}
fn locate(id: &str) -> PathBuf {
PathBuf::new()
.join(env!("CARGO_MANIFEST_DIR"))
.join("../resources/knapsack/")
.join(id)
}
fn main() {
let fname = locate("f1_l-d_kp_10_269");
let problem = read_instance(fname).unwrap();
let relaxation = KPRelax{pb: &problem};
let ranking = KPranking;
let mut cache = SimpleCache::default();
cache.initialize(&problem);
let dominance = EmptyDominanceChecker::default();
let residual = SubProblem {
state: Arc::new(problem.initial_state()),
value: 0,
path: vec![],
ub: isize::MAX,
depth: 0
};
let input = CompilationInput {
comp_type: CompilationType::Relaxed,
problem: &problem,
relaxation: &relaxation,
ranking: &ranking,
cutoff: &NoCutoff,
max_width: 5,
residual: &residual,
best_lb: isize::MIN,
cache: &cache,
dominance: &dominance,
};
let mut clean = Mdd::<KnapsackState, {FRONTIER}>::new();
_ = clean.compile(&input);
let config = VizConfigBuilder::default()
.show_deleted(true)
.group_merged(true)
.build()
.unwrap();
let dot = clean.as_graphviz(&config);
println!("{dot}");
}