RsBundle  Check-in [9198cc93a8]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge trunk
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | result-sender
Files: files | file ages | folders
SHA1: 9198cc93a8cd918fb314e11ac92ac0d3c040a9a1
User & Date: fifr 2019-11-22 12:00:42.252
Context
2019-11-22
12:18
EvalResult now contains the variant `Error`. check-in: 5499be6782 user: fifr tags: result-sender
12:00
Merge trunk check-in: 9198cc93a8 user: fifr tags: result-sender
11:52
Merge error check-in: e0832fff42 user: fifr tags: trunk
09:24
problem: make `ResultSender` a trait with implementation `ChannelSender` check-in: 43abfc4c67 user: fifr tags: result-sender
Changes
Unified Diff Ignore Whitespace Patch
Changes to Cargo.toml.
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
[package]
name = "bundle"
version = "0.7.0-dev"
edition = "2018"
authors = ["Frank Fischer <frank-fischer@shadow-soft.de>"]

[features]
default = []
blas = ["rs-blas", "openblas-src"]

[dependencies]

itertools = "^0.8"
libc = "^0.2.6"
log = "^0.4"
c_str_macro = "^1.0"
cplex-sys = "^0.5"
crossbeam = "^0.7"
threadpool = "^1.7"











>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[package]
name = "bundle"
version = "0.7.0-dev"
edition = "2018"
authors = ["Frank Fischer <frank-fischer@shadow-soft.de>"]

[features]
default = []
blas = ["rs-blas", "openblas-src"]

[dependencies]
either = "^1"
itertools = "^0.8"
libc = "^0.2.6"
log = "^0.4"
c_str_macro = "^1.0"
cplex-sys = "^0.5"
crossbeam = "^0.7"
threadpool = "^1.7"
Changes to examples/quadratic.rs.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see  <http://www.gnu.org/licenses/>
 */

use std::error::Error;

use better_panic;
use bundle::{self, dvec};
use env_logger;
use env_logger::fmt::Color;
use log::{debug, Level};
use rustop::opts;

use std::io::Write;
use std::sync::Arc;
use std::thread;

use bundle::problem::{FirstOrderProblem as ParallelProblem, ResultSender};
use bundle::solver::sync::{DefaultSolver, NoBundleSolver};
use bundle::{DVector, Minorant, Real};







<
<






>







11
12
13
14
15
16
17


18
19
20
21
22
23
24
25
26
27
28
29
30
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see  <http://www.gnu.org/licenses/>
 */



use better_panic;
use bundle::{self, dvec};
use env_logger;
use env_logger::fmt::Color;
use log::{debug, Level};
use rustop::opts;
use std::error::Error;
use std::io::Write;
use std::sync::Arc;
use std::thread;

use bundle::problem::{FirstOrderProblem as ParallelProblem, ResultSender};
use bundle::solver::sync::{DefaultSolver, NoBundleSolver};
use bundle::{DVector, Minorant, Real};
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
            b: [-12.0, -10.0],
            c: 3.0,
        }
    }
}

impl ParallelProblem for QuadraticProblem {
    type Err = Box<dyn Error + Send + Sync>;
    type Primal = ();

    fn num_variables(&self) -> usize {
        2
    }

    fn num_subproblems(&self) -> usize {







|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
            b: [-12.0, -10.0],
            c: 3.0,
        }
    }
}

impl ParallelProblem for QuadraticProblem {
    type Err = Box<dyn Error + Send>;
    type Primal = ();

    fn num_variables(&self) -> usize {
        2
    }

