RsBundle  Check-in [6186a4f7ed]

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

Overview
Comment:solver: make master problem a type argument
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | async
Files: files | file ages | folders
SHA1: 6186a4f7ed4ececcda0e227216837007f51d1994
User & Date: fifr 2019-07-17 14:41:42.505
Context
2019-07-17
15:38
Simplify error handling (again) by using boxed errors check-in: b6b5c1ec21 user: fifr tags: async
14:41
solver: make master problem a type argument check-in: 6186a4f7ed user: fifr tags: async
14:14
parallel: initialize solver check-in: 4abcab25e2 user: fifr tags: async
Changes
Unified Diff Ignore Whitespace Patch
Changes to examples/cflp.rs.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

use env_logger;
use ordered_float::NotNan;
use threadpool::ThreadPool;

use bundle::parallel::{EvalResult, FirstOrderProblem as ParallelProblem, ParallelSolver, ResultSender};
use bundle::{dvec, DVector, Minorant, Real};
use bundle::{FirstOrderProblem, SimpleEvaluation, Solver, StandardTerminator};

const Nfac: usize = 3;
const Ncus: usize = 5;
const F: [Real; Nfac] = [1000.0, 1000.0, 1000.0];
const CAP: [Real; Nfac] = [500.0, 500.0, 500.0];
const C: [[Real; Ncus]; Nfac] = [
    [4.0, 5.0, 6.0, 8.0, 10.0], //







|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

use env_logger;
use ordered_float::NotNan;
use threadpool::ThreadPool;

use bundle::parallel::{EvalResult, FirstOrderProblem as ParallelProblem, ParallelSolver, ResultSender};
use bundle::{dvec, DVector, Minorant, Real};
use bundle::{DefaultSolver, FirstOrderProblem, SimpleEvaluation, Solver, StandardTerminator};

const Nfac: usize = 3;
const Ncus: usize = 5;
const F: [Real; Nfac] = [1000.0, 1000.0, 1000.0];
const CAP: [Real; Nfac] = [500.0, 500.0, 500.0];
const C: [[Real; Ncus]; Nfac] = [
    [4.0, 5.0, 6.0, 8.0, 10.0], //
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    }
}

