RsBundle  Check-in [b7b6413301]

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

Overview
Comment:Move `SubModel` to submodule `guessmodels::GuessModel`
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | garbage
Files: files | file ages | folders
SHA1: b7b6413301594be00e82cae55542afe2b1b7834b
User & Date: fifr 2020-06-11 18:35:26.941
Context
2020-06-11
18:35
Move `SubModel` to submodule `guessmodels::GuessModel` Closed-Leaf check-in: b7b6413301 user: fifr tags: garbage
11:55
asyn: add some constructors for `Guess` check-in: 32006728e6 user: fifr tags: async-simplify
Changes
Unified Diff Ignore Whitespace Patch
Changes to Cargo.toml.
16
17
18
19
20
21
22

23
24
25
26
27
28
29
c_str_macro = "^1.0"
cplex-sys = "^0.6"
float-pretty-print = "^0.1"
rs-crossbeam = { version = "^0.7", optional = true, package = "crossbeam" }
threadpool = "^1.7"
num_cpus = "^1.2"
num-traits = "^0.2.8"

rs-blas = { version = "^0.20.0", optional = true, package = "blas" }
openblas-src = { version = "^0.9", optional = true, features = ["system"], default-features = false }
assert_float_eq = "^1.0"

[dev-dependencies]
env_logger = "^0.7"
ordered-float = "^1.0"







>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
c_str_macro = "^1.0"
cplex-sys = "^0.6"
float-pretty-print = "^0.1"
rs-crossbeam = { version = "^0.7", optional = true, package = "crossbeam" }
threadpool = "^1.7"
num_cpus = "^1.2"
num-traits = "^0.2.8"
rand = "^0.7.3"
rs-blas = { version = "^0.20.0", optional = true, package = "blas" }
openblas-src = { version = "^0.9", optional = true, features = ["system"], default-features = false }
assert_float_eq = "^1.0"

[dev-dependencies]
env_logger = "^0.7"
ordered-float = "^1.0"
Changes to src/mcf/problem.rs.
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32


33
34
35
36
37
38
39
use crate::problem::{
    FirstOrderProblem as ParallelProblem, ResultSender, UpdateSender, UpdateState as ParallelUpdateState,
};
use crate::{DVector, Minorant, Real};

use log::{debug, warn};
use num_traits::Float;

use threadpool::ThreadPool;

use std::f64::INFINITY;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::result;
use std::sync::{Arc, RwLock};