    fn num_subproblems(&self) -> usize {
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
            )
            .unwrap();
        });
        Ok(())
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    better_panic::install();
    env_logger::builder()
        .format(|buf, record| {
            let mut style = buf.style();
            let color = match record.level() {
                Level::Error | Level::Warn => Color::Red,
                Level::Trace => Color::Blue,







|







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
            )
            .unwrap();
        });
        Ok(())
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    better_panic::install();
    env_logger::builder()
        .format(|buf, record| {
            let mut style = buf.style();
            let color = match record.level() {
                Level::Error | Level::Warn => Color::Red,
                Level::Trace => Color::Blue,
Changes to src/master/boxed.rs.
17
18
19
20
21
22
23

24
25
26
27
28
29
30
pub mod unconstrained;
use self::unconstrained::UnconstrainedMasterProblem;

use super::MasterProblem;
pub use super::SubgradientExtension;
use crate::{DVector, Minorant, Real};


use itertools::multizip;
use log::debug;
use std::f64::{EPSILON, INFINITY, NEG_INFINITY};

/**
 * Turn unconstrained master problem into box-constrained one.
 *







>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pub mod unconstrained;
use self::unconstrained::UnconstrainedMasterProblem;

use super::MasterProblem;
pub use super::SubgradientExtension;
use crate::{DVector, Minorant, Real};

use either::Either;
use itertools::multizip;
use log::debug;
use std::f64::{EPSILON, INFINITY, NEG_INFINITY};

/**
 * Turn unconstrained master problem into box-constrained one.
 *
236
237
238
239
240
241
242
243
244
245
246
247



248
249
250
251
252
253
254
        self.master.weight()
    }

    fn set_weight(&mut self, weight: Real) -> Result<(), Self::Err> {
        self.master.set_weight(weight)
    }

    fn add_vars(
        &mut self,
        bounds: &[(Option<usize>, Real, Real)],
        extend_subgradient: &mut SubgradientExtension<Self::MinorantIndex>,
    ) -> Result<(), Self::Err> {



        if !bounds.is_empty() {
            for (index, l, u) in bounds.iter().filter_map(|v| v.0.map(|i| (i, v.1, v.2))) {
                self.lb[index] = l;
                self.ub[index] = u;
            }
            self.lb.extend(bounds.iter().filter(|v| v.0.is_none()).map(|x| x.1));
            self.ub.extend(bounds.iter().filter(|v| v.0.is_none()).map(|x| x.2));







|


|
|
>
>
>







237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
        self.master.weight()
    }

    fn set_weight(&mut self, weight: Real) -> Result<(), Self::Err> {
        self.master.set_weight(weight)
    }

    fn add_vars<S>(
        &mut self,
        bounds: &[(Option<usize>, Real, Real)],
        extend_subgradient: S,
    ) -> Result<(), Either<Self::Err, S::Err>>
    where
        S: SubgradientExtension<Self::MinorantIndex>,
    {
        if !bounds.is_empty() {
            for (index, l, u) in bounds.iter().filter_map(|v| v.0.map(|i| (i, v.1, v.2))) {
                self.lb[index] = l;
                self.ub[index] = u;
            }
            self.lb.extend(bounds.iter().filter(|v| v.0.is_none()).map(|x| x.1));
            self.ub.extend(bounds.iter().filter(|v| v.0.is_none()).map(|x| x.2));
Changes to src/master/boxed/unconstrained.rs.
17
18
19
20
21
22
23

24
25
26
27
28
29
30
pub mod cpx;
pub mod minimal;

use crate::{DVector, Minorant, Real};

pub use super::SubgradientExtension;


use std::error::Error;

/**
 * Trait for master problems without box constraints.
 *
 * Implementors of this trait are supposed to solve quadratic
 * optimization problems of the form







>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pub mod cpx;
pub mod minimal;

use crate::{DVector, Minorant, Real};

pub use super::SubgradientExtension;

use either::Either;
use std::error::Error;

/**
 * Trait for master problems without box constraints.
 *
 * Implementors of this trait are supposed to solve quadratic
 * optimization problems of the form
87
88
89
90
91
92
93
94
95
96
97
98
99


100
101
102
103
104
105
106
    fn add_minorant(&mut self, fidx: usize, minorant: Minorant) -> Result<Self::MinorantIndex, Self::Err>;

    /// Add or move some variables.
    ///
    /// The variables in `changed` have been changed, so the subgradient
    /// information must be updated. Furthermore, `nnew` new variables
    /// are added.
    fn add_vars(
        &mut self,
        nnew: usize,
        changed: &[usize],
        extend_subgradient: &mut SubgradientExtension<Self::MinorantIndex>,
    ) -> Result<(), Self::Err>;



    /// Solve the master problem.
    fn solve(&mut self, eta: &DVector, fbound: Real, augbound: Real, relprec: Real) -> Result<(), Self::Err>;

    /// Return the current dual optimal solution.
    fn dualopt(&self) -> &DVector;








|



|
|
>
>







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    fn add_minorant(&mut self, fidx: usize, minorant: Minorant) -> Result<Self::MinorantIndex, Self::Err>;

    /// Add or move some variables.
    ///
    /// The variables in `changed` have been changed, so the subgradient
    /// information must be updated. Furthermore, `nnew` new variables
    /// are added.
    fn add_vars<S>(
        &mut self,
        nnew: usize,
        changed: &[usize],
        extend_subgradient: S,
    ) -> Result<(), Either<Self::Err, S::Err>>
    where
        S: SubgradientExtension<Self::MinorantIndex>;

    /// Solve the master problem.
    fn solve(&mut self, eta: &DVector, fbound: Real, augbound: Real, relprec: Real) -> Result<(), Self::Err>;

    /// Return the current dual optimal solution.
    fn dualopt(&self) -> &DVector;

Changes to src/master/boxed/unconstrained/cpx.rs.
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

use super::{SubgradientExtension, UnconstrainedMasterProblem};
use crate::{Aggregatable, DVector, Minorant, Real};

use c_str_macro::c_str;
use cplex_sys as cpx;
use cplex_sys::trycpx;

use log::{debug, warn};

use std;
use std::f64::NEG_INFINITY;
use std::iter::{once, repeat};
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::sync::Arc;

#[derive(Debug)]
pub enum CplexMasterError {
    Cplex(cpx::CplexError),
    SubgradientExtension(Box<dyn std::error::Error + Send + Sync>),
    NoMinorants,
}

impl std::fmt::Display for CplexMasterError {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use CplexMasterError::*;
        match self {
            Cplex(err) => err.fmt(fmt),
            SubgradientExtension(err) => write!(fmt, "Subgradient extension failed: {}", err),
            NoMinorants => write!(fmt, "Master problem contains no minorants"),
        }
    }
}

impl std::error::Error for CplexMasterError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use CplexMasterError::*;
        match self {
            Cplex(err) => Some(err),
            SubgradientExtension(err) => Some(err.as_ref()),
            NoMinorants => None,
        }
    }
}

impl From<cpx::CplexError> for CplexMasterError {
    fn from(err: cpx::CplexError) -> Self {







>













<








<










<







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

use super::{SubgradientExtension, UnconstrainedMasterProblem};
use crate::{Aggregatable, DVector, Minorant, Real};

use c_str_macro::c_str;
use cplex_sys as cpx;
use cplex_sys::trycpx;
use either::Either;
use log::{debug, warn};

use std;
use std::f64::NEG_INFINITY;
use std::iter::{once, repeat};
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::sync::Arc;

#[derive(Debug)]
pub enum CplexMasterError {
    Cplex(cpx::CplexError),

    NoMinorants,
}

impl std::fmt::Display for CplexMasterError {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use CplexMasterError::*;
        match self {
            Cplex(err) => err.fmt(fmt),

            NoMinorants => write!(fmt, "Master problem contains no minorants"),
        }
    }
}

impl std::error::Error for CplexMasterError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use CplexMasterError::*;
        match self {
            Cplex(err) => Some(err),

            NoMinorants => None,
        }
    }
}

impl From<cpx::CplexError> for CplexMasterError {
    fn from(err: cpx::CplexError) -> Self {
305
306
307
308
309
310
311
312
313
314
315



316
317

318
319
320
321
322
323
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338
        // store minorant
        self.minorants[fidx].push(MinorantInfo { minorant, index });
        self.opt_mults[fidx].push(0.0);

        Ok(index)
    }

    fn add_vars(
        &mut self,
        nnew: usize,
        changed: &[usize],



        extend_subgradient: &mut SubgradientExtension<Self::MinorantIndex>,
    ) -> Result<()> {

        debug_assert!(!self.minorants[0].is_empty());
        if changed.is_empty() && nnew == 0 {
            return Ok(());
        }
        let noldvars = self.minorants[0][0].linear.len();
        let nnewvars = noldvars + nnew;

        let mut changedvars = vec![];
        changedvars.extend_from_slice(changed);
        changedvars.extend(noldvars..nnewvars);
        for (fidx, mins) in self.minorants.iter_mut().enumerate() {
            for m in &mut mins[..] {
                let new_subg =
                    extend_subgradient(fidx, m.index, &changedvars).map_err(CplexMasterError::SubgradientExtension)?;

                for (&j, &g) in changed.iter().zip(new_subg.iter()) {
                    m.linear[j] = g;
                }
                m.linear.extend_from_slice(&new_subg[changed.len()..]);
            }
        }








|



>
>
>
|
<
>












|
|
>







303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
        // store minorant
        self.minorants[fidx].push(MinorantInfo { minorant, index });
        self.opt_mults[fidx].push(0.0);

        Ok(index)
    }

    fn add_vars<S>(
        &mut self,
        nnew: usize,
        changed: &[usize],
        mut extend_subgradient: S,
    ) -> std::result::Result<(), Either<CplexMasterError, S::Err>>
    where
        S: SubgradientExtension<Self::MinorantIndex>,

    {
        debug_assert!(!self.minorants[0].is_empty());
        if changed.is_empty() && nnew == 0 {
            return Ok(());
        }
        let noldvars = self.minorants[0][0].linear.len();
        let nnewvars = noldvars + nnew;

        let mut changedvars = vec![];
        changedvars.extend_from_slice(changed);
        changedvars.extend(noldvars..nnewvars);
        for (fidx, mins) in self.minorants.iter_mut().enumerate() {
            for m in &mut mins[..] {
                let new_subg = extend_subgradient
                    .subgradient_extension(fidx, m.index, &changedvars)
                    .map_err(Either::Right)?;
                for (&j, &g) in changed.iter().zip(new_subg.iter()) {
                    m.linear[j] = g;
                }
                m.linear.extend_from_slice(&new_subg[changed.len()..]);
            }
        }

Changes to src/master/boxed/unconstrained/minimal.rs.
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
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see  <http://www.gnu.org/licenses/>
//

use super::{SubgradientExtension, UnconstrainedMasterProblem};
use crate::{Aggregatable, DVector, Minorant, Real};


use log::debug;

use std::error::Error;
use std::f64::NEG_INFINITY;
use std::fmt;

/// Minimal master problem error.
#[derive(Debug)]
pub enum MinimalMasterError {
    NoMinorants,
    MaxMinorants { subproblem: usize },
    SubgradientExtension(Box<dyn Error + Send + Sync>),
}

impl fmt::Display for MinimalMasterError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        use self::MinimalMasterError::*;
        match self {
            MaxMinorants { subproblem } => write!(
                fmt,
                "The minimal master problem allows at most two minorants (subproblem: {})",
                subproblem
            ),
            NoMinorants => write!(fmt, "The master problem does not contain a minorant"),
            SubgradientExtension(err) => write!(fmt, "Subgradient extension failed: {}", err),
        }
    }
}

impl Error for MinimalMasterError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use MinimalMasterError::*;
        match self {
            SubgradientExtension(err) => Some(err.as_ref()),
            _ => None,
        }
    }
}

/**
 * A minimal master problem with only two minorants.
 *
 * This is the simplest possible master problem for bundle methods. It
 * has only two minorants and only one function model. The advantage
 * is that this model can be solved explicitely and very quickly, but







>











<












<




|
<
<
<
<
<
<
<
<







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
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see  <http://www.gnu.org/licenses/>
//

use super::{SubgradientExtension, UnconstrainedMasterProblem};
use crate::{Aggregatable, DVector, Minorant, Real};

use either::Either;
use log::debug;

use std::error::Error;
use std::f64::NEG_INFINITY;
use std::fmt;

/// Minimal master problem error.
#[derive(Debug)]
pub enum MinimalMasterError {
    NoMinorants,
    MaxMinorants { subproblem: usize },

}

impl fmt::Display for MinimalMasterError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        use self::MinimalMasterError::*;
        match self {
            MaxMinorants { subproblem } => write!(
                fmt,
                "The minimal master problem allows at most two minorants (subproblem: {})",
                subproblem
            ),
            NoMinorants => write!(fmt, "The master problem does not contain a minorant"),

        }
    }
}

impl Error for MinimalMasterError {}









/**
 * A minimal master problem with only two minorants.
 *
 * This is the simplest possible master problem for bundle methods. It
 * has only two minorants and only one function model. The advantage
 * is that this model can be solved explicitely and very quickly, but
203
204
205
206
207
208
209
210
211
212
213
214
215



216
217
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
                }
                Ok(2 * fidx + 1)
            }
            _ => unreachable!("Invalid number of minorants in subproblem {}", fidx),
        }
    }

    fn add_vars(
        &mut self,
        nnew: usize,
        changed: &[usize],
        extend_subgradient: &mut SubgradientExtension<Self::MinorantIndex>,
    ) -> Result<(), Self::Err> {



        if self.num_subproblems_with_1 == 0 {
            return Ok(());
        }

        let noldvars = self.minorants[0][self
            .num_minorants_of
            .iter()
            .position(|&n| n > 0)
            .ok_or(MinimalMasterError::NoMinorants)?]

        .linear
        .len();
        let mut changedvars = vec![];
        changedvars.extend_from_slice(changed);
        changedvars.extend(noldvars..noldvars + nnew);

        for fidx in 0..self.num_subproblems {
            for i in 0..self.num_minorants_of[fidx] {
                let new_subg = extend_subgradient(fidx, 2 * fidx + i, &changedvars)

                    .map_err(MinimalMasterError::SubgradientExtension)?;
                let m = &mut self.minorants[i][fidx];
                for (&j, &g) in changed.iter().zip(new_subg.iter()) {
                    m.linear[j] = g;
                }
                m.linear.extend_from_slice(&new_subg[changed.len()..]);
            }
        }







|



|
|
>
>
>








|
>








|
>
|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
                }
                Ok(2 * fidx + 1)
            }
            _ => unreachable!("Invalid number of minorants in subproblem {}", fidx),
        }
    }

    fn add_vars<S>(
        &mut self,
        nnew: usize,
        changed: &[usize],
        mut extend_subgradient: S,
    ) -> Result<(), Either<Self::Err, S::Err>>
    where
        S: SubgradientExtension<Self::MinorantIndex>,
    {
        if self.num_subproblems_with_1 == 0 {
            return Ok(());
        }

        let noldvars = self.minorants[0][self
            .num_minorants_of
            .iter()
            .position(|&n| n > 0)
            .ok_or(MinimalMasterError::NoMinorants)
            .map_err(Either::Left)?]
        .linear
        .len();
        let mut changedvars = vec![];
        changedvars.extend_from_slice(changed);
        changedvars.extend(noldvars..noldvars + nnew);

        for fidx in 0..self.num_subproblems {
            for i in 0..self.num_minorants_of[fidx] {
                let new_subg = extend_subgradient
                    .subgradient_extension(fidx, 2 * fidx + i, &changedvars)
                    .map_err(Either::Right)?;
                let m = &mut self.minorants[i][fidx];
                for (&j, &g) in changed.iter().zip(new_subg.iter()) {
                    m.linear[j] = g;
                }
                m.linear.extend_from_slice(&new_subg[changed.len()..]);
            }
        }
Changes to src/master/mod.rs.
38
39
40
41
42
43
44

45
46
47
48
49












50












51
52
53
54
55
56
57

pub mod boxed;
pub use self::boxed::BoxedMasterProblem;

pub(crate) mod primalmaster;

use crate::{DVector, Minorant, Real};

use std::error::Error;
use std::result::Result;

/// Callback for subgradient extensions.
pub type SubgradientExtension<'a, I> =












    dyn FnMut(usize, I, &[usize]) -> Result<DVector, Box<dyn Error + Send + Sync + 'static>> + 'a;













/// Trait for master problems of a proximal bundle methods.
///
/// Note that solvers are allowed to create multiple master problems potentially
/// running them in different threads. Because of this they must implement `Send
/// + 'static`, i.e. they must be quite self-contained and independent from
/// everything else.







>




|
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>







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
82

pub mod boxed;
pub use self::boxed::BoxedMasterProblem;

pub(crate) mod primalmaster;

use crate::{DVector, Minorant, Real};
use either::Either;
use std::error::Error;
use std::result::Result;

/// Callback for subgradient extensions.
pub trait SubgradientExtension<I> {
    type Err;

    fn subgradient_extension(
        &mut self,
        fidx: usize,
        minorant_index: I,
        changed: &[usize],
    ) -> Result<DVector, Self::Err>;
}

impl<F, I, E> SubgradientExtension<I> for F
where
    F: FnMut(usize, I, &[usize]) -> Result<DVector, E>,
{
    type Err = E;

    fn subgradient_extension(
        &mut self,
        fidx: usize,
        minorant_index: I,
        changed: &[usize],
    ) -> Result<DVector, Self::Err> {
        (self)(fidx, minorant_index, changed)
    }
}

/// Trait for master problems of a proximal bundle methods.
///
/// Note that solvers are allowed to create multiple master problems potentially
/// running them in different threads. Because of this they must implement `Send
/// + 'static`, i.e. they must be quite self-contained and independent from
/// everything else.
94
95
96
97
98
99
100
101
102
103
104
105


106
107
108
109
110
111
112
    /// Return the current number of inner iterations.
    fn cnt_updates(&self) -> usize;

    /// Add or move some variables with bounds.
    ///
    /// If an index is specified, existing variables are moved,
    /// otherwise new variables are generated.
    fn add_vars(
        &mut self,
        bounds: &[(Option<usize>, Real, Real)],
        extend_subgradient: &mut SubgradientExtension<Self::MinorantIndex>,
    ) -> Result<(), Self::Err>;



    /// Add a new minorant to the model.
    ///
    /// The function returns a unique (among all minorants of all
    /// subproblems) index of the minorant. This index must remain
    /// valid until the minorant is aggregated.
    fn add_minorant(&mut self, fidx: usize, minorant: Minorant) -> Result<Self::MinorantIndex, Self::Err>;







|


|
|
>
>







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
    /// Return the current number of inner iterations.
    fn cnt_updates(&self) -> usize;

    /// Add or move some variables with bounds.
    ///
    /// If an index is specified, existing variables are moved,
    /// otherwise new variables are generated.
    fn add_vars<S>(
        &mut self,
        bounds: &[(Option<usize>, Real, Real)],
        extend_subgradient: S,
    ) -> Result<(), Either<Self::Err, S::Err>>
    where
        S: SubgradientExtension<Self::MinorantIndex>;

    /// Add a new minorant to the model.
    ///
    /// The function returns a unique (among all minorants of all
    /// subproblems) index of the minorant. This index must remain
    /// valid until the minorant is aggregated.
    fn add_minorant(&mut self, fidx: usize, minorant: Minorant) -> Result<Self::MinorantIndex, Self::Err>;
Changes to src/master/primalmaster.rs.
17
18
19
20
21
22
23

24
25
26
27
28
29
30

//! A wrapper around master problems to handle primal information.

use super::MasterProblem;
use crate::problem::SubgradientExtender;
use crate::{Aggregatable, Minorant, Real};


use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

/// A wrapper around `MasterProblem` to handle primal information.
///
/// For each minorant there can be an associated (generating) primal data.
/// Typically this data arises from a Lagrangian Relaxation approach. The primal







>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

//! A wrapper around master problems to handle primal information.

use super::MasterProblem;
use crate::problem::SubgradientExtender;
use crate::{Aggregatable, Minorant, Real};

use either::Either;
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

/// A wrapper around `MasterProblem` to handle primal information.
///
/// For each minorant there can be an associated (generating) primal data.
/// Typically this data arises from a Lagrangian Relaxation approach. The primal
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
        }
    }

    pub fn add_vars<E>(
        &mut self,
        vars: Vec<(Option<usize>, Real, Real)>,
        sgext: &mut SubgradientExtender<P, E>,
    ) -> Result<(), M::Err>
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>,
    {
        let primals = &self.primals;
        self.master.add_vars(&vars, &mut |fidx, minidx, vars| {
            sgext(
                fidx,
                primals
                    .get(&minidx)
                    .expect("Extension for non-existing primal requested"),
                vars,
            )
            .map_err(|err| err.into())
        })
    }

    pub fn add_minorant(&mut self, fidx: usize, minorant: Minorant, primal: P) -> Result<(), M::Err> {
        let index = self.master.add_minorant(fidx, minorant)?;
        let old = self.primals.insert(index, primal);
        debug_assert!(old.is_none(), "Minorant index already used");







|
<
<
<

|







<







53
54
55
56
57
58
59
60



61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
        }
    }

    pub fn add_vars<E>(
        &mut self,
        vars: Vec<(Option<usize>, Real, Real)>,
        sgext: &mut SubgradientExtender<P, E>,
    ) -> Result<(), Either<M::Err, E>> {



        let primals = &self.primals;
        self.master.add_vars(&vars, |fidx, minidx, vars: &[usize]| {
            sgext(
                fidx,
                primals
                    .get(&minidx)
                    .expect("Extension for non-existing primal requested"),
                vars,
            )

        })
    }

    pub fn add_minorant(&mut self, fidx: usize, minorant: Minorant, primal: P) -> Result<(), M::Err> {
        let index = self.master.add_minorant(fidx, minorant)?;
        let old = self.primals.insert(index, primal);
        debug_assert!(old.is_none(), "Minorant index already used");
Changes to src/solver/asyn.rs.
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125






126

127
128
129
130
131
132
133
134
135
136
137
138
pub type DefaultSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::FullMasterBuilder>;

/// The minimal bundle solver.
pub type NoBundleSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::MinimalMasterBuilder>;

/// Error raised by the parallel bundle [`Solver`].
#[derive(Debug)]
pub enum Error<E> {
    /// An error raised when creating a new master problem solver.
    BuildMaster(Box<dyn std::error::Error>),
    /// An error raised by the master problem process.
    Master(Box<dyn std::error::Error>),
    /// The iteration limit has been reached.
    IterationLimit { limit: usize },
    /// An error raised by a subproblem evaluation.
    Evaluation(E),
    /// An error raised subproblem update.
    Update(E),
    /// The dimension of some data is wrong.
    Dimension(String),
    /// Invalid bounds for a variable.
    InvalidBounds { lower: Real, upper: Real },
    /// The value of a variable is outside its bounds.
    ViolatedBounds { lower: Real, upper: Real, value: Real },
    /// The variable index is out of bounds.
    InvalidVariable { index: usize, nvars: usize },


    /// An error occurred in a subprocess.
    Process(RecvError),
    /// A method requiring an initialized solver has been called.
    NotInitialized,
    /// The problem has not been solved yet.
    NotSolved,
}











impl<E> std::fmt::Display for Error<E>
where
    E: std::fmt::Display,

{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
        use Error::*;
        match self {
            BuildMaster(err) => writeln!(fmt, "Cannot create master problem solver: {}", err),
            Master(err) => writeln!(fmt, "Error in master problem: {}", err),
            IterationLimit { limit } => writeln!(fmt, "The iteration limit has been reached: {}", limit),
            Evaluation(err) => writeln!(fmt, "Error in subproblem evaluation: {}", err),
            Update(err) => writeln!(fmt, "Error in subproblem update: {}", err),
            Dimension(what) => writeln!(fmt, "Wrong dimension for {}", what),
            InvalidBounds { lower, upper } => write!(fmt, "Invalid bounds, lower:{}, upper:{}", lower, upper),
            ViolatedBounds { lower, upper, value } => write!(
                fmt,
                "Violated bounds, lower:{}, upper:{}, value:{}",
                lower, upper, value
            ),
            InvalidVariable { index, nvars } => {
                write!(fmt, "Variable index out of bounds, got:{} must be < {}", index, nvars)
            }

            Process(err) => writeln!(fmt, "Error in subprocess: {}", err),
            NotInitialized => writeln!(fmt, "The solver must be initialized (called Solver::init()?)"),
            NotSolved => writeln!(fmt, "The problem has not been solved yet"),
        }
    }
}

impl<E> std::error::Error for Error<E>
where
    E: std::error::Error + 'static,

{
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match self {
            BuildMaster(err) => Some(err.as_ref()),
            Master(err) => Some(err.as_ref()),
            Evaluation(err) => Some(err),
            Process(err) => Some(err),
            _ => None,
        }
    }
}

impl<E, MErr> From<masterprocess::Error<MErr>> for Error<E>
where
    MErr: std::error::Error + 'static,
{
    fn from(err: masterprocess::Error<MErr>) -> Error<E> {






        Error::Master(err.into())

    }
}

impl<E> From<RecvError> for Error<E> {
    fn from(err: RecvError) -> Error<E> {
        Error::Process(err.into())
    }
}

type ClientSender<P> =
    Sender<std::result::Result<EvalResult<usize, <P as FirstOrderProblem>::Primal>, <P as FirstOrderProblem>::Err>>;








|

|

|



|

|








>
>








>
>
>
>
>
>
>
>
>
>
|

|
>



















>







|

|
>




|
|







|



|
>
>
>
>
>
>
|
>



|
|







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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
pub type DefaultSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::FullMasterBuilder>;

/// The minimal bundle solver.
pub type NoBundleSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::MinimalMasterBuilder>;

/// Error raised by the parallel bundle [`Solver`].
#[derive(Debug)]
pub enum Error<MErr, PErr> {
    /// An error raised when creating a new master problem solver.
    BuildMaster(MErr),
    /// An error raised by the master problem process.
    Master(MErr),
    /// The iteration limit has been reached.
    IterationLimit { limit: usize },
    /// An error raised by a subproblem evaluation.
    Evaluation(PErr),
    /// An error raised subproblem update.
    Update(PErr),
    /// The dimension of some data is wrong.
    Dimension(String),
    /// Invalid bounds for a variable.
    InvalidBounds { lower: Real, upper: Real },
    /// The value of a variable is outside its bounds.
    ViolatedBounds { lower: Real, upper: Real, value: Real },
    /// The variable index is out of bounds.
    InvalidVariable { index: usize, nvars: usize },
    /// Disconnected channel.
    Disconnected,
    /// An error occurred in a subprocess.
    Process(RecvError),
    /// A method requiring an initialized solver has been called.
    NotInitialized,
    /// The problem has not been solved yet.
    NotSolved,
}

/// The result type of the solver.
///
/// - `T` is the value type,
/// - `P` is the `FirstOrderProblem` associated with the solver,
/// - `M` is the `MasterBuilder` associated with the solver.
pub type Result<T, P, M> = std::result::Result<
    T,
    Error<<<M as MasterBuilder>::MasterProblem as MasterProblem>::Err, <P as FirstOrderProblem>::Err>,
>;

impl<MErr, PErr> std::fmt::Display for Error<MErr, PErr>
where
    MErr: std::fmt::Display,
    PErr: std::fmt::Display,
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
        use Error::*;
        match self {
            BuildMaster(err) => writeln!(fmt, "Cannot create master problem solver: {}", err),
            Master(err) => writeln!(fmt, "Error in master problem: {}", err),
            IterationLimit { limit } => writeln!(fmt, "The iteration limit has been reached: {}", limit),
            Evaluation(err) => writeln!(fmt, "Error in subproblem evaluation: {}", err),
            Update(err) => writeln!(fmt, "Error in subproblem update: {}", err),
            Dimension(what) => writeln!(fmt, "Wrong dimension for {}", what),
            InvalidBounds { lower, upper } => write!(fmt, "Invalid bounds, lower:{}, upper:{}", lower, upper),
            ViolatedBounds { lower, upper, value } => write!(
                fmt,
                "Violated bounds, lower:{}, upper:{}, value:{}",
                lower, upper, value
            ),
            InvalidVariable { index, nvars } => {
                write!(fmt, "Variable index out of bounds, got:{} must be < {}", index, nvars)
            }
            Disconnected => writeln!(fmt, "A channel got disconnected"),
            Process(err) => writeln!(fmt, "Error in subprocess: {}", err),
            NotInitialized => writeln!(fmt, "The solver must be initialized (called Solver::init()?)"),
            NotSolved => writeln!(fmt, "The problem has not been solved yet"),
        }
    }
}

impl<MErr, PErr> std::error::Error for Error<MErr, PErr>
where
    MErr: std::error::Error + 'static,
    PErr: std::error::Error + 'static,
{
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match self {
            BuildMaster(err) => Some(err),
            Master(err) => Some(err),
            Evaluation(err) => Some(err),
            Process(err) => Some(err),
            _ => None,
        }
    }
}

impl<MErr, PErr> From<masterprocess::Error<MErr, PErr>> for Error<MErr, PErr>
where
    MErr: std::error::Error + 'static,
{
    fn from(err: masterprocess::Error<MErr, PErr>) -> Error<MErr, PErr> {
        use masterprocess::Error::*;
        match err {
            DisconnectedSender => Error::Disconnected,
            DisconnectedReceiver => Error::Disconnected,
            Aggregation(err) => Error::Master(err.into()),
            SubgradientExtension(err) => Error::Update(err),
            Master(err) => Error::Master(err.into()),
        }
    }
}

impl<MErr, PErr> From<RecvError> for Error<MErr, PErr> {
    fn from(err: RecvError) -> Error<MErr, PErr> {
        Error::Process(err.into())
    }
}

type ClientSender<P> =
    Sender<std::result::Result<EvalResult<usize, <P as FirstOrderProblem>::Primal>, <P as FirstOrderProblem>::Err>>;

439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    /// This is actually the time of the last call to `Solver::init`.
    start_time: Instant,
}

impl<P, T, W, M> Solver<P, T, W, M>
where
    P: FirstOrderProblem,
    P::Err: Into<Box<dyn std::error::Error + Sync + Send>> + Send + 'static,
    T: Terminator<SolverData<P>> + Default,
    W: Weighter<SolverData<P>> + Default,
    M: MasterBuilder,
    <M::MasterProblem as MasterProblem>::MinorantIndex: std::hash::Hash,
{
    /// Create a new parallel bundle solver.
    pub fn new(problem: P) -> Self







|







461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    /// This is actually the time of the last call to `Solver::init`.
    start_time: Instant,
}

impl<P, T, W, M> Solver<P, T, W, M>
where
    P: FirstOrderProblem,
    P::Err: Send + 'static,
    T: Terminator<SolverData<P>> + Default,
    W: Weighter<SolverData<P>> + Default,
    M: MasterBuilder,
    <M::MasterProblem as MasterProblem>::MinorantIndex: std::hash::Hash,
{
    /// Create a new parallel bundle solver.
    pub fn new(problem: P) -> Self
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    ///
    /// This will reset the internal data structures so that a new fresh
    /// solution process can be started.
    ///
    /// It will also setup all worker processes.
    ///
    /// This function is automatically called by [`Solver::solve`].
    pub fn init(&mut self) -> Result<(), Error<P::Err>> {
        debug!("Initialize solver");

        let n = self.problem.num_variables();
        let m = self.problem.num_subproblems();

        self.data.init(dvec![0.0; n]);
        self.cnt_descent = 0;







|







530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
    ///
    /// This will reset the internal data structures so that a new fresh
    /// solution process can be started.
    ///
    /// It will also setup all worker processes.
    ///
    /// This function is automatically called by [`Solver::solve`].
    pub fn init(&mut self) -> Result<(), P, M> {
        debug!("Initialize solver");

        let n = self.problem.num_variables();
        let m = self.problem.num_subproblems();

        self.data.init(dvec![0.0; n]);
        self.cnt_descent = 0;
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
        self.data.nxt_d = Arc::new(dvec![0.0; num_variables]);
        self.data.nxt_y = Arc::new(dvec![]);
        self.data.update_rx = None;
        self.data.need_update = true;
    }

    /// Solve the problem with the default maximal iteration limit [`DEFAULT_ITERATION_LIMIT`].
    pub fn solve(&mut self) -> Result<(), Error<P::Err>> {
        self.solve_with_limit(DEFAULT_ITERATION_LIMIT)
    }

    /// Solve the problem with a maximal iteration limit.
    pub fn solve_with_limit(&mut self, limit: usize) -> Result<(), Error<P::Err>> {
        // First initialize the internal data structures.
        self.init()?;

        if self.solve_iter(limit)? {
            Ok(())
        } else {
            Err(Error::IterationLimit { limit })
        }
    }

    /// Solve the problem but stop after at most `niter` iterations.
    ///
    /// The function returns `Ok(true)` if the termination criterion
    /// has been satisfied. Otherwise it returns `Ok(false)` or an
    /// error code.
    ///
    /// If this function is called again, the solution process is
    /// continued from the previous point. Because of this one *must*
    /// call `init()` before the first call to this function.
    pub fn solve_iter(&mut self, niter: usize) -> Result<bool, Error<P::Err>> {
        debug!("Start solving up to {} iterations", niter);

        self.reset_iteration_data(niter);

        loop {
            let client_index;
            let master_index;







|




|



















|







611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
        self.data.nxt_d = Arc::new(dvec![0.0; num_variables]);
        self.data.nxt_y = Arc::new(dvec![]);
        self.data.update_rx = None;
        self.data.need_update = true;
    }

    /// Solve the problem with the default maximal iteration limit [`DEFAULT_ITERATION_LIMIT`].
    pub fn solve(&mut self) -> Result<(), P, M> {
        self.solve_with_limit(DEFAULT_ITERATION_LIMIT)
    }

    /// Solve the problem with a maximal iteration limit.
    pub fn solve_with_limit(&mut self, limit: usize) -> Result<(), P, M> {
        // First initialize the internal data structures.
        self.init()?;

        if self.solve_iter(limit)? {
            Ok(())
        } else {
            Err(Error::IterationLimit { limit })
        }
    }

    /// Solve the problem but stop after at most `niter` iterations.
    ///
    /// The function returns `Ok(true)` if the termination criterion
    /// has been satisfied. Otherwise it returns `Ok(false)` or an
    /// error code.
    ///
    /// If this function is called again, the solution process is
    /// continued from the previous point. Because of this one *must*
    /// call `init()` before the first call to this function.
    pub fn solve_iter(&mut self, niter: usize) -> Result<bool, P, M> {
        debug!("Start solving up to {} iterations", niter);

        self.reset_iteration_data(niter);

        loop {
            let client_index;
            let master_index;
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
    /// The function returns
    ///   - `Ok(true)` if the final iteration count has been reached,
    ///   - `Ok(false)` if the final iteration count has not been reached,
    ///   - `Err(_)` on error.
    fn handle_client_response(
        &mut self,
        msg: EvalResult<usize, <P as FirstOrderProblem>::Primal>,
    ) -> Result<bool, Error<P::Err>> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;
        match msg {
            EvalResult::ObjectiveValue { index, value } => {
                debug!("Receive objective from subproblem {}: {}", index, value);
                if self.data.nxt_ubs[index].is_infinite() {
                    self.data.cnt_remaining_ubs -= 1;
                }







|







718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
    /// The function returns
    ///   - `Ok(true)` if the final iteration count has been reached,
    ///   - `Ok(false)` if the final iteration count has not been reached,
    ///   - `Err(_)` on error.
    fn handle_client_response(
        &mut self,
        msg: EvalResult<usize, <P as FirstOrderProblem>::Primal>,
    ) -> Result<bool, P, M> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;
        match msg {
            EvalResult::ObjectiveValue { index, value } => {
                debug!("Receive objective from subproblem {}: {}", index, value);
                if self.data.nxt_ubs[index].is_infinite() {
                    self.data.cnt_remaining_ubs -= 1;
                }
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
    /// Finally the master problem starts the evaluation of all subproblems at
    /// the new candidate.
    ///
    /// Return values
    ///   - `Ok(true)` if the termination criterion has been satisfied,
    ///   - `Ok(false)` if the termination criterion has not been satisfied,
    ///   - `Err(_)` on error.
    fn handle_master_response(&mut self, master_res: MasterResponse) -> Result<bool, Error<P::Err>> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;

        self.data.nxt_mod = master_res.nxt_mod;
        self.data.sgnorm = master_res.sgnorm;
        self.data.expected_progress = self.data.cur_val - self.data.nxt_mod;
        self.data.cnt_updates = master_res.cnt_updates;








|







839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
    /// Finally the master problem starts the evaluation of all subproblems at
    /// the new candidate.
    ///
    /// Return values
    ///   - `Ok(true)` if the termination criterion has been satisfied,
    ///   - `Ok(false)` if the termination criterion has not been satisfied,
    ///   - `Err(_)` on error.
    fn handle_master_response(&mut self, master_res: MasterResponse) -> Result<bool, P, M> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;

        self.data.nxt_mod = master_res.nxt_mod;
        self.data.sgnorm = master_res.sgnorm;
        self.data.expected_progress = self.data.cur_val - self.data.nxt_mod;
        self.data.cnt_updates = master_res.cnt_updates;

894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
    /// variables. This method starts the update process.
    ///
    /// Return values
    ///   - `Ok(true)` if a new update process has been started,
    ///   - `Ok(false)` if there is already a running update process (only one
    ///     is allowed at the same time),
    ///   - `Err(_)` on error.
    fn update_problem(&mut self, step: Step) -> Result<bool, Error<P::Err>> {
        // only one update may be running at the same time
        if self.data.update_rx.is_some() {
            return Ok(false);
        }

        // Ok, we are doing a new update now ...
        self.data.need_update = false;







|







916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
    /// variables. This method starts the update process.
    ///
    /// Return values
    ///   - `Ok(true)` if a new update process has been started,
    ///   - `Ok(false)` if there is already a running update process (only one
    ///     is allowed at the same time),
    ///   - `Err(_)` on error.
    fn update_problem(&mut self, step: Step) -> Result<bool, P, M> {
        // only one update may be running at the same time
        if self.data.update_rx.is_some() {
            return Ok(false);
        }

        // Ok, we are doing a new update now ...
        self.data.need_update = false;
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
    }

    /// Handles an update response `update` of the problem.
    ///
    /// The method is called if the problem informs the solver about a change of
    /// the problem, e.g. adding a new variable. This method updates the other
    /// parts of the solver, e.g. the master problem, about the modification.
    fn handle_update_response(&mut self, update: Update<usize, P::Primal, P::Err>) -> Result<(), Error<P::Err>> {
        match update {
            Update::AddVariables { bounds, sgext, .. } => {
                if !bounds.is_empty() {
                    // add new variables
                    self.master_proc
                        .as_mut()
                        .unwrap()







|







955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
    }

    /// Handles an update response `update` of the problem.
    ///
    /// The method is called if the problem informs the solver about a change of
    /// the problem, e.g. adding a new variable. This method updates the other
    /// parts of the solver, e.g. the master problem, about the modification.
    fn handle_update_response(&mut self, update: Update<usize, P::Primal, P::Err>) -> Result<(), P, M> {
        match update {
            Update::AddVariables { bounds, sgext, .. } => {
                if !bounds.is_empty() {
                    // add new variables
                    self.master_proc
                        .as_mut()
                        .unwrap()
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
            self.data.nxt_mod,
            self.data.nxt_val,
            self.data.cur_val
        );
    }

    /// Return the aggregated primal of the given subproblem.
    pub fn aggregated_primal(&self, subproblem: usize) -> Result<P::Primal, Error<P::Err>> {
        Ok(self
            .master_proc
            .as_ref()
            .ok_or(Error::NotSolved)?
            .get_aggregated_primal(subproblem)?)
    }
}







|







1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
            self.data.nxt_mod,
            self.data.nxt_val,
            self.data.cur_val
        );
    }

    /// Return the aggregated primal of the given subproblem.
    pub fn aggregated_primal(&self, subproblem: usize) -> Result<P::Primal, P, M> {
        Ok(self
            .master_proc
            .as_ref()
            .ok_or(Error::NotSolved)?
            .get_aggregated_primal(subproblem)?)
    }
}
Changes to src/solver/masterprocess.rs.
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see  <http://www.gnu.org/licenses/>
 */

//! Asynchronous process solving a master problem.

use crossbeam::channel::{unbounded as channel, Receiver, RecvError, SendError, Sender};

use log::{debug, warn};
use std::sync::Arc;
use threadpool::ThreadPool;

use crate::master::primalmaster::PrimalMaster;
use crate::master::MasterProblem;
use crate::problem::{FirstOrderProblem, SubgradientExtender};
use crate::{DVector, Minorant, Real};

#[derive(Debug)]
pub enum Error<E> {
    /// An error in the process management.
    Process(Box<dyn std::error::Error + Send + Sync>),
    /// The communication channel for sending requests has been disconnected.
    DisconnectedSender,
    /// The communication channel for sending responds has been disconnected.
    DisconnectedReceiver,
    /// An error occurred when computing an aggregated primal.
    Aggregation(E),
    /// An error occurred during the subgradient extension.
    SubgradientExtension(Box<dyn std::error::Error + Send + Sync>),
    /// An error has been raised by the underlying master problem solver.
    Master(E),
}

impl<E> std::fmt::Display for Error<E>
where
    E: std::fmt::Display,

{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use Error::*;
        match self {
            Process(err) => writeln!(fmt, "Error in master process: {}", err),
            DisconnectedSender => writeln!(fmt, "Communication channel to master process has been disconnected"),
            DisconnectedReceiver => writeln!(fmt, "Communication channel from master process has been disconnected"),
            Aggregation(err) => writeln!(fmt, "Computation of primal aggregate failed: {}", err),
            SubgradientExtension(err) => writeln!(fmt, "Subgradient extension failed: {}", err),
            Master(err) => writeln!(fmt, "Error in master problem solver: {}", err),
        }
    }
}

impl<E> std::error::Error for Error<E>
where
    E: std::error::Error + 'static,

{
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match self {
            Process(err) => Some(err.as_ref()),
            DisconnectedSender => None,
            DisconnectedReceiver => None,
            Aggregation(err) => Some(err),
            SubgradientExtension(err) => Some(err.as_ref()),
            Master(err) => Some(err),
        }
    }
}

impl<E, T> From<SendError<T>> for Error<E>
where
    T: Send + 'static,
{
    fn from(_err: SendError<T>) -> Error<E> {
        Error::DisconnectedSender
    }
}

impl<E> From<RecvError> for Error<E> {
    fn from(_err: RecvError) -> Error<E> {
        Error::DisconnectedReceiver
    }
}

/// Configuration information for setting up a master problem.
pub struct MasterConfig {
    /// The number of subproblems.







>










|
<
<





|

|

|


|

|
>




<









|

|
>




<



|





|



|




|
|







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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see  <http://www.gnu.org/licenses/>
 */

//! Asynchronous process solving a master problem.

use crossbeam::channel::{unbounded as channel, Receiver, RecvError, SendError, Sender};
use either::Either;
use log::{debug, warn};
use std::sync::Arc;
use threadpool::ThreadPool;

use crate::master::primalmaster::PrimalMaster;
use crate::master::MasterProblem;
use crate::problem::{FirstOrderProblem, SubgradientExtender};
use crate::{DVector, Minorant, Real};

#[derive(Debug)]
pub enum Error<MErr, PErr> {


    /// The communication channel for sending requests has been disconnected.
    DisconnectedSender,
    /// The communication channel for sending responds has been disconnected.
    DisconnectedReceiver,
    /// An error occurred when computing an aggregated primal.
    Aggregation(MErr),
    /// An error occurred during the subgradient extension.
    SubgradientExtension(PErr),
    /// An error has been raised by the underlying master problem solver.
    Master(MErr),
}

impl<MErr, PErr> std::fmt::Display for Error<MErr, PErr>
where
    MErr: std::fmt::Display,
    PErr: std::fmt::Display,
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        use Error::*;
        match self {

            DisconnectedSender => writeln!(fmt, "Communication channel to master process has been disconnected"),
            DisconnectedReceiver => writeln!(fmt, "Communication channel from master process has been disconnected"),
            Aggregation(err) => writeln!(fmt, "Computation of primal aggregate failed: {}", err),
            SubgradientExtension(err) => writeln!(fmt, "Subgradient extension failed: {}", err),
            Master(err) => writeln!(fmt, "Error in master problem solver: {}", err),
        }
    }
}

