rustsat_tools/encodings/cnf/
knapsack.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! # CNF (Multi-Criteria) Knapsack Encoding

use rustsat::{
    clause,
    encodings::pb,
    instances::{fio::dimacs, BasicVarManager, Cnf, ManageVars},
    types::{constraints::PbConstraint, Lit},
};

use crate::encodings::knapsack::Knapsack;

enum Clause {
    /// Clause of the capacity encoding
    Capacity(<Cnf as IntoIterator>::IntoIter),
    /// Soft clause for item and objective
    Soft(usize, usize),
}

pub struct Encoding {
    data: Knapsack,
    next_clause: Clause,
}

impl Encoding {
    pub fn new<PBE>(data: Knapsack) -> Self
    where
        PBE: pb::BoundUpper + FromIterator<(Lit, usize)>,
    {
        let mut vm = BasicVarManager::default();
        let cap_constr = match PbConstraint::new_ub(
            data.items
                .iter()
                .map(|item| (vm.new_var().pos_lit(), item.weight as isize)),
            data.capacity as isize,
        ) {
            PbConstraint::Ub(constr) => constr,
            _ => unreachable!(),
        };
        let mut cap_cnf = Cnf::new();
        PBE::encode_ub_constr(cap_constr, &mut cap_cnf, &mut vm).unwrap();
        Self {
            data,
            next_clause: Clause::Capacity(cap_cnf.into_iter()),
        }
    }
}

impl Iterator for Encoding {
    type Item = dimacs::McnfLine;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match &mut self.next_clause {
                Clause::Capacity(iter) => {
                    let next = iter.next();
                    if next.is_some() {
                        return next.map(dimacs::McnfLine::Hard);
                    }
                    self.next_clause = Clause::Soft(0, 0);
                }
                Clause::Soft(iidx, oidx) => {
                    let iidx = *iidx;
                    let oidx = *oidx;
                    if iidx >= self.data.items.len() {
                        return None;
                    }
                    if oidx >= self.data.items[iidx].values.len() {
                        self.next_clause = Clause::Soft(iidx + 1, 0);
                        continue;
                    }
                    self.next_clause = Clause::Soft(iidx, oidx + 1);
                    return Some(dimacs::McnfLine::Soft(
                        clause![Lit::positive(iidx as u32)],
                        self.data.items[iidx].values[oidx],
                        oidx,
                    ));
                }
            }
        }
    }
}