RsBundle  Check-in [58f8ffe7a1]

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

Overview
Comment:Implement `MasterProblemError` as more specific enum
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | error-handling
Files: files | file ages | folders
SHA1: 58f8ffe7a128b455f398130f00639f911fa4b2fb
User & Date: fifr 2018-06-26 21:39:59.184
Context
2018-06-27
07:05
solver: remove unnecessary `move` from extend subgradient closure check-in: f410ce94ba user: fifr tags: error-handling
2018-06-26
21:39
Implement `MasterProblemError` as more specific enum check-in: 58f8ffe7a1 user: fifr tags: error-handling
21:17
master: Subgradient extension callback may return an error. check-in: cd30b6f272 user: fifr tags: error-handling
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/master/base.rs.
13
14
15
16
17
18
19

20
21
22











23











24
25








26
27
28
29
30
31
32
// 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 {DVector, Minorant, Real};

use std::error::Error;

use std::result;

/// Error type for master problems.











///











/// For now they can be arbitrary, unspecified errors.
pub type MasterProblemError = Box<dyn Error>;









/// Result type of master problems.
pub type Result<T> = result::Result<T, MasterProblemError>;

pub trait MasterProblem {
    /// Unique index for a minorant.
    type MinorantIndex: Copy + Eq;







>



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







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
// 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 {DVector, Minorant, Real};

use std::error::Error;
use std::fmt;
use std::result;

/// Error type for master problems.
#[derive(Debug)]
pub enum MasterProblemError {
    /// Extension of the subgradient failed with some user error.
    SubgradientExtension(Box<dyn Error>),
    /// No minorants available when solving the master problem
    NoMinorants,
    /// An error in the solver backend occurred.
    Solver(Box<dyn Error>),
    /// A custom error (specific to the master problem solver) occurred.
    Custom(Box<dyn Error>),
}

impl fmt::Display for MasterProblemError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        use self::MasterProblemError::*;
        match self {
            SubgradientExtension(err) => write!(fmt, "Subgradient extension failed: {}", err),
            NoMinorants => write!(fmt, "No minorants when solving the master problem"),
            Solver(err) => write!(fmt, "Solver error: {}", err),
            Custom(err) => err.fmt(fmt),
        }
    }
}

impl Error for MasterProblemError {
    fn cause(&self) -> Option<&Error> {
        use self::MasterProblemError::*;
        match self {
            SubgradientExtension(err) | Solver(err) | Custom(err) => Some(err.as_ref()),
            NoMinorants => None,
        }
    }
}

/// Result type of master problems.
pub type Result<T> = result::Result<T, MasterProblemError>;

pub trait MasterProblem {
    /// Unique index for a minorant.
    type MinorantIndex: Copy + Eq;
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    /// Add or movesome 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 FnMut(usize, Self::MinorantIndex, &[usize]) -> Result<DVector>,
    ) -> Result<()>;

    /// 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.







|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    /// Add or movesome 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 FnMut(usize, Self::MinorantIndex, &[usize]) -> result::Result<DVector, Box<dyn Error>>,
    ) -> Result<()>;

    /// 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.
Changes to src/master/boxed.rs.
16
17
18
19
20
21
22

23

24
25
26
27
28
29
30

use master::MasterProblem;
use master::UnconstrainedMasterProblem;
use {DVector, Minorant, Real};

use super::Result;
use itertools::multizip;

use std::f64::{EPSILON, INFINITY, NEG_INFINITY};