impl<MErr, PErr> std::error::Error for Error<MErr, PErr>
where
    MErr: std::error::Error + 'static,
    PErr: std::error::Error + 'static,
{
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match self {

            DisconnectedSender => None,
            DisconnectedReceiver => None,
            Aggregation(err) => Some(err),
            SubgradientExtension(err) => Some(err),
            Master(err) => Some(err),
        }
    }
}

impl<MErr, PErr, T> From<SendError<T>> for Error<MErr, PErr>
where
    T: Send + 'static,
{
    fn from(_err: SendError<T>) -> Error<MErr, PErr> {
        Error::DisconnectedSender
    }
}

impl<MErr, PErr> From<RecvError> for Error<MErr, PErr> {
    fn from(_err: RecvError) -> Error<MErr, PErr> {
        Error::DisconnectedReceiver
    }
}

/// Configuration information for setting up a master problem.
pub struct MasterConfig {
    /// The number of subproblems.
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
    pub cnt_updates: usize,
}

type ToMasterSender<P, M> = Sender<MasterTask<<P as FirstOrderProblem>::Primal, <P as FirstOrderProblem>::Err, M>>;

type ToMasterReceiver<P, M> = Receiver<MasterTask<<P as FirstOrderProblem>::Primal, <P as FirstOrderProblem>::Err, M>>;

