Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Introduce master problem builder |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | master-builder |
| Files: | files | file ages | folders |
| SHA1: |
5ee9fe59e5bd74857a7dcdb2990c3830 |
| User & Date: | fifr 2019-07-22 08:52:58.146 |
Context
|
2019-07-22
| ||
| 08:54 | masterprocess: simplify some types check-in: 5e14cab158 user: fifr tags: master-builder | |
| 08:52 | Introduce master problem builder check-in: 5ee9fe59e5 user: fifr tags: master-builder | |
|
2019-07-21
| ||
| 20:54 | master::base: remove old commented code check-in: f91bd4a57c user: fifr tags: async | |
Changes
Changes to src/master/base.rs.
| ︙ | ︙ | |||
109 110 111 112 113 114 115 |
/// Return the multiplier associated with a minorant.
fn multiplier(&self, min: Self::MinorantIndex) -> Real;
/// Move the center of the master problem to $\alpha \cdot d$.
fn move_center(&mut self, alpha: Real, d: &DVector);
}
| > > > > > > > > > > > > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
/// Return the multiplier associated with a minorant.
fn multiplier(&self, min: Self::MinorantIndex) -> Real;
/// Move the center of the master problem to $\alpha \cdot d$.
fn move_center(&mut self, alpha: Real, d: &DVector);
}
/// A builder for creating a master problem solvers.
pub trait Builder {
/// The master problem to be build.
type MasterProblem: MasterProblem;
/// Type of errors.
type Err;
/// Create a new master problem.
fn build(&mut self) -> Result<Self::MasterProblem, Self::Err>;
}
|
Changes to src/master/boxed.rs.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | // 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/> // | < | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// 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 crate::master::{self, MasterProblem, SubgradientExtension, UnconstrainedMasterProblem};
use crate::{DVector, Minorant, Real};
use itertools::multizip;
use log::debug;
use std::f64::{EPSILON, INFINITY, NEG_INFINITY};
/**
|
| ︙ | ︙ | |||
357 358 359 360 361 362 363 |
BoxedMasterProblem::set_max_updates(self, max_updates)
}
fn cnt_updates(&self) -> usize {
self.cnt_updates
}
}
| > > > > > > > > > > > > > > > > > > > > | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
BoxedMasterProblem::set_max_updates(self, max_updates)
}
fn cnt_updates(&self) -> usize {
self.cnt_updates
}
}
/// Builder for `BoxedMasterProblem`.
///
/// `B` is a builder of the underlying `UnconstrainedMasterProblem`.
#[derive(Default)]
pub struct Builder<B>(B);
impl<B> master::Builder for Builder<B>
where
B: master::unconstrained::Builder,
B::MasterProblem: UnconstrainedMasterProblem,
{
type MasterProblem = BoxedMasterProblem<B::MasterProblem>;
type Err = B::Err;
fn build(&mut self) -> Result<Self::MasterProblem, Self::Err> {
self.0.build().map(BoxedMasterProblem::with_master)
}
}
|
Changes to src/master/cpx.rs.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 | // along with this program. If not, see <http://www.gnu.org/licenses/> // //! Master problem implementation using CPLEX. #![allow(unused_unsafe)] | | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// along with this program. If not, see <http://www.gnu.org/licenses/>
//
//! Master problem implementation using CPLEX.
#![allow(unused_unsafe)]
use crate::master::unconstrained::{self, UnconstrainedMasterProblem};
use crate::master::SubgradientExtension;
use crate::{Aggregatable, DVector, Minorant, Real};
use c_str_macro::c_str;
use cplex_sys as cpx;
use cplex_sys::trycpx;
use log::debug;
|
| ︙ | ︙ | |||
95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
/// The minorants for each subproblem in the model.
minorants: Vec<Vec<Minorant>>,
/// Optimal multipliers for each subproblem in the model.
opt_mults: Vec<DVector>,
/// Optimal aggregated minorant.
opt_minorant: Minorant,
}
impl Drop for CplexMaster {
fn drop(&mut self) {
unsafe { cpx::freeprob(cpx::env(), &mut self.lp) };
}
}
| > > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
/// The minorants for each subproblem in the model.
minorants: Vec<Vec<Minorant>>,
/// Optimal multipliers for each subproblem in the model.
opt_mults: Vec<DVector>,
/// Optimal aggregated minorant.
opt_minorant: Minorant,
}
unsafe impl Send for CplexMaster {}
impl Drop for CplexMaster {
fn drop(&mut self) {
unsafe { cpx::freeprob(cpx::env(), &mut self.lp) };
}
}
|
| ︙ | ︙ | |||
526 527 528 529 530 531 532 |
self.updateinds.clear();
self.force_update = false;
Ok(())
}
}
| > > > > > > > > > > > > > | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
self.updateinds.clear();
self.force_update = false;
Ok(())
}
}
#[derive(Default)]
pub struct Builder;
impl unconstrained::Builder for Builder {
type MasterProblem = CplexMaster;
type Err = CplexMasterError;
fn build(&mut self) -> Result<CplexMaster> {
CplexMaster::new()
}
}
|
Changes to src/master/minimal.rs.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 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 crate::master::{SubgradientExtension, UnconstrainedMasterProblem};
use crate::{Aggregatable, DVector, Minorant, Real};
use log::debug;
use std::error::Error;
use std::f64::NEG_INFINITY;
| > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// 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 crate::master::unconstrained;
use crate::master::{SubgradientExtension, UnconstrainedMasterProblem};
use crate::{Aggregatable, DVector, Minorant, Real};
use log::debug;
use std::error::Error;
use std::f64::NEG_INFINITY;
|
| ︙ | ︙ | |||
238 239 240 241 242 243 244 |
fn move_center(&mut self, alpha: Real, d: &DVector) {
for m in &mut self.minorants {
m.move_center(alpha, d);
}
}
}
| > > > > > > > > > > > > | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
fn move_center(&mut self, alpha: Real, d: &DVector) {
for m in &mut self.minorants {
m.move_center(alpha, d);
}
}
}
pub struct Builder;
impl unconstrained::Builder for Builder {
type MasterProblem = MinimalMaster;
type Err = MinimalMasterError;
fn build(&mut self) -> Result<MinimalMaster, Self::Err> {
MinimalMaster::new()
}
}
|
Changes to src/master/mod.rs.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
//! * changing the weight parameter $w$,
//! * modifying $\hat{f}$ by adding or removing linear functions $\ell_i$,
//! * moving the center of the linear functions $\ell_i$ (and the
//! bounds), i.e. replacing $\hat{f}$ by $d \mapsto \hat{f}(d -
//! \hat{d})$ for some given $\hat{d} \in \mathbb{R}\^n$.
mod base;
| | | | | < | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
//! * changing the weight parameter $w$,
//! * modifying $\hat{f}$ by adding or removing linear functions $\ell_i$,
//! * moving the center of the linear functions $\ell_i$ (and the
//! bounds), i.e. replacing $\hat{f}$ by $d \mapsto \hat{f}(d -
//! \hat{d})$ for some given $\hat{d} \in \mathbb{R}\^n$.
mod base;
pub use self::base::{Builder, MasterProblem, SubgradientExtension};
pub mod boxed;
pub use self::boxed::BoxedMasterProblem;
pub mod unconstrained;
pub use self::unconstrained::UnconstrainedMasterProblem;
pub mod minimal;
pub use self::minimal::MinimalMaster;
// pub mod grb;
// pub use master::grb::GurobiMaster;
pub mod cpx;
|
Changes to src/master/unconstrained.rs.
| ︙ | ︙ | |||
110 111 112 113 114 115 116 |
/// # Error
/// The indices of the minorants `mins` must belong to subproblem `fidx`.
fn aggregate(&mut self, fidx: usize, mins: &[usize]) -> Result<(Self::MinorantIndex, DVector), Self::Err>;
/// Move the center of the master problem along $\alpha \cdot d$.
fn move_center(&mut self, alpha: Real, d: &DVector);
}
| > > > > > > > > > > > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
/// # Error
/// The indices of the minorants `mins` must belong to subproblem `fidx`.
fn aggregate(&mut self, fidx: usize, mins: &[usize]) -> Result<(Self::MinorantIndex, DVector), Self::Err>;
/// Move the center of the master problem along $\alpha \cdot d$.
fn move_center(&mut self, alpha: Real, d: &DVector);
}
/// A builder for creating unconstrained master problem solvers.
pub trait Builder {
/// The master problem to be build.
type MasterProblem: UnconstrainedMasterProblem;
/// Type of errors.
type Err;
/// Create a new master problem.
fn build(&mut self) -> Result<Self::MasterProblem, Self::Err>;
}
|
Changes to src/parallel/masterprocess.rs.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
use crossbeam::channel::{unbounded as channel, Receiver, Sender};
use log::{debug, warn};
use std::sync::Arc;
use threadpool::ThreadPool;
use super::solver::Error;
use super::FirstOrderProblem;
| | < < | 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 |
use crossbeam::channel::{unbounded as channel, Receiver, Sender};
use log::{debug, warn};
use std::sync::Arc;
use threadpool::ThreadPool;
use super::solver::Error;
use super::FirstOrderProblem;
use crate::master::MasterProblem;
use crate::{DVector, Minorant, Real};
/// Configuration information for setting up a master problem.
pub struct MasterConfig {
/// The number of subproblems.
pub num_subproblems: usize,
/// The number of variables.
pub num_vars: usize,
/// The lower bounds on the variables.
pub lower_bounds: Option<DVector>,
/// The lower bounds on the variables.
pub upper_bounds: Option<DVector>,
}
/// A task for the master problem.
enum MasterTask<Pr> {
/// Add a new minorant for a subfunction to the master problem.
AddMinorant(usize, Minorant, Pr),
/// Move the center of the master problem in the given direction.
MoveCenter(Real, Arc<DVector>),
|
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
pub sgnorm: Real,
}
type ToMasterSender<Pr> = Sender<MasterTask<Pr>>;
type ToMasterReceiver<Pr> = Receiver<MasterTask<Pr>>;
| | | | | > | | | 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 |
pub sgnorm: Real,
}
type ToMasterSender<Pr> = Sender<MasterTask<Pr>>;
type ToMasterReceiver<Pr> = Receiver<MasterTask<Pr>>;
type MasterSender<E> = Sender<std::result::Result<MasterResponse, E>>;
pub type MasterReceiver<E> = Receiver<std::result::Result<MasterResponse, E>>;
pub struct MasterProcess<P, M>
where
P: FirstOrderProblem,
M: MasterProblem,
{
/// The channel to transmit new tasks to the master problem.
tx: ToMasterSender<P::Primal>,
/// The channel to receive solutions from the master problem.
pub rx: MasterReceiver<M::Err>,
phantom: std::marker::PhantomData<M>,
}
/// Information about a minorant.
#[derive(Debug, Clone)]
struct MinorantInfo<Pr> {
/// The minorant's index in the master problem
index: usize,
/// Current multiplier.
multiplier: Real,
/// Primal associated with this minorant.
primal: Option<Pr>,
}
impl<P, M> MasterProcess<P, M>
where
P: FirstOrderProblem,
P::Primal: Send + 'static,
M: MasterProblem<MinorantIndex = usize> + Send + 'static,
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();
let (from_master_tx, from_master_rx) = channel();
// The the master process thread.
threadpool.execute(move || {
debug!("Master process started");
let mut from_master_tx = from_master_tx;
if let Err(err) = Self::master_main(master, master_config, &mut from_master_tx, to_master_rx) {
#[allow(unused_must_use)]
{
from_master_tx.send(Err(err));
}
}
debug!("Master proces stopped");
});
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 181 |
self.tx
.send(MasterTask::SetWeight { weight })
.map_err(|err| Error::Process(err.into()))
}
/// The main loop of the master process.
fn master_main(
master_config: MasterConfig,
| > | | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
self.tx
.send(MasterTask::SetWeight { weight })
.map_err(|err| Error::Process(err.into()))
}
/// The main loop of the master process.
fn master_main(
master: M,
master_config: MasterConfig,
tx: &mut MasterSender<M::Err>,
rx: ToMasterReceiver<P::Primal>,
) -> std::result::Result<(), M::Err> {
let mut master = master;
let mut minorants: Vec<MinorantInfo<P::Primal>> = vec![];
// Initialize the master problem.
master.set_num_subproblems(master_config.num_subproblems)?;
master.set_vars(
master_config.num_vars,
master_config.lower_bounds,
|
| ︙ | ︙ |
Changes to src/parallel/solver.rs.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
use std::time::Instant;
use threadpool::ThreadPool;
use crate::{DVector, Real};
use super::masterprocess::{MasterConfig, MasterProcess};
use super::problem::{EvalResult, FirstOrderProblem};
| > | > > | > > | 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 |
use std::time::Instant;
use threadpool::ThreadPool;
use crate::{DVector, Real};
use super::masterprocess::{MasterConfig, MasterProcess};
use super::problem::{EvalResult, FirstOrderProblem};
use crate::master::{boxed, cpx};
use crate::master::{BoxedMasterProblem, Builder, MasterProblem as MP};
use crate::solver::{SolverParams, Step};
use crate::terminator::{StandardTerminatable, StandardTerminator, Terminator};
use crate::weighter::{HKWeightable, HKWeighter, Weighter};
/// The default iteration limit.
pub const DEFAULT_ITERATION_LIMIT: usize = 10_000;
type MasterBuilder = boxed::Builder<cpx::Builder>;
type MasterProblem = BoxedMasterProblem<cpx::CplexMaster>;
type MasterProblemError = <MasterProblem as MP>::Err;
/// 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(MasterProblemError),
/// The iteration limit has been reached.
IterationLimit { limit: usize },
/// An error raised by a subproblem evaluation.
Evaluation(E),
/// The dimension of some data is wrong.
|
| ︙ | ︙ | |||
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 |
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 {
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),
Dimension(what) => writeln!(fmt, "Wrong dimension for {}", what),
Process(err) => writeln!(fmt, "Error in subprocess: {}", err),
NotInitialized => writeln!(fmt, "The solver must be initialized (called Solver::init()?)"),
}
}
}
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 {
Master(err) => Some(err),
Evaluation(err) => Some(err),
Process(err) => Some(err.as_ref()),
_ => None,
}
}
}
| > > | 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 |
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),
Dimension(what) => writeln!(fmt, "Wrong dimension for {}", what),
Process(err) => writeln!(fmt, "Error in subprocess: {}", err),
NotInitialized => writeln!(fmt, "The solver must be initialized (called Solver::init()?)"),
}
}
}
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),
Evaluation(err) => Some(err),
Process(err) => Some(err.as_ref()),
_ => None,
}
}
}
|
| ︙ | ︙ | |||
180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
pub threadpool: ThreadPool,
/// The first order problem.
problem: P,
/// The algorithm data.
data: SolverData,
/// The master problem process.
master: Option<MasterProcess<P, MasterProblem>>,
/// The channel to receive the evaluation results from subproblems.
client_tx: Option<ClientSender<P>>,
| > > > | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
pub threadpool: ThreadPool,
/// The first order problem.
problem: P,
/// The algorithm data.
data: SolverData,
/// The master problem builder.
master_builder: MasterBuilder,
/// The master problem process.
master: Option<MasterProcess<P, MasterProblem>>,
/// The channel to receive the evaluation results from subproblems.
client_tx: Option<ClientSender<P>>,
|
| ︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
nxt_mod: 0.0,
new_cutval: 0.0,
sgnorm: 0.0,
cur_weight: 1.0,
},
threadpool: ThreadPool::with_name("Parallel bundle solver".to_string(), num_cpus::get()),
master: None,
client_tx: None,
client_rx: None,
cnt_descent: 0,
cnt_null: 0,
cnt_evals: 0,
| > | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
nxt_mod: 0.0,
new_cutval: 0.0,
sgnorm: 0.0,
cur_weight: 1.0,
},
threadpool: ThreadPool::with_name("Parallel bundle solver".to_string(), num_cpus::get()),
master_builder: MasterBuilder::default(),
master: None,
client_tx: None,
client_rx: None,
cnt_descent: 0,
cnt_null: 0,
cnt_evals: 0,
|
| ︙ | ︙ | |||
310 311 312 313 314 315 316 |
.map(|ub| ub.len() != n)
.unwrap_or(false)
{
return Err(Error::Dimension("upper bounds".to_string()));
}
debug!("Start master process");
| | > > > > > > | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
.map(|ub| ub.len() != n)
.unwrap_or(false)
{
return Err(Error::Dimension("upper bounds".to_string()));
}
debug!("Start master process");
self.master = Some(MasterProcess::start(
self.master_builder
.build()
.map_err(|err| Error::BuildMaster(err.into()))?,
master_config,
&mut self.threadpool,
));
let master = self.master.as_mut().unwrap();
debug!("Initial problem evaluation");
// We need an initial evaluation of all oracles for the first center.
let y = Arc::new(self.data.cur_y.clone());
for i in 0..m {
self.problem
|
| ︙ | ︙ |
Changes to src/solver.rs.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
//
//! The main bundle method solver.
use crate::{Aggregatable, DVector, Real};
use crate::{Evaluation, FirstOrderProblem, Update};
| | < < > > | 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 |
//
//! The main bundle method solver.
use crate::{Aggregatable, DVector, Real};
use crate::{Evaluation, FirstOrderProblem, Update};
use crate::master::{self, boxed, cpx, minimal, MasterProblem};
use crate::terminator::{StandardTerminatable, StandardTerminator, Terminator};
use crate::weighter::{HKWeightable, HKWeighter, Weighter};
use log::{debug, info, warn};
use std::error::Error;
use std::f64::{INFINITY, NEG_INFINITY};
use std::fmt;
use std::mem::swap;
use std::time::Instant;
/// A solver error.
#[derive(Debug)]
pub enum SolverError<E, MErr> {
/// An error occurred during oracle evaluation.
Evaluation(E),
/// An error occurred during oracle update.
Update(E),
/// An error has been raised by the master problem.
BuildMaster(Box<dyn Error>),
/// An error has been raised by the master problem.
Master(MErr),
/// The oracle did not return a minorant.
NoMinorant,
/// The dimension of some data is wrong.
Dimension,
/// Some parameter has an invalid value.
|
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
}
impl<E, MErr> fmt::Display for SolverError<E, MErr>
where
E: fmt::Display,
MErr: fmt::Display,
{
| | > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
}
impl<E, MErr> fmt::Display for SolverError<E, MErr>
where
E: fmt::Display,
MErr: fmt::Display,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
use self::SolverError::*;
match self {
Evaluation(err) => write!(fmt, "Oracle evaluation failed: {}", err),
Update(err) => write!(fmt, "Oracle update failed: {}", err),
BuildMaster(err) => write!(fmt, "Creation of master problem failed: {}", err),
Master(err) => write!(fmt, "Master problem failed: {}", err),
NoMinorant => write!(fmt, "The oracle did not return a minorant"),
Dimension => write!(fmt, "Dimension of lower bounds does not match number of variables"),
Parameter(msg) => write!(fmt, "Parameter error: {}", msg),
InvalidBounds { lower, upper } => write!(fmt, "Invalid bounds, lower:{}, upper:{}", lower, upper),
ViolatedBounds { lower, upper, value } => write!(
fmt,
|
| ︙ | ︙ | |||
92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
MErr: Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
SolverError::Evaluation(err) => Some(err),
SolverError::Update(err) => Some(err),
SolverError::Master(err) => Some(err),
_ => None,
}
}
}
impl<E, MErr> From<MErr> for SolverError<E, MErr> {
fn from(err: MErr) -> SolverError<E, MErr> {
| > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
MErr: Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
SolverError::Evaluation(err) => Some(err),
SolverError::Update(err) => Some(err),
SolverError::Master(err) => Some(err),
SolverError::BuildMaster(err) => Some(err.as_ref()),
_ => None,
}
}
}
impl<E, MErr> From<MErr> for SolverError<E, MErr> {
fn from(err: MErr) -> SolverError<E, MErr> {
|
| ︙ | ︙ | |||
209 210 211 212 213 214 215 |
}
/// An invalid value for some parameter has been passes.
#[derive(Debug)]
pub struct ParameterError(String);
impl fmt::Display for ParameterError {
| | | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
}
/// An invalid value for some parameter has been passes.
#[derive(Debug)]
pub struct ParameterError(String);
impl fmt::Display for ParameterError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
write!(fmt, "{}", self.0)
}
}
impl Error for ParameterError {}
/// Parameters for tuning the solver.
|
| ︙ | ︙ | |||
248 249 250 251 252 253 254 |
* Must be in (0, acceptance_factor).
*/
pub nullstep_factor: Real,
}
impl SolverParams {
/// Verify that all parameters are valid.
| | | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
* Must be in (0, acceptance_factor).
*/
pub nullstep_factor: Real,
}
impl SolverParams {
/// Verify that all parameters are valid.
fn check(&self) -> std::result::Result<(), ParameterError> {
if self.max_bundle_size < 2 {
Err(ParameterError(format!(
"max_bundle_size must be >= 2 (got: {})",
self.max_bundle_size
)))
} else if self.acceptance_factor <= 0.0 || self.acceptance_factor >= 1.0 {
Err(ParameterError(format!(
|
| ︙ | ︙ | |||
343 344 345 346 347 348 349 350 |
///
/// This is the last primal generated by the oracle.
pub fn last_primal(&self, fidx: usize) -> Option<&Pr> {
self.minorants[fidx].last().and_then(|m| m.primal.as_ref())
}
}
/// The default bundle solver with general master problem.
| > > > > > > | | | > | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
///
/// This is the last primal generated by the oracle.
pub fn last_primal(&self, fidx: usize) -> Option<&Pr> {
self.minorants[fidx].last().and_then(|m| m.primal.as_ref())
}
}
/// The default builder.
pub type FullMasterBuilder = boxed::Builder<cpx::Builder>;
/// The minimal bundle builder.
pub type MinimalMasterBuilder = boxed::Builder<minimal::Builder>;
/// The default bundle solver with general master problem.
pub type DefaultSolver<P> = Solver<P, StandardTerminator, HKWeighter, FullMasterBuilder>;
/// A bundle solver with a minimal cutting plane model.
pub type NoBundleSolver<P> = Solver<P, StandardTerminator, HKWeighter, MinimalMasterBuilder>;
/**
* Implementation of a bundle method.
*/
pub struct Solver<P, T, W, M = FullMasterBuilder>
where
P: FirstOrderProblem,
M: master::Builder,
{
/// The first order problem description.
problem: P,
/// The solver parameter.
pub params: SolverParams,
|
| ︙ | ︙ | |||
435 436 437 438 439 440 441 |
* Time when the solution process started.
*
* This is actually the time of the last call to `Solver::init`.
*/
start_time: Instant,
/// The master problem.
| | > > > > > > > | | | | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
* Time when the solution process started.
*
* This is actually the time of the last call to `Solver::init`.
*/
start_time: Instant,
/// The master problem.
master: M::MasterProblem,
/// The active minorant indices for each subproblem.
minorants: Vec<Vec<MinorantInfo<P::Primal>>>,
/// Accumulated information about the last iteration.
iterinfos: Vec<IterationInfo>,
}
pub type Result<T, P, M> = std::result::Result<
T,
SolverError<<P as FirstOrderProblem>::Err, <<M as master::Builder>::MasterProblem as MasterProblem>::Err>,
>;
impl<P, T, W, M> Solver<P, T, W, M>
where
P: FirstOrderProblem,
P::Err: Into<Box<std::error::Error + Send + Sync + 'static>>,
T: for<'a> Terminator<BundleState<'a>> + Default,
W: for<'a> Weighter<BundleState<'a>> + Default,
M: master::Builder + Default,
M::Err: Into<Box<std::error::Error + Send + Sync + 'static>>,
M::MasterProblem: MasterProblem<MinorantIndex = usize>,
<M::MasterProblem as master::MasterProblem>::Err: Into<Box<std::error::Error + Send + Sync + 'static>>,
{
/**
* Create a new solver for the given problem.
*
* Note that the solver owns the problem, so you cannot use the
* same problem description elsewhere as long as it is assigned to
* the solver. However, it is possible to get a reference to the
* internally stored problem using `Solver::problem()`.
*/
#[allow(clippy::type_complexity)]
pub fn new_params(problem: P, params: SolverParams) -> Result<Solver<P, T, W, M>, P, M> {
Ok(Solver {
problem,
params,
terminator: T::default(),
weighter: W::default(),
bounds: vec![],
cur_y: dvec![],
|
| ︙ | ︙ | |||
487 488 489 490 491 492 493 |
nxt_mods: dvec![],
new_cutval: 0.0,
sgnorm: 0.0,
expected_progress: 0.0,
cnt_descent: 0,
cnt_null: 0,
start_time: Instant::now(),
| | | | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
nxt_mods: dvec![],
new_cutval: 0.0,
sgnorm: 0.0,
expected_progress: 0.0,
cnt_descent: 0,
cnt_null: 0,
start_time: Instant::now(),
master: M::default().build().map_err(|e| SolverError::BuildMaster(e.into()))?,
minorants: vec![],
iterinfos: vec![],
})
}
/// A new solver with default parameter.
#[allow(clippy::type_complexity)]
pub fn new(problem: P) -> Result<Solver<P, T, W, M>, P, M> {
Solver::new_params(problem, SolverParams::default())
}
/**
* Set the first order problem description associated with this
* solver.
*
|
| ︙ | ︙ | |||
518 519 520 521 522 523 524 |
/// Returns a reference to the solver's current problem.
pub fn problem(&self) -> &P {
&self.problem
}
/// Initialize the solver.
| | | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
/// Returns a reference to the solver's current problem.
pub fn problem(&self) -> &P {
&self.problem
}
/// Initialize the solver.
pub fn init(&mut self) -> Result<(), P, M> {
self.params.check().map_err(SolverError::Parameter)?;
if self.cur_y.len() != self.problem.num_variables() {
self.cur_valid = false;
self.cur_y.init0(self.problem.num_variables());
}
let lb = self.problem.lower_bounds();
|
| ︙ | ︙ | |||
562 563 564 565 566 567 568 |
Ok(())
}
/// Solve the problem with at most 10_000 iterations.
///
/// Use `solve_with_limit` for an explicit iteration limit.
| | | | | | 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 605 606 607 608 609 610 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 |
Ok(())
}
/// Solve the problem with at most 10_000 iterations.
///
/// Use `solve_with_limit` for an explicit iteration limit.
pub fn solve(&mut self) -> Result<(), P, M> {
const LIMIT: usize = 10_000;
self.solve_with_limit(LIMIT)
}
/// Solve the problem with explicit iteration limit.
pub fn solve_with_limit(&mut self, iter_limit: usize) -> Result<(), P, M> {
// First initialize the internal data structures.
self.init()?;
if self.solve_iter(iter_limit)? {
Ok(())
} else {
Err(SolverError::IterationLimit { limit: iter_limit })
}
}
/// Solve the problem but stop after `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> {
for _ in 0..niter {
let mut term = self.step()?;
let changed = self.update_problem(term)?;
// do not stop if the problem has been changed
if changed && term == Step::Term {
term = Step::Null
}
self.show_info(term);
if term == Step::Term {
return Ok(true);
}
}
Ok(false)
}
/// Called to update the problem.
///
/// Calling this function typically triggers the problem to
/// separate new constraints depending on the current solution.
fn update_problem(&mut self, term: Step) -> Result<bool, P, M> {
let updates = {
let state = UpdateState {
minorants: &self.minorants,
step: term,
iteration_info: &self.iterinfos,
// this is a dirty trick: when updating the center, we
// simply swapped the `cur_*` fields with the `nxt_*`
|
| ︙ | ︙ | |||
755 756 757 758 759 760 761 |
/**
* Initializes the master problem.
*
* The oracle is evaluated once at the initial center and the
* master problem is initialized with the returned subgradient
* information.
*/
| | | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
/**
* Initializes the master problem.
*
* The oracle is evaluated once at the initial center and the
* master problem is initialized with the returned subgradient
* information.
*/
fn init_master(&mut self) -> Result<(), P, M> {
let m = self.problem.num_subproblems();
let lb = self.problem.lower_bounds().map(DVector);
let ub = self.problem.upper_bounds().map(DVector);
if lb
.as_ref()
|
| ︙ | ︙ | |||
829 830 831 832 833 834 835 |
debug!("Init master completed");
Ok(())
}
/// Solve the model (i.e. master problem) to compute the next candidate.
| | | 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 |
debug!("Init master completed");
Ok(())
}
/// Solve the model (i.e. master problem) to compute the next candidate.
fn solve_model(&mut self) -> Result<(), P, M> {
self.master.solve(self.cur_val)?;
self.nxt_d = self.master.get_primopt();
self.nxt_y.add(&self.cur_y, &self.nxt_d);
self.nxt_mod = self.master.get_primoptval();
self.sgnorm = self.master.get_dualoptnorm2().sqrt();
self.expected_progress = self.cur_val - self.nxt_mod;
|
| ︙ | ︙ | |||
852 853 854 855 856 857 858 |
debug!(" cur_val ={}", self.cur_val);
debug!(" nxt_mod ={}", self.nxt_mod);
debug!(" expected={}", self.expected_progress);
Ok(())
}
/// Reduce size of bundle.
| | | 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 |
debug!(" cur_val ={}", self.cur_val);
debug!(" nxt_mod ={}", self.nxt_mod);
debug!(" expected={}", self.expected_progress);
Ok(())
}
/// Reduce size of bundle.
fn compress_bundle(&mut self) -> Result<(), P, M> {
for i in 0..self.problem.num_subproblems() {
let n = self.master.num_minorants(i);
if n >= self.params.max_bundle_size {
// aggregate minorants with smallest coefficients
self.minorants[i].sort_by_key(|m| -((1e6 * m.multiplier) as isize));
let aggr = self.minorants[i].split_off(self.params.max_bundle_size - 2);
let aggr_sum = aggr.iter().map(|m| m.multiplier).sum();
|
| ︙ | ︙ | |||
875 876 877 878 879 880 881 |
});
}
}
Ok(())
}
/// Perform a descent step.
| | | | | 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 |
});
}
}
Ok(())
}
/// Perform a descent step.
fn descent_step(&mut self) -> Result<(), P, M> {
let new_weight = self.weighter.descent_weight(¤t_state!(self, Step::Descent));
self.master.set_weight(new_weight)?;
self.cnt_descent += 1;
swap(&mut self.cur_y, &mut self.nxt_y);
swap(&mut self.cur_val, &mut self.nxt_val);
swap(&mut self.cur_mod, &mut self.nxt_mod);
swap(&mut self.cur_vals, &mut self.nxt_vals);
swap(&mut self.cur_mods, &mut self.nxt_mods);
self.master.move_center(1.0, &self.nxt_d);
debug!("Descent Step");
debug!(" dir ={}", self.nxt_d);
debug!(" newy={}", self.cur_y);
Ok(())
}
/// Perform a null step.
fn null_step(&mut self) -> Result<(), P, M> {
let new_weight = self.weighter.null_weight(¤t_state!(self, Step::Null));
self.master.set_weight(new_weight)?;
self.cnt_null += 1;
debug!("Null Step");
Ok(())
}
/// Perform one bundle iteration.
#[allow(clippy::collapsible_if)]
pub fn step(&mut self) -> Result<Step, P, M> {
self.iterinfos.clear();
if !self.cur_valid {
// current point needs new evaluation
self.init_master()?;
}
|
| ︙ | ︙ |