/// An error in the mmcf file format.
#[derive(Debug)]
#[non_exhaustive]
pub enum MMCFReadError {
    Format(String),
    MissingNodField(&'static str),







>








>
>







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
use crate::problem::{
    FirstOrderProblem as ParallelProblem, ResultSender, UpdateSender, UpdateState as ParallelUpdateState,
};
use crate::{DVector, Minorant, Real};

use log::{debug, warn};
use num_traits::Float;
use rand::{rngs::ThreadRng, thread_rng, Rng};
use threadpool::ThreadPool;

use std::f64::INFINITY;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::result;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::Duration;

/// An error in the mmcf file format.
#[derive(Debug)]
#[non_exhaustive]
pub enum MMCFReadError {
    Format(String),
    MissingNodField(&'static str),
127
128
129
130
131
132
133

134
135
136
137
138
139


140
141
142
143
144
145
146
struct Elem {
    ind: usize,
    val: Real,
}

/// A single MMCF subproblem, i.e. one network.
struct Subproblem {

    net: mcf::Solver,
    lhs: Vec<Vec<Elem>>,
    /// The right-hand side ... might be empty.
    rhs: DVector,
    cbase: DVector,
    c: DVector,


}

unsafe impl Send for Subproblem {}
unsafe impl Sync for Subproblem {}

pub struct MMCFProblem {
    pub multimodel: bool,







>






>
>







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
struct Elem {
    ind: usize,
    val: Real,
}

/// A single MMCF subproblem, i.e. one network.
struct Subproblem {
    fidx: usize,
    net: mcf::Solver,
    lhs: Vec<Vec<Elem>>,
    /// The right-hand side ... might be empty.
    rhs: DVector,
    cbase: DVector,
    c: DVector,

    rng: ThreadRng,
}

unsafe impl Send for Subproblem {}
unsafe impl Sync for Subproblem {}

pub struct MMCFProblem {
    pub multimodel: bool,
188
189
190
191
192
193
194





195
196
197
198
199
200
201
            (-self.net.objective()?, dvec![0.0; y.len()])
        };

        let sol = self.net.get_solution()?;
        for (i, lhs) in lhs.enumerate() {
            subg[i] -= lhs.iter().map(|elem| elem.val * sol[elem.ind]).sum::<Real>();
        }






        Ok((objective, subg, sol))
    }

    fn evaluate_constraint(&self, primal: &DVector, cidx: usize) -> Real {
        let rhs = self.rhs.get(cidx).unwrap_or(&0.0);
        let lhs = self.lhs[cidx]







>
>
>
>
>







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
            (-self.net.objective()?, dvec![0.0; y.len()])
        };

        let sol = self.net.get_solution()?;
        for (i, lhs) in lhs.enumerate() {
            subg[i] -= lhs.iter().map(|elem| elem.val * sol[elem.ind]).sum::<Real>();
        }

        if self.fidx == 1 || self.fidx == 5 || self.fidx == 7 || self.fidx == 10 {
            sleep(Duration::from_millis(self.rng.gen_range(10, 100)));
            //sleep(Duration::from_millis(10));
        }

        Ok((objective, subg, sol))
    }

    fn evaluate_constraint(&self, primal: &DVector, cidx: usize) -> Real {
        let rhs = self.rhs.get(cidx).unwrap_or(&0.0);
        let lhs = self.lhs[cidx]
345
346
347
348
349
350
351

352

353
354
355
356
357

358
359
360
361
362
363
364
            .collect::<Vec<_>>();

        let subproblems = nets
            .into_iter()
            .zip(cbase)
            .zip(lhs)
            .zip(std::iter::once(rhs).chain(std::iter::repeat(dvec![])))

            .map(|(((net, cbase), lhs), rhs)| Subproblem {

                net,
                cbase,
                c: dvec![],
                lhs,
                rhs,

            })
            .map(RwLock::new)
            .map(Arc::new)
            .collect();

        Ok(MMCFProblem {
            multimodel: false,







>
|
>





>







356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
            .collect::<Vec<_>>();

        let subproblems = nets
            .into_iter()
            .zip(cbase)
            .zip(lhs)
            .zip(std::iter::once(rhs).chain(std::iter::repeat(dvec![])))
            .enumerate()
            .map(|(fidx, (((net, cbase), lhs), rhs))| Subproblem {
                fidx,
                net,
                cbase,
                c: dvec![],
                lhs,
                rhs,
                rng: thread_rng(),
            })
            .map(RwLock::new)
            .map(Arc::new)
            .collect();

        Ok(MMCFProblem {
            multimodel: false,
Changes to src/solver/asyn.rs.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
use super::masterprocess::{self, MasterConfig, MasterProcess, MasterResponse, Response};
use crate::data::Minorant;
use crate::master::{Builder as MasterBuilder, MasterProblem};
use crate::problem::{FirstOrderProblem, UpdateState};
use crate::terminator::{StandardTerminatable, StandardTerminator, Terminator};
use crate::weighter::{HKWeightable, HKWeighter, Weighter};

mod subzero;
use subzero::SubZero;

/// The default iteration limit.
pub const DEFAULT_ITERATION_LIMIT: usize = 10_000;

/// The default solver.
pub type DefaultSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::FullMasterBuilder>;








|
|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
use super::masterprocess::{self, MasterConfig, MasterProcess, MasterResponse, Response};
use crate::data::Minorant;
use crate::master::{Builder as MasterBuilder, MasterProblem};
use crate::problem::{FirstOrderProblem, UpdateState};
use crate::terminator::{StandardTerminatable, StandardTerminator, Terminator};
use crate::weighter::{HKWeightable, HKWeighter, Weighter};

pub mod guessmodels;
use guessmodels::{Guess, GuessModel, NearestValue};

/// The default iteration limit.
pub const DEFAULT_ITERATION_LIMIT: usize = 10_000;

/// The default solver.
pub type DefaultSolver<P> = Solver<P, StandardTerminator, HKWeighter, crate::master::FullMasterBuilder>;

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
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
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
    fn aggregated_primal(&self, i: usize) -> &Pr {
        &self.primal_aggrs[i]
    }
}

/// An evaluation point.
#[derive(Clone)]
struct Point {
    /// The globally unique index of the evaluation point.
    index: usize,

    /// The evaluation point itself.
    point: Arc<DVector>,
}

impl Point {
    fn distance(&self, p: &Point) -> Real {
        if self.index != p.index {
            let mut d = self.point.as_ref().clone();
            d.add_scaled(-1.0, &p.point);
            d.norm2()
        } else {
            Real::zero()
        }
    }
}

/// A guessed function value.
#[derive(Clone, Copy)]
pub struct Guess {
    /// The guessed value.
    value: Real,
    /// The accuracy distance.
    dist: Real,
}

impl Guess {
    /// Create a new guess value.
    ///
    /// If `dist` is zero the value is assumed to be exact.
    pub fn new(value: Real, dist: Real) -> Guess {
        Guess { value, dist }
    }

    /// Create an approximate guess value.
    pub fn new_approx(value: Real, dist: Real) -> Guess {
        Guess { value, dist }
    }

    /// Create an exact guess value.
    pub fn new_exact(value: Real) -> Guess {
        Guess {
            value,
            dist: Real::zero(),
        }
    }

    /// Return `true` if this is an exact guess value.
    ///
    /// In other words, the value is not `guessed` anymore.
    pub fn is_exact(&self) -> bool {
        self.dist.is_zero()
    }
}

impl Default for Guess {
    fn default() -> Guess {
        Guess {
            value: Real::zero(),
            dist: Real::infinity(),
        }
    }
}

/// A subproblem model for guessing candidate and center values.
trait SubModel {
    /// Set the center of this model.
    fn set_center(&mut self, y: &Point);

    /// Set the candidate of this model.
    fn set_candidate(&mut self, y: &Point);

    /// Add a function value to this model.
    fn add_function_value(&mut self, y: &Point, value: Real);

    /// Add a minorant to this model.
    ///
    /// The minorant must be centered at the global 0.
    fn add_minorant(&mut self, y: &Point, m: &Arc<Minorant>);

    /// Return the current guess value at the current candidate.
    ///
    /// The function might return `None` if no guess value is available.
    fn candidate_value(&self) -> Option<Guess>;

    /// Return the current lower bound value at the center.
    ///
    /// In case no lower bound is available, the function should return
    /// -infinity.
    fn center_lb(&self) -> Real;
}

/// Model data of a single subproblem.
///
/// This struct does not handle the subproblem model itself. However, it handles
/// the asynchronous precision data, i.e. the guessed Lipschitz-constant and the
/// distance of the evaluation points to the candidate.
///
/// The concrete model used for computing the guessed values in the candidate
/// and the center must be provided by an implementation of `SubProblem`.
struct SubData {
    /// The index associated with this subproblem.
    fidx: usize,
    /// The subproblem.
    sub: Box<dyn SubModel>,
    /// The current candidate index.
    candidate_index: usize,
    /// The last index at which the evaluation has been started.
    last_eval_index: usize,
    /// Whether a subproblem evaluation is currently running.
    is_running: bool,
    /// Whether the last evaluation has been sufficiently close.
    is_close_enough: bool,
    /// The guess value and its evaluation distance in the current center.
    center_guess: Guess,
    /// The current guess of the Lipschitz constant.
    l_guess: Real,
}

impl SubData {
    fn new(fidx: usize, sub: Box<dyn SubModel>) -> SubData {
        SubData {
            fidx,
            sub,
            last_eval_index: 0,
            candidate_index: 0,
            is_running: false,
            is_close_enough: false,







|



















<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












|















|







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
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
    fn aggregated_primal(&self, i: usize) -> &Pr {
        &self.primal_aggrs[i]
    }
}

/// An evaluation point.
#[derive(Clone)]
pub struct Point {
    /// The globally unique index of the evaluation point.
    index: usize,

    /// The evaluation point itself.
    point: Arc<DVector>,
}

impl Point {
    fn distance(&self, p: &Point) -> Real {
        if self.index != p.index {
            let mut d = self.point.as_ref().clone();
            d.add_scaled(-1.0, &p.point);
            d.norm2()
        } else {
            Real::zero()
        }
    }
}












































































/// Model data of a single subproblem.
///
/// This struct does not handle the subproblem model itself. However, it handles
/// the asynchronous precision data, i.e. the guessed Lipschitz-constant and the
/// distance of the evaluation points to the candidate.
///
/// The concrete model used for computing the guessed values in the candidate
/// and the center must be provided by an implementation of `SubProblem`.
struct SubData {
    /// The index associated with this subproblem.
    fidx: usize,
    /// The subproblem.
    sub: Box<dyn GuessModel>,
    /// The current candidate index.
    candidate_index: usize,
    /// The last index at which the evaluation has been started.
    last_eval_index: usize,
    /// Whether a subproblem evaluation is currently running.
    is_running: bool,
    /// Whether the last evaluation has been sufficiently close.
    is_close_enough: bool,
    /// The guess value and its evaluation distance in the current center.
    center_guess: Guess,
    /// The current guess of the Lipschitz constant.
    l_guess: Real,
}

impl SubData {
    fn new(fidx: usize, sub: Box<dyn GuessModel>) -> SubData {
        SubData {
            fidx,
            sub,
            last_eval_index: 0,
            candidate_index: 0,
            is_running: false,
            is_close_enough: false,
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
            tx,
            &mut self.threadpool,
        ));

        debug!("Initial problem evaluation");
        // We need an initial evaluation of all oracles for the first center.
        self.data.subs = (0..m)
            .map(|fidx| SubData::new(fidx, Box::new(SubZero::new())))
            .collect();
        self.data.nxt_y = self.data.cur_y.clone();

        // The initial evaluation point.
        let point = Point {
            index: self.data.candidate_index,
            point: self.data.cur_y.clone(),







|







787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
            tx,
            &mut self.threadpool,
        ));

        debug!("Initial problem evaluation");
        // We need an initial evaluation of all oracles for the first center.
        self.data.subs = (0..m)
            .map(|fidx| SubData::new(fidx, Box::new(NearestValue::new())))
            .collect();
        self.data.nxt_y = self.data.cur_y.clone();

        // The initial evaluation point.
        let point = Point {
            index: self.data.candidate_index,
            point: self.data.cur_y.clone(),
Added src/solver/asyn/guessmodels.rs.








































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
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
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
/*
 * Copyright (c) 2020 Frank Fischer <frank-fischer@shadow-soft.de>
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * 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 super::Point;
use crate::{data::Minorant, Real};

use num_traits::{Float, Zero};
use std::sync::Arc;

mod nearestvalue;
pub use nearestvalue::NearestValue;

/// A guessed function value.
#[derive(Clone, Copy)]
pub struct Guess {
    /// The guessed value.
    pub value: Real,
    /// The accuracy distance.
    pub dist: Real,
}

impl Guess {
    /// Create a new guess value.
    ///
    /// If `dist` is zero the value is assumed to be exact.
    pub fn new(value: Real, dist: Real) -> Guess {
        Guess { value, dist }
    }

    /// Create an approximate guess value.
    pub fn new_approx(value: Real, dist: Real) -> Guess {
        Guess { value, dist }
    }

    /// Create an exact guess value.
    pub fn new_exact(value: Real) -> Guess {
        Guess {
            value,
            dist: Real::zero(),
        }
    }

    /// Return `true` if this is an exact guess value.
    ///
    /// In other words, the value is not `guessed` anymore.
    pub fn is_exact(&self) -> bool {
        self.dist.is_zero()
    }
}

impl Default for Guess {
    fn default() -> Guess {
        Guess {
            value: Real::zero(),
            dist: Real::infinity(),
        }
    }
}

/// A subproblem model for guessing candidate and center values.
pub trait GuessModel {
    /// Set the center of this model.
    fn set_center(&mut self, y: &Point);

    /// Set the candidate of this model.
    fn set_candidate(&mut self, y: &Point);

    /// Add a function value to this model.
    fn add_function_value(&mut self, y: &Point, value: Real);

    /// Add a minorant to this model.
    ///
    /// The minorant must be centered at the global 0.
    fn add_minorant(&mut self, y: &Point, m: &Arc<Minorant>);

    /// Return the current guess value at the current candidate.
    ///
    /// The function might return `None` if no guess value is available.
    fn candidate_value(&self) -> Option<Guess>;

    /// Return the current lower bound value at the center.
    ///
    /// In case no lower bound is available, the function should return
    /// -infinity.
    fn center_lb(&self) -> Real;
}
Name change from src/solver/asyn/subzero.rs to src/solver/asyn/guessmodels/nearestvalue.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
68
69
70
71
72
73
74
75
76
77
78
79
use num_traits::{Float, Zero};
use std::collections::VecDeque;
use std::sync::Arc;

use crate::data::Minorant;
use crate::Real;

use super::{Guess, Point, SubModel};

/// The maximal number of last evaluation points to be kept in the model.
#[allow(non_upper_case_globals)]
const MaxEvalPoints: usize = 5;
/// The maximal number of last minorants to be kept in the model.
#[allow(non_upper_case_globals)]
const MaxMinorants: usize = 5;

/// Information associated with one subproblem.
pub struct SubZero {
    /// The current center point and the known lower bound.
    center: Option<(Point, Real)>,

    /// The current candidate point.
    candidate: Option<Point>,

    /// The last evaluation points (y, function_value).
    eval_points: VecDeque<(Point, Real)>,

    /// The last minorants.
    minorants: VecDeque<Arc<Minorant>>,

    /// The candidate guess value and distance.
    candidate_value: Option<Guess>,
}

impl SubZero {
    pub fn new() -> SubZero {
        SubZero {
            center: None,
            candidate: None,
            eval_points: VecDeque::new(),
            minorants: VecDeque::new(),
            candidate_value: None,
        }
    }
}

impl Default for SubZero {
    fn default() -> SubZero {
        SubZero::new()
    }
}

impl SubModel for SubZero {
    fn set_center(&mut self, y: &Point) {
        // compute the best lower bound for the new center.
        let lb = self
            .minorants
            .iter()
            .map(|m| m.eval(&y.point))
            .max_by(|a, b| a.partial_cmp(b).unwrap())







|









|
















|
|
|









|
|
|



|







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
use num_traits::{Float, Zero};
use std::collections::VecDeque;
use std::sync::Arc;

use crate::data::Minorant;
use crate::Real;

use super::{Guess, GuessModel, Point};

/// The maximal number of last evaluation points to be kept in the model.
#[allow(non_upper_case_globals)]
const MaxEvalPoints: usize = 5;
/// The maximal number of last minorants to be kept in the model.
#[allow(non_upper_case_globals)]
const MaxMinorants: usize = 5;

/// Information associated with one subproblem.
pub struct NearestValue {
    /// The current center point and the known lower bound.
    center: Option<(Point, Real)>,

    /// The current candidate point.
    candidate: Option<Point>,

    /// The last evaluation points (y, function_value).
    eval_points: VecDeque<(Point, Real)>,

    /// The last minorants.
    minorants: VecDeque<Arc<Minorant>>,

    /// The candidate guess value and distance.
    candidate_value: Option<Guess>,
}

impl NearestValue {
    pub fn new() -> NearestValue {
        NearestValue {
            center: None,
            candidate: None,
            eval_points: VecDeque::new(),
            minorants: VecDeque::new(),
            candidate_value: None,
        }
    }
}

impl Default for NearestValue {
    fn default() -> NearestValue {
        NearestValue::new()
    }
}

impl GuessModel for NearestValue {
    fn set_center(&mut self, y: &Point) {
        // compute the best lower bound for the new center.
        let lb = self
            .minorants
            .iter()
            .map(|m| m.eval(&y.point))
            .max_by(|a, b| a.partial_cmp(b).unwrap())