type MasterSender<E> = Sender<Result<MasterResponse, Error<E>>>;

pub type MasterReceiver<E> = Receiver<Result<MasterResponse, Error<E>>>;

pub struct MasterProcess<P, M>
where
    P: FirstOrderProblem,
    M: MasterProblem,
{
    /// The channel to transmit new tasks to the master problem.
    tx: ToMasterSender<P, M>,

    /// The channel to receive solutions from the master problem.
    pub rx: MasterReceiver<M::Err>,

    phantom: std::marker::PhantomData<M>,
}

impl<P, M> MasterProcess<P, M>
where
    P: FirstOrderProblem,
    P::Primal: Send + 'static,
    P::Err: Into<Box<dyn std::error::Error + Sync + Send>> + 'static,
    M: MasterProblem + Send + 'static,
    M::MinorantIndex: std::hash::Hash,
    M::Err: Send + 'static,
{
    pub fn start(master: M, master_config: MasterConfig, threadpool: &mut ThreadPool) -> Self {
        // Create a pair of communication channels.
        let (to_master_tx, to_master_rx) = channel();







|

|










|








|







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    pub cnt_updates: usize,
}

type ToMasterSender<P, M> = Sender<MasterTask<<P as FirstOrderProblem>::Primal, <P as FirstOrderProblem>::Err, M>>;

type ToMasterReceiver<P, M> = Receiver<MasterTask<<P as FirstOrderProblem>::Primal, <P as FirstOrderProblem>::Err, M>>;

type MasterSender<MErr, PErr> = Sender<Result<MasterResponse, Error<MErr, PErr>>>;

pub type MasterReceiver<MErr, PErr> = Receiver<Result<MasterResponse, Error<MErr, PErr>>>;

pub struct MasterProcess<P, M>
where
    P: FirstOrderProblem,
    M: MasterProblem,
{
    /// The channel to transmit new tasks to the master problem.
    tx: ToMasterSender<P, M>,

    /// The channel to receive solutions from the master problem.
    pub rx: MasterReceiver<M::Err, P::Err>,

    phantom: std::marker::PhantomData<M>,
}

impl<P, M> MasterProcess<P, M>
where
    P: FirstOrderProblem,
    P::Primal: Send + 'static,
    P::Err: Send + 'static,
    M: MasterProblem + Send + 'static,
    M::MinorantIndex: std::hash::Hash,
    M::Err: Send + 'static,
{
    pub fn start(master: M, master_config: MasterConfig, threadpool: &mut ThreadPool) -> Self {
        // Create a pair of communication channels.
        let (to_master_tx, to_master_rx) = channel();
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221





222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
    }

    /// Add new variables to the master problem.
    pub fn add_vars(
        &mut self,
        vars: Vec<(Option<usize>, Real, Real)>,
        sgext: Box<SubgradientExtender<P::Primal, P::Err>>,
    ) -> Result<(), Error<M::Err>>
    where
        P::Err: 'static,
    {
        Ok(self.tx.send(MasterTask::AddVariables(vars, sgext))?)
    }

    /// Add a new minorant to the master problem model.
    ///
    /// This adds the specified `minorant` with associated `primal` data to the
    /// model of subproblem `i`.
    pub fn add_minorant(&mut self, i: usize, minorant: Minorant, primal: P::Primal) -> Result<(), Error<M::Err>> {





        Ok(self.tx.send(MasterTask::AddMinorant(i, minorant, primal))?)
    }

    /// Move the center of the master problem.
    ///
    /// This moves the master problem's center in direction $\\alpha \\cdot d$.
    pub fn move_center(&mut self, alpha: Real, d: Arc<DVector>) -> Result<(), Error<M::Err>> {
        Ok(self.tx.send(MasterTask::MoveCenter(alpha, d))?)
    }

    /// Solve the master problem.
    ///
    /// The current function value in the center `center_value`.
    /// Once the master problem is solved the process will send a
    /// [`MasterResponse`] message to the `tx` channel.
    pub fn solve(&mut self, center_value: Real) -> Result<(), Error<M::Err>> {
        Ok(self.tx.send(MasterTask::Solve { center_value })?)
    }

    /// Compresses the model.
    pub fn compress(&mut self) -> Result<(), Error<M::Err>> {
        Ok(self.tx.send(MasterTask::Compress)?)
    }

    /// Sets the new weight of the proximal term in the master problem.
    pub fn set_weight(&mut self, weight: Real) -> Result<(), Error<M::Err>> {
        Ok(self.tx.send(MasterTask::SetWeight { weight })?)
    }

    /// Get the current aggregated primal for a certain subproblem.
    pub fn get_aggregated_primal(&self, subproblem: usize) -> Result<P::Primal, Error<M::Err>> {
        let (tx, rx) = channel();
        self.tx.send(MasterTask::GetAggregatedPrimal { subproblem, tx })?;
        rx.recv()?.map_err(Error::Aggregation)
    }

    /// The main loop of the master process.
    fn master_main(
        master: M,
        master_config: MasterConfig,
        tx: &mut MasterSender<M::Err>,
        rx: ToMasterReceiver<P, M>,
        subgradient_extender: &mut Option<Box<SubgradientExtender<P::Primal, P::Err>>>,
    ) -> Result<(), Error<M::Err>> {
        let mut master = PrimalMaster::<_, P::Primal>::new(master);

        // Initialize the master problem.
        master
            .set_num_subproblems(master_config.num_subproblems)
            .map_err(Error::Master)?;
        master
            .set_vars(
                master_config.num_vars,
                master_config.lower_bounds,
                master_config.upper_bounds,
            )
            .map_err(Error::Master)?;

        // The main iteration: wait for new tasks.
        for m in rx {
            match m {
                MasterTask::AddVariables(vars, mut sgext) => {
                    debug!("master: add {} variables to the subproblem", vars.len());
                    master.add_vars(vars, &mut sgext).map_err(Error::Master)?;



                    *subgradient_extender = Some(sgext);
                }
                MasterTask::AddMinorant(i, mut m, primal) => {
                    debug!("master: add minorant to subproblem {}", i);
                    // It may happen the number new minorant belongs to an earlier evaluation
                    // with less variables (i.e. new variables have been added
                    // after the start of the evaluation but before the new
                    // minorant is added, i.e. now). In this case we must add
                    // extend the minorant accordingly.
                    if m.linear.len() < master.num_variables() {
                        if let Some(ref mut sgext) = subgradient_extender {
                            let newinds = (m.linear.len()..master.num_variables()).collect::<Vec<_>>();
                            let new_subg = sgext(i, &primal, &newinds)
                                .map_err(|err| Error::<M::Err>::SubgradientExtension(err.into()))?;
                            m.linear.extend(new_subg);
                        }
                    }
                    master.add_minorant(i, m, primal).map_err(Error::Master)?;
                }
                MasterTask::MoveCenter(alpha, d) => {
                    debug!("master: move center");







|










|
>
>
>
>
>






|








|




|




|




|









|


|



















|
>
>
>













|







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
    }

    /// Add new variables to the master problem.
    pub fn add_vars(
        &mut self,
        vars: Vec<(Option<usize>, Real, Real)>,
        sgext: Box<SubgradientExtender<P::Primal, P::Err>>,
    ) -> Result<(), Error<M::Err, P::Err>>
    where
        P::Err: 'static,
    {
        Ok(self.tx.send(MasterTask::AddVariables(vars, sgext))?)
    }

    /// Add a new minorant to the master problem model.
    ///
    /// This adds the specified `minorant` with associated `primal` data to the
    /// model of subproblem `i`.
    pub fn add_minorant(
        &mut self,
        i: usize,
        minorant: Minorant,
        primal: P::Primal,
    ) -> Result<(), Error<M::Err, P::Err>> {
        Ok(self.tx.send(MasterTask::AddMinorant(i, minorant, primal))?)
    }

    /// Move the center of the master problem.
    ///
    /// This moves the master problem's center in direction $\\alpha \\cdot d$.
    pub fn move_center(&mut self, alpha: Real, d: Arc<DVector>) -> Result<(), Error<M::Err, P::Err>> {
        Ok(self.tx.send(MasterTask::MoveCenter(alpha, d))?)
    }

    /// Solve the master problem.
    ///
    /// The current function value in the center `center_value`.
    /// Once the master problem is solved the process will send a
    /// [`MasterResponse`] message to the `tx` channel.
    pub fn solve(&mut self, center_value: Real) -> Result<(), Error<M::Err, P::Err>> {
        Ok(self.tx.send(MasterTask::Solve { center_value })?)
    }

    /// Compresses the model.
    pub fn compress(&mut self) -> Result<(), Error<M::Err, P::Err>> {
        Ok(self.tx.send(MasterTask::Compress)?)
    }

    /// Sets the new weight of the proximal term in the master problem.
    pub fn set_weight(&mut self, weight: Real) -> Result<(), Error<M::Err, P::Err>> {
        Ok(self.tx.send(MasterTask::SetWeight { weight })?)
    }

    /// Get the current aggregated primal for a certain subproblem.
    pub fn get_aggregated_primal(&self, subproblem: usize) -> Result<P::Primal, Error<M::Err, P::Err>> {
        let (tx, rx) = channel();
        self.tx.send(MasterTask::GetAggregatedPrimal { subproblem, tx })?;
        rx.recv()?.map_err(Error::Aggregation)
    }

    /// The main loop of the master process.
    fn master_main(
        master: M,
        master_config: MasterConfig,
        tx: &mut MasterSender<M::Err, P::Err>,
        rx: ToMasterReceiver<P, M>,
        subgradient_extender: &mut Option<Box<SubgradientExtender<P::Primal, P::Err>>>,
    ) -> Result<(), Error<M::Err, P::Err>> {
        let mut master = PrimalMaster::<_, P::Primal>::new(master);

        // Initialize the master problem.
        master
            .set_num_subproblems(master_config.num_subproblems)
            .map_err(Error::Master)?;
        master
            .set_vars(
                master_config.num_vars,
                master_config.lower_bounds,
                master_config.upper_bounds,
            )
            .map_err(Error::Master)?;

        // The main iteration: wait for new tasks.
        for m in rx {
            match m {
                MasterTask::AddVariables(vars, mut sgext) => {
                    debug!("master: add {} variables to the subproblem", vars.len());
                    master.add_vars(vars, &mut sgext).map_err(|err| match err {
                        Either::Left(err) => Error::Master(err),
                        Either::Right(err) => Error::SubgradientExtension(err),
                    })?;
                    *subgradient_extender = Some(sgext);
                }
                MasterTask::AddMinorant(i, mut m, primal) => {
                    debug!("master: add minorant to subproblem {}", i);
                    // It may happen the number new minorant belongs to an earlier evaluation
                    // with less variables (i.e. new variables have been added
                    // after the start of the evaluation but before the new
                    // minorant is added, i.e. now). In this case we must add
                    // extend the minorant accordingly.
                    if m.linear.len() < master.num_variables() {
                        if let Some(ref mut sgext) = subgradient_extender {
                            let newinds = (m.linear.len()..master.num_variables()).collect::<Vec<_>>();
                            let new_subg = sgext(i, &primal, &newinds)
                                .map_err(|err| Error::<M::Err, P::Err>::SubgradientExtension(err))?;
                            m.linear.extend(new_subg);
                        }
                    }
                    master.add_minorant(i, m, primal).map_err(Error::Master)?;
                }
                MasterTask::MoveCenter(alpha, d) => {
                    debug!("master: move center");
Changes to src/solver/sync.rs.
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124






125

126
127
128
129
130
131
132
133
134
135
136
137
pub type DefaultSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::FullMasterBuilder>;

/// The minimal bundle solver.
pub type NoBundleSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::MinimalMasterBuilder>;

/// Error raised by the parallel bundle [`Solver`].
#[derive(Debug)]
pub enum Error<E> {
    /// An error raised when creating a new master problem solver.
    BuildMaster(Box<dyn std::error::Error>),
    /// An error raised by the master problem process.
    Master(Box<dyn std::error::Error>),
    /// The iteration limit has been reached.
    IterationLimit { limit: usize },
    /// An error raised by a subproblem evaluation.
    Evaluation(E),
    /// An error raised subproblem update.
    Update(E),
    /// The dimension of some data is wrong.
    Dimension(String),
    /// Invalid bounds for a variable.
    InvalidBounds { lower: Real, upper: Real },
    /// The value of a variable is outside its bounds.
    ViolatedBounds { lower: Real, upper: Real, value: Real },
    /// The variable index is out of bounds.
    InvalidVariable { index: usize, nvars: usize },


    /// An error occurred in a subprocess.
    Process(RecvError),
    /// A method requiring an initialized solver has been called.
    NotInitialized,
    /// The problem has not been solved yet.
    NotSolved,
}











impl<E> std::fmt::Display for Error<E>
where
    E: std::fmt::Display,

{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;
        match self {
            BuildMaster(err) => writeln!(fmt, "Cannot create master problem solver: {}", err),
            Master(err) => writeln!(fmt, "Error in master problem: {}", err),
            IterationLimit { limit } => writeln!(fmt, "The iteration limit has been reached: {}", limit),
            Evaluation(err) => writeln!(fmt, "Error in subproblem evaluation: {}", err),
            Update(err) => writeln!(fmt, "Error in subproblem update: {}", err),
            Dimension(what) => writeln!(fmt, "Wrong dimension for {}", what),
            InvalidBounds { lower, upper } => write!(fmt, "Invalid bounds, lower:{}, upper:{}", lower, upper),
            ViolatedBounds { lower, upper, value } => write!(
                fmt,
                "Violated bounds, lower:{}, upper:{}, value:{}",
                lower, upper, value
            ),
            InvalidVariable { index, nvars } => {
                write!(fmt, "Variable index out of bounds, got:{} must be < {}", index, nvars)
            }

            Process(err) => writeln!(fmt, "Error in subprocess: {}", err),
            NotInitialized => writeln!(fmt, "The solver must be initialized (called Solver::init()?)"),
            NotSolved => writeln!(fmt, "The problem has not been solved yet"),
        }
    }
}

impl<E> std::error::Error for Error<E>
where
    E: std::error::Error + 'static,

{
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match self {
            BuildMaster(err) => Some(err.as_ref()),
            Master(err) => Some(err.as_ref()),
            Evaluation(err) => Some(err),
            Process(err) => Some(err),
            _ => None,
        }
    }
}

impl<E, MErr> From<masterprocess::Error<MErr>> for Error<E>
where
    MErr: std::error::Error + 'static,
{
    fn from(err: masterprocess::Error<MErr>) -> Error<E> {






        Error::Master(err.into())

    }
}

impl<E> From<RecvError> for Error<E> {
    fn from(err: RecvError) -> Error<E> {
        Error::Process(err.into())
    }
}

type ClientSender<P> =
    Sender<std::result::Result<EvalResult<usize, <P as FirstOrderProblem>::Primal>, <P as FirstOrderProblem>::Err>>;








|

|

|



|

|








>
>








>
>
>
>
>
>
>
>
>
>
|

|
>



















>







|

|
>




|
|







|



|
>
>
>
>
>
>
|
>



|
|







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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
pub type DefaultSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::FullMasterBuilder>;

/// The minimal bundle solver.
pub type NoBundleSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::MinimalMasterBuilder>;

/// Error raised by the parallel bundle [`Solver`].
#[derive(Debug)]
pub enum Error<MErr, PErr> {
    /// An error raised when creating a new master problem solver.
    BuildMaster(MErr),
    /// An error raised by the master problem process.
    Master(MErr),
    /// The iteration limit has been reached.
    IterationLimit { limit: usize },
    /// An error raised by a subproblem evaluation.
    Evaluation(PErr),
    /// An error raised subproblem update.
    Update(PErr),
    /// The dimension of some data is wrong.
    Dimension(String),
    /// Invalid bounds for a variable.
    InvalidBounds { lower: Real, upper: Real },
    /// The value of a variable is outside its bounds.
    ViolatedBounds { lower: Real, upper: Real, value: Real },
    /// The variable index is out of bounds.
    InvalidVariable { index: usize, nvars: usize },
    /// Disconnected channel.
    Disconnected,
    /// An error occurred in a subprocess.
    Process(RecvError),
    /// A method requiring an initialized solver has been called.
    NotInitialized,
    /// The problem has not been solved yet.
    NotSolved,
}

/// The result type of the solver.
///
/// - `T` is the value type,
/// - `P` is the `FirstOrderProblem` associated with the solver,
/// - `M` is the `MasterBuilder` associated with the solver.
pub type Result<T, P, M> = std::result::Result<
    T,
    Error<<<M as MasterBuilder>::MasterProblem as MasterProblem>::Err, <P as FirstOrderProblem>::Err>,
>;

impl<MErr, PErr> std::fmt::Display for Error<MErr, PErr>
where
    MErr: std::fmt::Display,
    PErr: std::fmt::Display,
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;
        match self {
            BuildMaster(err) => writeln!(fmt, "Cannot create master problem solver: {}", err),
            Master(err) => writeln!(fmt, "Error in master problem: {}", err),
            IterationLimit { limit } => writeln!(fmt, "The iteration limit has been reached: {}", limit),
            Evaluation(err) => writeln!(fmt, "Error in subproblem evaluation: {}", err),
            Update(err) => writeln!(fmt, "Error in subproblem update: {}", err),
            Dimension(what) => writeln!(fmt, "Wrong dimension for {}", what),
            InvalidBounds { lower, upper } => write!(fmt, "Invalid bounds, lower:{}, upper:{}", lower, upper),
            ViolatedBounds { lower, upper, value } => write!(
                fmt,
                "Violated bounds, lower:{}, upper:{}, value:{}",
                lower, upper, value
            ),
            InvalidVariable { index, nvars } => {
                write!(fmt, "Variable index out of bounds, got:{} must be < {}", index, nvars)
            }
            Disconnected => writeln!(fmt, "A channel got disconnected"),
            Process(err) => writeln!(fmt, "Error in subprocess: {}", err),
            NotInitialized => writeln!(fmt, "The solver must be initialized (called Solver::init()?)"),
            NotSolved => writeln!(fmt, "The problem has not been solved yet"),
        }
    }
}

impl<MErr, PErr> std::error::Error for Error<MErr, PErr>
where
    MErr: std::error::Error + 'static,
    PErr: std::error::Error + 'static,
{
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;
        match self {
            BuildMaster(err) => Some(err),
            Master(err) => Some(err),
            Evaluation(err) => Some(err),
            Process(err) => Some(err),
            _ => None,
        }
    }
}

impl<MErr, PErr> From<masterprocess::Error<MErr, PErr>> for Error<MErr, PErr>
where
    MErr: std::error::Error + 'static,
{
    fn from(err: masterprocess::Error<MErr, PErr>) -> Error<MErr, PErr> {
        use masterprocess::Error::*;
        match err {
            DisconnectedSender => Error::Disconnected,
            DisconnectedReceiver => Error::Disconnected,
            Aggregation(err) => Error::Master(err),
            SubgradientExtension(err) => Error::Update(err),
            Master(err) => Error::Master(err.into()),
        }
    }
}

impl<MErr, PErr> From<RecvError> for Error<MErr, PErr> {
    fn from(err: RecvError) -> Error<MErr, PErr> {
        Error::Process(err.into())
    }
}

type ClientSender<P> =
    Sender<std::result::Result<EvalResult<usize, <P as FirstOrderProblem>::Primal>, <P as FirstOrderProblem>::Err>>;

417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
    /// This is actually the time of the last call to `Solver::init`.
    start_time: Instant,
}

impl<P, T, W, M> Solver<P, T, W, M>
where
    P: FirstOrderProblem,
    P::Err: Into<Box<dyn std::error::Error + Sync + Send>> + Send + 'static,
    T: Terminator<SolverData> + Default,
    W: Weighter<SolverData> + Default,
    M: MasterBuilder,
    <M::MasterProblem as MasterProblem>::MinorantIndex: std::hash::Hash,
{
    /// Create a new parallel bundle solver.
    pub fn new(problem: P) -> Self







|







439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    /// This is actually the time of the last call to `Solver::init`.
    start_time: Instant,
}

impl<P, T, W, M> Solver<P, T, W, M>
where
    P: FirstOrderProblem,
    P::Err: Send + 'static,
    T: Terminator<SolverData> + Default,
    W: Weighter<SolverData> + Default,
    M: MasterBuilder,
    <M::MasterProblem as MasterProblem>::MinorantIndex: std::hash::Hash,
{
    /// Create a new parallel bundle solver.
    pub fn new(problem: P) -> Self
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
    ///
    /// This will reset the internal data structures so that a new fresh
    /// solution process can be started.
    ///
    /// It will also setup all worker processes.
    ///
    /// This function is automatically called by [`Solver::solve`].
    pub fn init(&mut self) -> Result<(), Error<P::Err>> {
        debug!("Initialize solver");

        let n = self.problem.num_variables();
        let m = self.problem.num_subproblems();

        self.data.init(dvec![0.0; n]);
        self.cnt_descent = 0;







|







508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    ///
    /// This will reset the internal data structures so that a new fresh
    /// solution process can be started.
    ///
    /// It will also setup all worker processes.
    ///
    /// This function is automatically called by [`Solver::solve`].
    pub fn init(&mut self) -> Result<(), P, M> {
        debug!("Initialize solver");

        let n = self.problem.num_variables();
        let m = self.problem.num_subproblems();

        self.data.init(dvec![0.0; n]);
        self.cnt_descent = 0;
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
        self.data.cnt_remaining_mins = num_subproblems;
        self.data.nxt_d = Arc::new(dvec![0.0; num_variables]);
        self.data.nxt_y = Arc::new(dvec![]);
        self.data.updated = true;
    }

    /// Solve the problem with the default maximal iteration limit [`DEFAULT_ITERATION_LIMIT`].
    pub fn solve(&mut self) -> Result<(), Error<P::Err>> {
        self.solve_with_limit(DEFAULT_ITERATION_LIMIT)
    }

    /// Solve the problem with a maximal iteration limit.
    pub fn solve_with_limit(&mut self, limit: usize) -> Result<(), Error<P::Err>> {
        // First initialize the internal data structures.
        self.init()?;

        if self.solve_iter(limit)? {
            Ok(())
        } else {
            Err(Error::IterationLimit { limit })
        }
    }

    /// Solve the problem but stop after at most `niter` iterations.
    ///
    /// The function returns `Ok(true)` if the termination criterion
    /// has been satisfied. Otherwise it returns `Ok(false)` or an
    /// error code.
    ///
    /// If this function is called again, the solution process is
    /// continued from the previous point. Because of this one *must*
    /// call `init()` before the first call to this function.
    pub fn solve_iter(&mut self, niter: usize) -> Result<bool, Error<P::Err>> {
        debug!("Start solving up to {} iterations", niter);

        self.reset_iteration_data(niter);
        loop {
            select! {
                recv(self.client_rx.as_ref().ok_or(Error::NotInitialized)?) -> msg => {
                    let msg = msg?.map_err(Error::Evaluation)?;







|




|



















|







587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
        self.data.cnt_remaining_mins = num_subproblems;
        self.data.nxt_d = Arc::new(dvec![0.0; num_variables]);
        self.data.nxt_y = Arc::new(dvec![]);
        self.data.updated = true;
    }

    /// Solve the problem with the default maximal iteration limit [`DEFAULT_ITERATION_LIMIT`].
    pub fn solve(&mut self) -> Result<(), P, M> {
        self.solve_with_limit(DEFAULT_ITERATION_LIMIT)
    }

    /// Solve the problem with a maximal iteration limit.
    pub fn solve_with_limit(&mut self, limit: usize) -> Result<(), P, M> {
        // First initialize the internal data structures.
        self.init()?;

        if self.solve_iter(limit)? {
            Ok(())
        } else {
            Err(Error::IterationLimit { limit })
        }
    }

    /// Solve the problem but stop after at most `niter` iterations.
    ///
    /// The function returns `Ok(true)` if the termination criterion
    /// has been satisfied. Otherwise it returns `Ok(false)` or an
    /// error code.
    ///
    /// If this function is called again, the solution process is
    /// continued from the previous point. Because of this one *must*
    /// call `init()` before the first call to this function.
    pub fn solve_iter(&mut self, niter: usize) -> Result<bool, P, M> {
        debug!("Start solving up to {} iterations", niter);

        self.reset_iteration_data(niter);
        loop {
            select! {
                recv(self.client_rx.as_ref().ok_or(Error::NotInitialized)?) -> msg => {
                    let msg = msg?.map_err(Error::Evaluation)?;
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

    /// Handle a response from a subproblem evaluation.
    ///
    /// The function returns `Ok(true)` if the final iteration count has been reached.
    fn handle_client_response(
        &mut self,
        msg: EvalResult<usize, <P as FirstOrderProblem>::Primal>,
    ) -> Result<bool, Error<P::Err>> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;
        match msg {
            EvalResult::ObjectiveValue { index, value } => {
                debug!("Receive objective from subproblem {}: {}", index, value);
                if self.data.nxt_ubs[index].is_infinite() {
                    self.data.cnt_remaining_ubs -= 1;
                }







|







642
643
644
645
646
647
648
649
650
651
652
653
654
655
656

    /// Handle a response from a subproblem evaluation.
    ///
    /// The function returns `Ok(true)` if the final iteration count has been reached.
    fn handle_client_response(
        &mut self,
        msg: EvalResult<usize, <P as FirstOrderProblem>::Primal>,
    ) -> Result<bool, P, M> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;
        match msg {
            EvalResult::ObjectiveValue { index, value } => {
                debug!("Receive objective from subproblem {}: {}", index, value);
                if self.data.nxt_ubs[index].is_infinite() {
                    self.data.cnt_remaining_ubs -= 1;
                }
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
        // Compute the new candidate. The main loop will wait for the result of
        // this solution process of the master problem.
        self.master_proc.as_mut().unwrap().solve(self.data.cur_val)?;

        Ok(self.data.cnt_iter >= self.data.max_iter)
    }

    fn handle_master_response(&mut self, master_res: MasterResponse) -> Result<bool, Error<P::Err>> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;

        self.data.nxt_mod = master_res.nxt_mod;
        self.data.sgnorm = master_res.sgnorm;
        self.data.expected_progress = self.data.cur_val - self.data.nxt_mod;
        self.data.cnt_updates = master_res.cnt_updates;








|







743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
        // Compute the new candidate. The main loop will wait for the result of
        // this solution process of the master problem.
        self.master_proc.as_mut().unwrap().solve(self.data.cur_val)?;

        Ok(self.data.cnt_iter >= self.data.max_iter)
    }

    fn handle_master_response(&mut self, master_res: MasterResponse) -> Result<bool, P, M> {
        let master = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;

        self.data.nxt_mod = master_res.nxt_mod;
        self.data.sgnorm = master_res.sgnorm;
        self.data.expected_progress = self.data.cur_val - self.data.nxt_mod;
        self.data.cnt_updates = master_res.cnt_updates;

776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
            self.problem
                .evaluate(i, self.data.nxt_y.clone(), ChannelSender::new(i, client_tx.clone()))
                .map_err(Error::Evaluation)?;
        }
        Ok(false)
    }

    fn update_problem(&mut self, step: Step) -> Result<bool, Error<P::Err>> {
        let master_proc = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;
        let (update_tx, update_rx) = channel();
        self.problem
            .update(
                UpdateData {
                    cur_y: Arc::new(self.data.cur_y.clone()),
                    nxt_y: self.data.nxt_y.clone(),







|







798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
            self.problem
                .evaluate(i, self.data.nxt_y.clone(), ChannelSender::new(i, client_tx.clone()))
                .map_err(Error::Evaluation)?;
        }
        Ok(false)
    }

    fn update_problem(&mut self, step: Step) -> Result<bool, P, M> {
        let master_proc = self.master_proc.as_mut().ok_or(Error::NotInitialized)?;
        let (update_tx, update_rx) = channel();
        self.problem
            .update(
                UpdateData {
                    cur_y: Arc::new(self.data.cur_y.clone()),
                    nxt_y: self.data.nxt_y.clone(),
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
            self.data.nxt_mod,
            self.data.nxt_val,
            self.data.cur_val
        );
    }

    /// Return the aggregated primal of the given subproblem.
    pub fn aggregated_primal(&self, subproblem: usize) -> Result<P::Primal, Error<P::Err>> {
        Ok(self
            .master_proc
            .as_ref()
            .ok_or(Error::NotSolved)?
            .get_aggregated_primal(subproblem)?)
    }
}







|







897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
            self.data.nxt_mod,
            self.data.nxt_val,
            self.data.cur_val
        );
    }

    /// Return the aggregated primal of the given subproblem.
    pub fn aggregated_primal(&self, subproblem: usize) -> Result<P::Primal, P, M> {
        Ok(self
            .master_proc
            .as_ref()
            .ok_or(Error::NotSolved)?
            .get_aggregated_primal(subproblem)?)
    }
}