fn main() -> Result<(), Box<Error>> {
    env_logger::init();

    {
        let mut slv = Solver::new(CFLProblem::new())?;
        slv.terminator = Box::new(StandardTerminator {
            termination_precision: 1e-9,
        });
        slv.solve()?;

        for i in 0..Ncus {
            let x = slv.aggregated_primals(Nfac + i);







|







232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    }
}

fn main() -> Result<(), Box<Error>> {
    env_logger::init();

    {
        let mut slv = DefaultSolver::new(CFLProblem::new())?;
        slv.terminator = Box::new(StandardTerminator {
            termination_precision: 1e-9,
        });
        slv.solve()?;

        for i in 0..Ncus {
            let x = slv.aggregated_primals(Nfac + i);
Changes to examples/mmcf.rs.
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
 */

use bundle;
use env_logger;
use log::info;

use bundle::mcf;
use bundle::{FirstOrderProblem, Solver, SolverParams, StandardTerminator};

use std::env;

fn main() {
    env_logger::init();

    let mut args = env::args();
    let program = args.next().unwrap();

    if let Some(filename) = args.next() {
        info!("Reading instance: {}", filename);
        let mut mmcf = mcf::MMCFProblem::read_mnetgen(&filename).unwrap();
        mmcf.multimodel = false;

        let mut solver = Solver::new_params(
            mmcf,
            SolverParams {
                max_bundle_size: 25,
                min_weight: 1e-3,
                max_weight: 100.0,
                ..Default::default()
            },







|














|







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

use bundle;
use env_logger;
use log::info;

use bundle::mcf;
use bundle::{DefaultSolver, FirstOrderProblem, Solver, SolverParams, StandardTerminator};

use std::env;

fn main() {
    env_logger::init();

    let mut args = env::args();
    let program = args.next().unwrap();

    if let Some(filename) = args.next() {
        info!("Reading instance: {}", filename);
        let mut mmcf = mcf::MMCFProblem::read_mnetgen(&filename).unwrap();
        mmcf.multimodel = false;

        let mut solver = DefaultSolver::new_params(
            mmcf,
            SolverParams {
                max_bundle_size: 25,
                min_weight: 1e-3,
                max_weight: 100.0,
                ..Default::default()
            },
Changes to examples/quadratic.rs.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

use std::error::Error;

use bundle::{self, dvec};
use env_logger;
use log::debug;

use bundle::{DVector, FirstOrderProblem, Minorant, Real, SimpleEvaluation, Solver, SolverParams};

struct QuadraticProblem {
    a: [[Real; 2]; 2],
    b: [Real; 2],
    c: Real,
}








|







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

use std::error::Error;

use bundle::{self, dvec};
use env_logger;
use log::debug;

use bundle::{DVector, DefaultSolver, FirstOrderProblem, Minorant, Real, SimpleEvaluation, Solver, SolverParams};

struct QuadraticProblem {
    a: [[Real; 2]; 2],
    b: [Real; 2],
    c: Real,
}

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    }
}

fn main() {
    env_logger::init();

    let f = QuadraticProblem::new();
    let mut solver = Solver::new_params(
        f,
        SolverParams {
            min_weight: 1.0,
            max_weight: 1.0,
            ..Default::default()
        },
    )
    .unwrap();
    solver.solve().unwrap();
}







|










83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    }
}

fn main() {
    env_logger::init();

    let f = QuadraticProblem::new();
    let mut solver = DefaultSolver::new_params(
        f,
        SolverParams {
            min_weight: 1.0,
            max_weight: 1.0,
            ..Default::default()
        },
    )
    .unwrap();
    solver.solve().unwrap();
}
Changes to src/lib.rs.
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
pub use crate::minorant::{Aggregatable, Minorant};

pub mod firstorderproblem;
pub use crate::firstorderproblem::{Evaluation, FirstOrderProblem, SimpleEvaluation, Update};

pub mod solver;
pub use crate::solver::{
    BundleState, IterationInfo, Solver, SolverParams, StandardTerminator, Step, Terminator, UpdateState, Weighter,

};

pub mod parallel;

mod hkweighter;
pub use crate::hkweighter::HKWeighter;








|
>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
pub use crate::minorant::{Aggregatable, Minorant};

pub mod firstorderproblem;
pub use crate::firstorderproblem::{Evaluation, FirstOrderProblem, SimpleEvaluation, Update};

pub mod solver;
pub use crate::solver::{
    BundleState, DefaultSolver, IterationInfo, Solver, SolverParams, StandardTerminator, Step, Terminator, UpdateState,
    Weighter,
};

pub mod parallel;

mod hkweighter;
pub use crate::hkweighter::HKWeighter;

Changes to src/master/base.rs.
70
71
72
73
74
75
76





77
78
79
80
81
82
83

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

    /// Error returned by the subgradient-extension callback.
    type SubgradientExtensionErr;






    /// Set the number of subproblems.
    fn set_num_subproblems(&mut self, n: usize) -> Result<(), Self::SubgradientExtensionErr>;

    /// Set the lower and upper bounds of the variables.
    fn set_vars(
        &mut self,







>
>
>
>
>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

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

    /// Error returned by the subgradient-extension callback.
    type SubgradientExtensionErr;

    /// Create a new master problem.
    fn new() -> Result<Self, Self::SubgradientExtensionErr>
    where
        Self: Sized;

    /// Set the number of subproblems.
    fn set_num_subproblems(&mut self, n: usize) -> Result<(), Self::SubgradientExtensionErr>;

    /// Set the lower and upper bounds of the variables.
    fn set_vars(
        &mut self,
Changes to src/master/boxed.rs.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * 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.
 */
pub struct BoxedMasterProblem<M: UnconstrainedMasterProblem> {
    lb: DVector,
    ub: DVector,
    eta: DVector,

    /// Primal optimal solution.
    primopt: DVector,








|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * 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.
 */
pub struct BoxedMasterProblem<M> {
    lb: DVector,
    ub: DVector,
    eta: DVector,

    /// Primal optimal solution.
    primopt: DVector,

56
57
58
59
60
61
62


63

64
65
66
67
68
69
70
71
    /// Current number of updates.
    cnt_updates: usize,

    /// The unconstrained master problem solver.
    master: M,
}



impl<M: UnconstrainedMasterProblem> BoxedMasterProblem<M> {

    pub fn new(master: M) -> BoxedMasterProblem<M> {
        BoxedMasterProblem {
            lb: dvec![],
            ub: dvec![],
            eta: dvec![],
            primopt: dvec![],
            primoptval: 0.0,
            dualoptnorm2: 0.0,







>
>
|
>
|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    /// Current number of updates.
    cnt_updates: usize,

    /// The unconstrained master problem solver.
    master: M,
}

impl<M> BoxedMasterProblem<M>
where
    M: UnconstrainedMasterProblem,
{
    pub fn with_master(master: M) -> BoxedMasterProblem<M> {
        BoxedMasterProblem {
            lb: dvec![],
            ub: dvec![],
            eta: dvec![],
            primopt: dvec![],
            primoptval: 0.0,
            dualoptnorm2: 0.0,
172
173
174
175
176
177
178
179



180
181
182




183
184
185
186
187
188
189
     * the current box-multipliers $\eta$.
     */
    fn get_norm_subg2(&self) -> Real {
        self.eta.dot(self.master.dualopt())
    }
}

impl<M: UnconstrainedMasterProblem> MasterProblem for BoxedMasterProblem<M> {



    type MinorantIndex = M::MinorantIndex;

    type SubgradientExtensionErr = M::SubgradientExtensionErr;





    fn set_num_subproblems(&mut self, n: usize) -> Result<(), Self::SubgradientExtensionErr> {
        self.master.set_num_subproblems(n)
    }

    fn set_vars(
        &mut self,







|
>
>
>



>
>
>
>







175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
     * the current box-multipliers $\eta$.
     */
    fn get_norm_subg2(&self) -> Real {
        self.eta.dot(self.master.dualopt())
    }
}

impl<M> MasterProblem for BoxedMasterProblem<M>
where
    M: UnconstrainedMasterProblem,
{
    type MinorantIndex = M::MinorantIndex;

    type SubgradientExtensionErr = M::SubgradientExtensionErr;

    fn new() -> Result<Self, Self::SubgradientExtensionErr> {
        M::new().map(BoxedMasterProblem::with_master)
    }

    fn set_num_subproblems(&mut self, n: usize) -> Result<(), Self::SubgradientExtensionErr> {
        self.master.set_num_subproblems(n)
    }

    fn set_vars(
        &mut self,
Changes to src/parallel/solver.rs.
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
        Ok(())
    }

    fn master_main(
        tx: &mut MasterSender<P::Err>,
        rx: MasterReceiver,
    ) -> std::result::Result<(), MasterProblemError<P::Err>> {
        let mut master = CplexMaster::new().map(BoxedMasterProblem::new)?;
        for m in rx {
            match m {
                MasterTask::AddMinorant(i, m) => {
                    master.add_minorant(i, m)?;
                }
                MasterTask::MoveCenter(alpha, d) => {
                    master.move_center(alpha, &d);







|







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
        Ok(())
    }

    fn master_main(
        tx: &mut MasterSender<P::Err>,
        rx: MasterReceiver,
    ) -> std::result::Result<(), MasterProblemError<P::Err>> {
        let mut master = CplexMaster::new().map(BoxedMasterProblem::with_master)?;
        for m in rx {
            match m {
                MasterTask::AddMinorant(i, m) => {
                    master.add_minorant(i, m)?;
                }
                MasterTask::MoveCenter(alpha, d) => {
                    master.move_center(alpha, &d);
Changes to src/solver.rs.
15
16
17
18
19
20
21
22
23


24
25
26
27
28
29
30
//

//! The main bundle method solver.

use crate::{Aggregatable, DVector, Real};
use crate::{Evaluation, FirstOrderProblem, HKWeighter, Update};

use crate::master::{BoxedMasterProblem, Error as MasterProblemError, MasterProblem, UnconstrainedMasterProblem};
use crate::master::{CplexMaster, MinimalMaster};



use log::{debug, info, warn};

use std::error::Error;
use std::f64::{INFINITY, NEG_INFINITY};
use std::fmt;
use std::mem::swap;







|
|
>
>







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

//! The main bundle method solver.

use crate::{Aggregatable, DVector, Real};
use crate::{Evaluation, FirstOrderProblem, HKWeighter, Update};

use crate::master::CplexMaster;
use crate::master::{
    BoxedMasterProblem, Error as MasterProblemError, MasterProblem, MinimalMaster, UnconstrainedMasterProblem,
};

use log::{debug, info, warn};

use std::error::Error;
use std::f64::{INFINITY, NEG_INFINITY};
use std::fmt;
use std::mem::swap;
379
380
381
382
383
384
385






386
387
388


389

390
391
392
393
394
395
396
    ///
    /// 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())
    }
}







/**
 * Implementation of a bundle method.
 */


pub struct Solver<P: FirstOrderProblem> {

    /// The first order problem description.
    problem: P,

    /// The solver parameter.
    pub params: SolverParams,

    /// Termination predicate.







>
>
>
>
>
>



>
>
|
>







381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
    ///
    /// 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.
pub type DefaultSolver<P> = Solver<P, BoxedMasterProblem<CplexMaster<<P as FirstOrderProblem>::Err>>>;

/// A bundle solver with a minimal cutting plane model.
pub type NoBundleSolver<P> = Solver<P, BoxedMasterProblem<MinimalMaster<<P as FirstOrderProblem>::Err>>>;

/**
 * Implementation of a bundle method.
 */
pub struct Solver<P, M = BoxedMasterProblem<CplexMaster<<P as FirstOrderProblem>::Err>>>
where
    P: FirstOrderProblem,
{
    /// The first order problem description.
    problem: P,

    /// The solver parameter.
    pub params: SolverParams,

    /// Termination predicate.
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
493
494
495
496
497
     * 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: Box<MasterProblem<MinorantIndex = usize, SubgradientExtensionErr = P::Err>>,

    /// The active minorant indices for each subproblem.
    minorants: Vec<Vec<MinorantInfo<P::Primal>>>,

    /// Accumulated information about the last iteration.
    iterinfos: Vec<IterationInfo>,
}

impl<P: FirstOrderProblem> Solver<P>
where

    P::Err: Into<Box<dyn 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()`.
     */
    pub fn new_params(problem: P, params: SolverParams) -> Result<Solver<P>, SolverError<P::Err>> {

        Ok(Solver {
            problem,
            params,
            terminator: Box::new(StandardTerminator {
                termination_precision: 1e-3,
            }),
            weighter: Box::new(HKWeighter::new()),







|








|

>

>









|
>







473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
     * 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,

    /// The active minorant indices for each subproblem.
    minorants: Vec<Vec<MinorantInfo<P::Primal>>>,

    /// Accumulated information about the last iteration.
    iterinfos: Vec<IterationInfo>,
}

impl<P, M> Solver<P, M>
where
    P: FirstOrderProblem,
    P::Err: Into<Box<dyn Error + Send + Sync>> + 'static,
    M: MasterProblem<MinorantIndex = usize, SubgradientExtensionErr = P::Err>,
{
    /**
     * 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()`.
     */
    pub fn new_params(problem: P, params: SolverParams) -> Result<Solver<P, M>, SolverError<P::Err>> {
        let master: M = M::new()?;
        Ok(Solver {
            problem,
            params,
            terminator: Box::new(StandardTerminator {
                termination_precision: 1e-3,
            }),
            weighter: Box::new(HKWeighter::new()),
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
            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: Box::new(BoxedMasterProblem::new(MinimalMaster::new()?)),
            minorants: vec![],
            iterinfos: vec![],
        })
    }

    /// A new solver with default parameter.
    pub fn new(problem: P) -> Result<Solver<P>, SolverError<P::Err>> {
        Solver::new_params(problem, SolverParams::default())
    }

    /**
     * Set the first order problem description associated with this
     * solver.
     *







|






|







524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
            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: master,
            minorants: vec![],
            iterinfos: vec![],
        })
    }

    /// A new solver with default parameter.
    pub fn new(problem: P) -> Result<Solver<P, M>, SolverError<P::Err>> {
        Solver::new_params(problem, SolverParams::default())
    }

    /**
     * Set the first order problem description associated with this
     * solver.
     *
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
     * 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<(), SolverError<P::Err>> {
        let m = self.problem.num_subproblems();

        self.master = if m == 1 && self.params.max_bundle_size == 2 {
            debug!("Use minimal master problem");
            Box::new(BoxedMasterProblem::new(MinimalMaster::new()?))
        } else {
            debug!("Use CPLEX master problem");
            Box::new(BoxedMasterProblem::new(CplexMaster::new()?))
        };

        let lb = self.problem.lower_bounds().map(DVector);
        let ub = self.problem.upper_bounds().map(DVector);

        if lb
            .as_ref()
            .map(|lb| lb.len() != self.problem.num_variables())
            .unwrap_or(false)







<
<
<
<
<
<
<
<







793
794
795
796
797
798
799








800
801
802
803
804
805
806
     * 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<(), SolverError<P::Err>> {
        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()
            .map(|lb| lb.len() != self.problem.num_variables())
            .unwrap_or(false)