rustsat_tools/encodings/pb/
facilitylocation.rs1use rustsat::{
4 encodings::atomics,
5 instances::fio::opb,
6 lit,
7 types::{constraints::CardConstraint, Lit},
8};
9
10use crate::encodings::facilitylocation::FacilityLocation;
11
12#[derive(Default)]
13enum Line {
14 #[default]
16 Hint,
17 Description,
19 Objective(usize),
21 OneFacility(usize),
23 MustOpen(usize, usize),
26}
27
28pub struct Encoding {
29 data: FacilityLocation,
30 next_line: Option<Line>,
31}
32
33impl Encoding {
34 pub fn new(data: FacilityLocation) -> Self {
35 Self {
36 data,
37 next_line: Some(Line::default()),
38 }
39 }
40}
41
42impl Iterator for Encoding {
43 type Item = opb::FileLine<<Vec<(Lit, usize)> as IntoIterator>::IntoIter>;
44
45 fn next(&mut self) -> Option<Self::Item> {
46 let selected = |customer: usize, facility: usize| {
47 lit![(customer * self.data.n_facilities() + facility) as u32]
48 };
49 let opening = |facility: usize| {
50 lit![(self.data.n_facilities() * self.data.n_customers() + facility) as u32]
51 };
52 match self.next_line.take() {
53 Some(line) => Some(match line {
54 Line::Hint => {
55 self.next_line = Some(Line::Description);
56 opb::FileLine::Comment(format!(
57 "#variable= {} #constraint= {}",
58 (self.data.n_customers() + 1) * self.data.n_facilities(),
59 self.data.n_customers() * (self.data.n_facilities() + 1)
60 ))
61 }
62 Line::Description => {
63 self.next_line = Some(Line::Objective(0));
64 opb::FileLine::Comment(
65 "MO uncapacitated facility location instance generated by RustSAT"
66 .to_string(),
67 )
68 }
69 Line::Objective(objidx) => {
70 let data = &self.data;
71 let opening_cost = (0..self.data.n_facilities())
72 .map(|fac| (opening(fac), data.opening_cost(objidx, fac)));
73 let obj: Vec<_> = (0..self.data.n_customers())
74 .flat_map(|customer| {
75 (0..self.data.n_facilities()).map(move |fac| {
76 (
77 selected(customer, fac),
78 data.supply_cost(objidx, customer, fac),
79 )
80 })
81 })
82 .chain(opening_cost)
83 .collect();
84 self.next_line = Some(if objidx + 1 < self.data.n_objs() {
85 Line::Objective(objidx + 1)
86 } else {
87 Line::OneFacility(0)
88 });
89 opb::FileLine::Objective(obj.into_iter())
90 }
91 Line::OneFacility(customer) => {
92 let constr = CardConstraint::new_eq(
93 (0..self.data.n_facilities()).map(|fac| selected(customer, fac)),
94 1,
95 );
96 self.next_line = Some(if customer + 1 < self.data.n_customers() {
97 Line::OneFacility(customer + 1)
98 } else {
99 Line::MustOpen(0, 0)
100 });
101 opb::FileLine::Card(constr)
102 }
103 Line::MustOpen(fac, customer) => {
104 let constr = atomics::lit_impl_lit(selected(customer, fac), opening(fac));
105 self.next_line = if customer + 1 < self.data.n_customers() {
106 Some(Line::MustOpen(fac, customer + 1))
107 } else if fac + 1 < self.data.n_facilities() {
108 Some(Line::MustOpen(fac + 1, 0))
109 } else {
110 None
111 };
112 opb::FileLine::Clause(constr)
113 }
114 }),
115 None => None,
116 }
117 }
118}