/**
 * Turn unconstrained master problem into box-constrained one.
 *
 * This master problem adds box constraints to an unconstrainted
 * master problem implementation. The box constraints are enforced by
 * an additional outer optimization loop.







>

>







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

use master::MasterProblem;
use master::UnconstrainedMasterProblem;
use {DVector, Minorant, Real};

use super::Result;
use itertools::multizip;
use std::error::Error;
use std::f64::{EPSILON, INFINITY, NEG_INFINITY};
use std::result;

/**
 * Turn unconstrained master problem into box-constrained one.
 *
 * This master problem adds box constraints to an unconstrainted
 * master problem implementation. The box constraints are enforced by
 * an additional outer optimization loop.
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    fn set_weight(&mut self, weight: Real) -> Result<()> {
        self.master.set_weight(weight)
    }

    fn add_vars(
        &mut self,
        bounds: &[(Option<usize>, Real, Real)],
        extend_subgradient: &mut FnMut(usize, Self::MinorantIndex, &[usize]) -> Result<DVector>,
    ) -> Result<()> {
        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));







|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
    fn set_weight(&mut self, weight: Real) -> Result<()> {
        self.master.set_weight(weight)
    }

    fn add_vars(
        &mut self,
        bounds: &[(Option<usize>, Real, Real)],
        extend_subgradient: &mut FnMut(usize, Self::MinorantIndex, &[usize]) -> result::Result<DVector, Box<dyn Error>>,
    ) -> Result<()> {
        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));
Changes to src/master/cpx.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
// along with this program.  If not, see  <http://www.gnu.org/licenses/>
//

//! Master problem implementation using CPLEX.

#![allow(unused_unsafe)]

use master::UnconstrainedMasterProblem;
use {DVector, Minorant, Real};

use cplex_sys as cpx;

use super::Result;
use std;
use std::error::Error;
use std::f64::{self, NEG_INFINITY};
use std::fmt;
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::result;

/// A solver error.
#[derive(Debug)]
pub enum CplexMasterError {
    NoMinorants,
}

impl fmt::Display for CplexMasterError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        use self::CplexMasterError::*;
        match self {
            NoMinorants => write!(fmt, "No minorants when solving the master problem"),
        }
    }
}

impl Error for CplexMasterError {}

pub struct CplexMaster {
    lp: *mut cpx::Lp,

    /// True if the QP must be updated.
    force_update: bool,








|








<




<
<
|
<
<
|
<
<
|
<
<
|
|
<
<
<







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
// along with this program.  If not, see  <http://www.gnu.org/licenses/>
//

//! Master problem implementation using CPLEX.

#![allow(unused_unsafe)]

use master::{Error as MasterProblemError, UnconstrainedMasterProblem};
use {DVector, Minorant, Real};

use cplex_sys as cpx;

use super::Result;
use std;
use std::error::Error;
use std::f64::{self, NEG_INFINITY};

use std::os::raw::{c_char, c_int};
use std::ptr;
use std::result;



impl From<cpx::CplexError> for MasterProblemError {


    fn from(err: cpx::CplexError) -> MasterProblemError {


        MasterProblemError::Solver(err.into())


    }
}




pub struct CplexMaster {
    lp: *mut cpx::Lp,

    /// True if the QP must be updated.
    force_update: bool,

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
        }
    }

    fn add_vars(
        &mut self,
        nnew: usize,
        changed: &[usize],
        extend_subgradient: &mut FnMut(usize, Self::MinorantIndex, &[usize]) -> Result<DVector>,
    ) -> Result<()> {
        debug_assert!(!self.minorants[0].is_empty());
        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() {
            if !mins.is_empty() {
                for (i, m) in mins.iter_mut().enumerate() {
                    let new_subg = extend_subgradient(fidx, i, &changedvars)?;

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







|











|
>







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
        }
    }

    fn add_vars(
        &mut self,
        nnew: usize,
        changed: &[usize],
        extend_subgradient: &mut FnMut(usize, Self::MinorantIndex, &[usize]) -> result::Result<DVector, Box<dyn Error>>,
    ) -> Result<()> {
        debug_assert!(!self.minorants[0].is_empty());
        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() {
            if !mins.is_empty() {
                for (i, m) in mins.iter_mut().enumerate() {
                    let new_subg =
                        extend_subgradient(fidx, i, &changedvars).map_err(MasterProblemError::SubgradientExtension)?;
                    for (&j, &g) in changed.iter().zip(new_subg.iter()) {
                        m.linear[j] = g;
                    }
                    m.linear.extend_from_slice(&new_subg[changed.len()..]);
                }
            }
        }
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
    fn solve(&mut self, eta: &DVector, _fbound: Real, _augbound: Real, _relprec: Real) -> Result<()> {
        if self.force_update || !self.updateinds.is_empty() {
            try!(self.init_qp());
        }

        let nvars = unsafe { cpx::getnumcols(cpx::env(), self.lp) as usize };
        if nvars == 0 {
            return Err(CplexMasterError::NoMinorants.into());
        }
        // update linear costs
        {
            let mut c = Vec::with_capacity(nvars);
            let mut inds = Vec::with_capacity(nvars);
            for mins in &self.minorants {
                for m in mins {







|







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
    fn solve(&mut self, eta: &DVector, _fbound: Real, _augbound: Real, _relprec: Real) -> Result<()> {
        if self.force_update || !self.updateinds.is_empty() {
            try!(self.init_qp());
        }

        let nvars = unsafe { cpx::getnumcols(cpx::env(), self.lp) as usize };
        if nvars == 0 {
            return Err(MasterProblemError::NoMinorants);
        }
        // update linear costs
        {
            let mut c = Vec::with_capacity(nvars);
            let mut inds = Vec::with_capacity(nvars);
            for mins in &self.minorants {
                for m in mins {
Changes to src/master/minimal.rs.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45






46
47
48
49
50
51
52
// 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 master::UnconstrainedMasterProblem;
use {DVector, Minorant, Real};

use super::Result;
use std::error::Error;
use std::f64::NEG_INFINITY;
use std::fmt;
use std::result;

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

impl fmt::Display for MinimalMasterError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        use self::MinimalMasterError::*;
        match self {
            NumSubproblems { nsubs } => write!(fmt, "Too many subproblems (got: {} must be <= 2)", nsubs),
            MaxMinorants => write!(fmt, "The minimal master problem allows at most two minorants"),
            NoMinorants => write!(fmt, "No minorants when solving the master problem"),
        }
    }
}

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







|













<








<





>
>
>
>
>
>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 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 master::{Error as MasterProblemError, UnconstrainedMasterProblem};
use {DVector, Minorant, Real};

use super::Result;
use std::error::Error;
use std::f64::NEG_INFINITY;
use std::fmt;
use std::result;

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

}

impl fmt::Display for MinimalMasterError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        use self::MinimalMasterError::*;
        match self {
            NumSubproblems { nsubs } => write!(fmt, "Too many subproblems (got: {} must be <= 2)", nsubs),
            MaxMinorants => write!(fmt, "The minimal master problem allows at most two minorants"),

        }
    }
}

impl Error for MinimalMasterError {}

impl Into<MasterProblemError> for MinimalMasterError {
    fn into(self) -> MasterProblemError {
        MasterProblemError::Custom(self.into())
    }
}

/**
 * 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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
        Ok(self.minorants.len() - 1)
    }

    fn add_vars(
        &mut self,
        nnew: usize,
        changed: &[usize],
        extend_subgradient: &mut FnMut(usize, Self::MinorantIndex, &[usize]) -> Result<DVector>,
    ) -> Result<()> {
        if !self.minorants.is_empty() {
            let noldvars = self.minorants[0].linear.len();
            let mut changedvars = vec![];
            changedvars.extend_from_slice(changed);
            changedvars.extend(noldvars..noldvars + nnew);
            for (i, m) in self.minorants.iter_mut().enumerate() {
                let new_subg = extend_subgradient(0, i, &changedvars)?;

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








|







|
>







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
        Ok(self.minorants.len() - 1)
    }

    fn add_vars(
        &mut self,
        nnew: usize,
        changed: &[usize],
        extend_subgradient: &mut FnMut(usize, Self::MinorantIndex, &[usize]) -> result::Result<DVector, Box<dyn Error>>,
    ) -> Result<()> {
        if !self.minorants.is_empty() {
            let noldvars = self.minorants[0].linear.len();
            let mut changedvars = vec![];
            changedvars.extend_from_slice(changed);
            changedvars.extend(noldvars..noldvars + nnew);
            for (i, m) in self.minorants.iter_mut().enumerate() {
                let new_subg =
                    extend_subgradient(0, i, &changedvars).map_err(MasterProblemError::SubgradientExtension)?;
                for (&j, &g) in changed.iter().zip(new_subg.iter()) {
                    m.linear[j] = g;
                }
                m.linear.extend_from_slice(&new_subg[changed.len()..]);
            }
        }

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
            self.opt_mult[1] = alpha2;
            self.opt_minorant = self.minorants[0].combine(1.0 - alpha2, alpha2, &self.minorants[1]);
        } else if self.minorants.len() == 1 {
            self.opt_minorant = self.minorants[0].clone();
            self.opt_mult.resize(1, 1.0);
            self.opt_mult[0] = 1.0;
        } else {
            return Err(MinimalMasterError::NoMinorants.into());
        }

        debug!("Unrestricted");
        debug!("  opt_minorant={}", self.opt_minorant);
        if self.opt_mult.len() == 2 {
            debug!("  opt_mult={}", self.opt_mult);
        }







|







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
            self.opt_mult[1] = alpha2;
            self.opt_minorant = self.minorants[0].combine(1.0 - alpha2, alpha2, &self.minorants[1]);
        } else if self.minorants.len() == 1 {
            self.opt_minorant = self.minorants[0].clone();
            self.opt_mult.resize(1, 1.0);
            self.opt_mult[0] = 1.0;
        } else {
            return Err(MasterProblemError::NoMinorants);
        }

        debug!("Unrestricted");
        debug!("  opt_minorant={}", self.opt_minorant);
        if self.opt_mult.len() == 2 {
            debug!("  opt_mult={}", self.opt_mult);
        }
Changes to src/master/unconstrained.rs.
14
15
16
17
18
19
20



21
22
23
24
25
26
27
// along with this program.  If not, see  <http://www.gnu.org/licenses/>
//

use {DVector, Minorant, Real};

use super::Result;




/**
 * Trait for master problems without box constraints.
 *
 * Implementors of this trait are supposed to solve quadratic
 * optimization problems of the form
 *
 * \\[ \min \left\\{ \hat{f}(d) + \frac{u}{2} \\| d \\|\^2 \colon







>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// along with this program.  If not, see  <http://www.gnu.org/licenses/>
//

use {DVector, Minorant, Real};

use super::Result;

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

/**
 * Trait for master problems without box constraints.
 *
 * Implementors of this trait are supposed to solve quadratic
 * optimization problems of the form
 *
 * \\[ \min \left\\{ \hat{f}(d) + \frac{u}{2} \\| d \\|\^2 \colon
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    /// 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 FnMut(usize, Self::MinorantIndex, &[usize]) -> Result<DVector>,
    ) -> Result<()>;

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

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







|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
    /// 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 FnMut(usize, Self::MinorantIndex, &[usize]) -> result::Result<DVector, Box<dyn Error>>,
    ) -> Result<()>;

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

    /// Return the current dual optimal solution.
    fn dualopt(&self) -> &DVector;
Changes to src/solver.rs.
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
}

impl<E: Error> Error for SolverError<E> {
    fn cause(&self) -> Option<&Error> {
        match self {
            SolverError::Evaluation(err) => Some(err),
            SolverError::Update(err) => Some(err),
            SolverError::Master(err) => Some(err.as_ref()),
            _ => None,
        }
    }
}

impl<E> From<ParameterError> for SolverError<E> {
    fn from(err: ParameterError) -> SolverError<E> {







|







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
}

impl<E: Error> Error for SolverError<E> {
    fn cause(&self) -> Option<&Error> {
        match self {
            SolverError::Evaluation(err) => Some(err),
            SolverError::Update(err) => Some(err),
            SolverError::Master(err) => Some(err),
            _ => None,
        }
    }
}

impl<E> From<ParameterError> for SolverError<E> {
    fn from(err: ParameterError) -> SolverError<E> {