flag_algebra/tools/
reduction.rs

1//! WIP tools to minimize certificates
2
3use crate::flag::Flag;
4use crate::sdp::*;
5use crate::tools::*;
6use log::*;
7use sprs::CsMat;
8
9/// A higher level object to try several selectors on a problem
10#[derive(Debug, Clone)]
11pub struct FlagSolver<F: Flag> {
12    pb: Problem<f64, F>,
13    name: String,
14    pub optimal_value: Option<f64>,
15    /// If there is an optimal value, this correspond to a selector giving it
16    select: Selector,
17    /// The selector corresponding to the file name.spda
18    select_sdpa_file: Option<Selector>,
19    /// Selector corresponding to the file certificate
20    select_certificate_file: Option<Selector>,
21    protected: Vec<bool>,
22}
23
24impl<F> FlagSolver<F>
25where
26    F: Flag,
27{
28    /// Create a new instance that owns a problem
29    pub fn new<S: Into<String>>(pb: Problem<f64, F>, name: S) -> Self {
30        Self {
31            protected: vec![false; pb.ineqs.len()],
32            select: Selector::new(&pb), // Current best selector
33            name: name.into(),
34            pb,
35            optimal_value: None,
36            select_sdpa_file: None,
37            select_certificate_file: None,
38        }
39    }
40    /// Prevents inequality `i` to be ruled out
41    pub fn protect(mut self, i: usize) -> Self {
42        self.protected[i] = true;
43        self
44    }
45    fn write_sdpa(&mut self, select: Selector) {
46        self.pb
47            .view(&select)
48            .write_sdpa(&self.name)
49            .expect("Cannot write sdpa file");
50        self.select_sdpa_file = Some(select);
51    }
52    fn run_csdp(&mut self) -> Result<f64, ()> {
53        match self.select_sdpa_file.take() {
54            Some(select) => {
55                self.select_certificate_file = Some(select);
56                match self.pb.run_csdp(&self.name, None, false) {
57                    Ok(v) => Ok(v),
58                    Err(crate::sdpa::Error::SdpNotSolved(_)) => Err(()),
59                    Err(e) => panic!("Failed to run csdp {e}"),
60                }
61            }
62            None => panic!("No problem to solve"),
63        }
64    }
65    pub fn init(&mut self) {
66        self.write_sdpa(self.select.clone());
67        self.optimal_value = Some(self.run_csdp().expect("Cannot find initial solution"))
68    }
69    fn use_certificate(&mut self) {
70        self.select = self
71            .select
72            .refine_with_certificate(&self.load_certificate(), &self.protected)
73    }
74    pub fn run(&mut self, select: Selector) -> Result<(), String> {
75        self.write_sdpa(select.clone());
76        if let Ok(v) = self.run_csdp() {
77            if match self.optimal_value {
78                None => {
79                    self.optimal_value = Some(v);
80                    true
81                }
82                Some(v0) => (v - v0).abs() < 1e-6,
83            } {
84                self.select = select;
85                info!("New certificate of weight {:?}", self.select.weight());
86                return Ok(());
87            } else {
88                trace!("Selector rejected");
89                return Err("Selector rejected".into());
90            }
91        };
92        Err("run_csdp failed".into())
93    }
94    fn load_certificate(&self) -> Certificate<f64> {
95        if let Some(ref select) = self.select_certificate_file {
96            Certificate::from_file_select(&self.pb.view(select), "certificate").unwrap()
97        } else {
98            panic!("No certificate yet")
99        }
100    }
101    /// Try to minimize using the certificate
102    pub fn minimize_certificate(&mut self) {
103        self.write_sdpa(self.select.clone());
104        let _ = self.run_csdp().expect("Cannot find the same value");
105        let new_select = self
106            .select
107            .refine_with_certificate(&self.load_certificate(), &self.protected);
108        self.run(new_select).expect("Certificate too simplified")
109    }
110    /// Try to remove each `cauchy_schwarz` to see if they are useful
111    pub fn cs_elim(&mut self) {
112        info!("Try to remove Cauchy-Schwarz");
113        for &i in self.select.cs_vec().iter().rev() {
114            if let Ok(select) = self.select.remove_cs(i) {
115                let _ = self.run(select);
116            } else {
117                unreachable!()
118            }
119        }
120    }
121    pub fn thin_cs_elim(&mut self) {
122        info!("Try to refine Cauchy-Schwarz");
123        for &id in &self.select.cs_vec() {
124            let dim = self.select.cs_dim(id);
125            for i in 0..dim {
126                let mut mat = CsMat::zero((dim, dim));
127                mat.insert(i, i, 1.);
128                if let Ok(select) = self.select.restrict_cs(id, mat) {
129                    let _ = self.run(select);
130                } else {
131                    unreachable!()
132                }
133            }
134        }
135    }
136    pub fn ineqs_elim(&mut self) {
137        info!("Try to remove inequalities");
138        for i in 0..self.select.ineqs.len() {
139            if !self.protected[i] {
140                let mut select = self.select.clone();
141                select.ineqs[i].clear();
142                let _ = self.run(select);
143            }
144        }
145    }
146    pub fn thin_ineqs_elim(&mut self) {
147        info!("Try to refine inequalities");
148        for i in (0..self.select.ineqs.len()).rev() {
149            if !self.protected[i] {
150                for j in 0..self.pb.ineqs[i].data.len() {
151                    if let Ok(posj) = self.select.ineqs[i].binary_search(&j) {
152                        let mut select = self.select.clone();
153                        let _ = select.ineqs[i].remove(posj);
154                        if let Ok(()) = self.run(select) {
155                            self.use_certificate();
156                        }
157                    }
158                }
159            }
160        }
161    }
162    pub fn print_report(&self)
163    where
164        F: Draw,
165    {
166        assert_eq!(Some(self.select.clone()), self.select_certificate_file);
167        let cert = self.load_certificate();
168        print_report(&self.pb.view(&self.select), &cert, "report").unwrap()
169    }
170    // Strategies
171
172    pub fn minimize_once(&mut self)
173    where
174        F: Draw,
175    {
176        if self.optimal_value.is_none() {
177            self.init();
178        }
179        self.print_report()
180    }
181    pub fn minimize(&mut self)
182    where
183        F: Draw,
184    {
185        if self.optimal_value.is_none() {
186            self.init();
187        }
188        self.cs_elim();
189        self.ineqs_elim();
190        self.thin_ineqs_elim();
191        self.write_sdpa(self.select.clone());
192        let _ = self.run_csdp().unwrap();
193        self.print_report()
194    }
195    pub fn minimize2(&mut self)
196    where
197        F: Draw,
198    {
199        if self.optimal_value.is_none() {
200            self.init()
201        };
202        self.cs_elim();
203        self.thin_cs_elim();
204        self.ineqs_elim();
205        self.minimize_certificate();
206        self.write_sdpa(self.select.clone());
207        let _ = self.run_csdp().unwrap();
208        self.print_report()
209    }
210    pub fn minimize3(&mut self)
211    where
212        F: Draw,
213    {
214        info!("Method 3");
215        if self.optimal_value.is_none() {
216            self.init()
217        };
218        self.cs_elim();
219        //self.thin_cs_elim();
220        //self.minimize_certificate();
221        self.ineqs_elim();
222        self.thin_cs_elim();
223        self.thin_ineqs_elim();
224        self.write_sdpa(self.select.clone());
225        let _ = self.run_csdp().unwrap();
226        println!("{:?}", self.optimal_value);
227        self.print_report()
228    }
229}