RsBundle  Check-in [f0ef5ba392]

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

Overview
Comment:Refactor several lint warnings.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f0ef5ba392b7cb5fc13637b7fe90f79838336e50
User & Date: fifr 2017-01-20 09:19:39.444
References
2020-07-27
09:52
boxed: fix computation in `get_norm_subg2` introduced in [f0ef5ba392]. check-in: eba6824e6a user: fifr tags: release
Context
2017-01-20
09:35
mcf: Do not use temporaries for static CStrings. check-in: f4dea49467 user: fifr tags: trunk
09:19
Refactor several lint warnings. check-in: f0ef5ba392 user: fifr tags: trunk
2017-01-19
21:53
vector: use iterators instead of unchecked access. check-in: 80c0335251 user: fifr tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cplex.rs.
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//!   trycpx!(CPXNETchgobj(cplex::env(), net, CPX_MAX))
//! }
//! ```

#![allow(dead_code)]

use libc::{c_int, c_double, c_char};
use std::ptr;
use std::fmt;
use std::error;

pub use std::result::Result as cplex_std_Result;
pub use std::convert::From as cplex_std_From;

pub const CPXMSGBUFSIZE: usize = 1024;







<







37
38
39
40
41
42
43

44
45
46
47
48
49
50
//!   trycpx!(CPXNETchgobj(cplex::env(), net, CPX_MAX))
//! }
//! ```

#![allow(dead_code)]

use libc::{c_int, c_double, c_char};

use std::fmt;
use std::error;

pub use std::result::Result as cplex_std_Result;
pub use std::convert::From as cplex_std_From;

pub const CPXMSGBUFSIZE: usize = 1024;
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
pub const CPX_ALG_NET: CPXINT = 3;
pub const CPX_ALG_BARRIER: CPXINT = 4;

/// Globally unique environment.
static mut ENV: *mut CPXenv = 0 as *mut CPXenv;

pub unsafe fn env() -> *mut CPXenv {
    if ENV == ptr::null_mut() {
        let mut status: c_int = 0;
        ENV = CPXopenCPLEX(&mut status);
        if status != 0 {
            panic!("Can't open CPLEX environment");
        }
    }
    ENV







|







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
pub const CPX_ALG_NET: CPXINT = 3;
pub const CPX_ALG_BARRIER: CPXINT = 4;

/// Globally unique environment.
static mut ENV: *mut CPXenv = 0 as *mut CPXenv;

pub unsafe fn env() -> *mut CPXenv {
    if ENV.is_null() {
        let mut status: c_int = 0;
        ENV = CPXopenCPLEX(&mut status);
        if status != 0 {
            panic!("Can't open CPLEX environment");
        }
    }
    ENV
Changes to src/hkweighter.rs.
59
60
61
62
63
64
65






66
67
68
69
70
71
72
            eps_weight: 1e30,
            m_r: m_r,
            iter: 0,
            model_max: NEG_INFINITY,
        }
    }
}







impl Weighter for HKWeighter {
    fn weight(&mut self, state: &BundleState, params: &SolverParams) -> Real {
        assert!(params.min_weight > 0.0);
        assert!(params.max_weight >= params.min_weight);

        debug!("HKWeighter {:?} iter:{}", state.step, self.iter);







>
>
>
>
>
>







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
            eps_weight: 1e30,
            m_r: m_r,
            iter: 0,
            model_max: NEG_INFINITY,
        }
    }
}

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

impl Weighter for HKWeighter {
    fn weight(&mut self, state: &BundleState, params: &SolverParams) -> Real {
        assert!(params.min_weight > 0.0);
        assert!(params.max_weight >= params.min_weight);

        debug!("HKWeighter {:?} iter:{}", state.step, self.iter);
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
                   sgnorm,
                   state.cur_val,
                   state.new_cutval,
                   lin_err,
                   self.eps_weight);
            debug!("  new_weight={}", new_weight);

            return new_weight;
        } else {
            self.model_max = self.model_max.max(state.nxt_mod);
            let new_weight = if self.iter > 0 && cur_nxt > self.m_r * cur_mod {
                    w
                } else if self.iter > 3 {
                    state.weight / 2.0
                } else if state.nxt_val < self.model_max {
                    state.weight / 2.0
                } else {
                    state.weight
                }
                .max(state.weight / FACTOR)
                .max(params.min_weight);
            self.eps_weight = self.eps_weight.max(2.0 * cur_mod);
            if new_weight < state.weight {
                self.iter = 1;
                self.model_max = NEG_INFINITY;
            } else {
                self.iter = max(self.iter + 1, 1);
            }

            debug!("  model_max={}", self.model_max);
            debug!("  new_weight={}", new_weight);

            return new_weight;
        }
    }
}







|




|
<
<

















|



117
118
119
120
121
122
123
124
125
126
127
128
129


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
                   sgnorm,
                   state.cur_val,
                   state.new_cutval,
                   lin_err,
                   self.eps_weight);
            debug!("  new_weight={}", new_weight);

            new_weight
        } else {
            self.model_max = self.model_max.max(state.nxt_mod);
            let new_weight = if self.iter > 0 && cur_nxt > self.m_r * cur_mod {
                    w
                } else if self.iter > 3 || state.nxt_val < self.model_max {


                    state.weight / 2.0
                } else {
                    state.weight
                }
                .max(state.weight / FACTOR)
                .max(params.min_weight);
            self.eps_weight = self.eps_weight.max(2.0 * cur_mod);
            if new_weight < state.weight {
                self.iter = 1;
                self.model_max = NEG_INFINITY;
            } else {
                self.iter = max(self.iter + 1, 1);
            }

            debug!("  model_max={}", self.model_max);
            debug!("  new_weight={}", new_weight);

            new_weight
        }
    }
}
Name change from src/master/master.rs to src/master/base.rs.
Changes to src/master/boxed.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
// Copyright (c) 2016 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 {Real, DVector, Minorant};
use master::master::{MasterProblem, Error, Result};
use master::UnconstrainedMasterProblem;

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

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
















|







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
// Copyright (c) 2016, 2017 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 {Real, DVector, Minorant};
use master::{MasterProblem, Error, Result};
use master::UnconstrainedMasterProblem;

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

/**
 * Turn unconstrained master problem into box-constrained one.
 *
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
            self.eta[i] = neweta;
        }

        debug!("Eta update");
        debug!("  primopt={}", self.primopt);
        debug!("  eta    ={}", self.eta);

        return updated_eta;
    }

    // Compute the new candidate point.
    //
    // This consists of two steps:
    //
    // 1. the new point is computed as $-\tfrac{1}{u}\bar{g}$, where $\bar{g}$







|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
            self.eta[i] = neweta;
        }

        debug!("Eta update");
        debug!("  primopt={}", self.primopt);
        debug!("  eta    ={}", self.eta);

        updated_eta
    }

    // Compute the new candidate point.
    //
    // This consists of two steps:
    //
    // 1. the new point is computed as $-\tfrac{1}{u}\bar{g}$, where $\bar{g}$
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
    fn eta_cutval(&self) -> Real {
        let mut val = 0.0;
        for i in 0..self.lb.len() {
            if self.eta[i] >= 0.0 {
                if self.lb[i] > NEG_INFINITY {
                    val += self.lb[i] * self.eta[i];
                }
            } else {
                if self.ub[i] < INFINITY {
                    val += self.ub[i] * self.eta[i];
                }
            }
        }
        return val;
    }


    /**
     * Return $\\|G \alpha - \eta\\|_2\^2$.
     *
     * This is the norm-square of the dual optimal solution including
     * the current box-multipliers $\eta$.
     */
    fn get_norm_subg2(&self) -> Real {
        let dualopt = self.master.dualopt();
        let mut norm2 = 0.0;
        for i in 0..self.eta.len() {
            let x = dualopt[i] - self.eta[i];
            norm2 += x * x;
        }
        return norm2;
    }
}


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








<
|
|
|
|
<
|











<
|
<
<
<
<







149
150
151
152
153
154
155

156
157
158
159

160
161
162
163
164
165
166
167
168
169
170
171

172




173
174
175
176
177
178
179
    fn eta_cutval(&self) -> Real {
        let mut val = 0.0;
        for i in 0..self.lb.len() {
            if self.eta[i] >= 0.0 {
                if self.lb[i] > NEG_INFINITY {
                    val += self.lb[i] * self.eta[i];
                }

            } else if self.ub[i] < INFINITY {
                val += self.ub[i] * self.eta[i];
            }
        }

        val
    }


    /**
     * Return $\\|G \alpha - \eta\\|_2\^2$.
     *
     * This is the norm-square of the dual optimal solution including
     * the current box-multipliers $\eta$.
     */
    fn get_norm_subg2(&self) -> Real {
        let dualopt = self.master.dualopt();

        dualopt.iter().zip(self.eta.iter()).map(|(x,y)| x * y).sum()




    }
}


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

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
    fn multiplier(&self, min: Self::MinorantIndex) -> Real {
        self.master.multiplier(min)
    }

    fn move_center(&mut self, alpha: Real, d: &DVector) {
        self.need_new_candidate = true;
        self.master.move_center(alpha, d);
        for i in 0..self.primopt.len() {
            self.lb[i] -= alpha * d[i];
            self.ub[i] -= alpha * d[i];
        }
    }

    fn set_max_updates(&mut self, max_updates: usize) {
        BoxedMasterProblem::set_max_updates(self, max_updates);
    }

    fn cnt_updates(&self) -> usize {
        self.cnt_updates
    }
}







<
|
|
<










309
310
311
312
313
314
315

316
317

318
319
320
321
322
323
324
325
326
327
    fn multiplier(&self, min: Self::MinorantIndex) -> Real {
        self.master.multiplier(min)
    }

    fn move_center(&mut self, alpha: Real, d: &DVector) {
        self.need_new_candidate = true;
        self.master.move_center(alpha, d);

        self.lb.add_scaled(-alpha, d);
        self.ub.add_scaled(-alpha, d);

    }

    fn set_max_updates(&mut self, max_updates: usize) {
        BoxedMasterProblem::set_max_updates(self, max_updates);
    }

    fn cnt_updates(&self) -> usize {
        self.cnt_updates
    }
}
Changes to src/master/cpx.rs.
1
2
3
4
5
6
7
8
// Copyright (c) 2016 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
|







1
2
3
4
5
6
7
8
// Copyright (c) 2016, 2017 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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
        if nvars == 0 {
            return Err(Error::NoMinorants);
        }
        // update linear costs
        {
            let mut c = Vec::with_capacity(nvars);
            let mut inds = Vec::with_capacity(nvars);
            for mins in self.minorants.iter() {
                for m in mins {
                    inds.push(c.len() as c_int);
                    c.push(-m.constant * self.weight - m.linear.dot(eta));
                }
            }
            trycpx!(CPXchgobj(env(), self.lp, nvars as c_int, inds.as_ptr(), c.as_ptr()));
        }







|







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
        if nvars == 0 {
            return Err(Error::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 {
                    inds.push(c.len() as c_int);
                    c.push(-m.constant * self.weight - m.linear.dot(eta));
                }
            }
            trycpx!(CPXchgobj(env(), self.lp, nvars as c_int, inds.as_ptr(), c.as_ptr()));
        }
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
378
        }
        self.qterm[aggr_idx][aggr_idx] = aggr_diag;

        Ok((aggr_idx, aggr_coeffs))
    }

    fn move_center(&mut self, alpha: Real, d: &DVector) {
        for mins in self.minorants.iter_mut() {
            for m in mins.iter_mut() {
                m.move_center(alpha, d);
            }
        }
    }
}


impl CplexMaster {
    fn init_qp(&mut self) -> Result<()> {
        if self.lp != ptr::null_mut() {
            trycpx!(CPXfreeprob(env(), &mut self.lp));
        }
        trycpx!({
            let mut status = 0;
            self.lp = CPXcreateprob(env(), &mut status, CString::new("mcf").unwrap().as_ptr());
            status
        });







|










|







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
378
        }
        self.qterm[aggr_idx][aggr_idx] = aggr_diag;

        Ok((aggr_idx, aggr_coeffs))
    }

    fn move_center(&mut self, alpha: Real, d: &DVector) {
        for mins in &mut self.minorants {
            for m in mins.iter_mut() {
                m.move_center(alpha, d);
            }
        }
    }
}


impl CplexMaster {
    fn init_qp(&mut self) -> Result<()> {
        if !self.lp.is_null() {
            trycpx!(CPXfreeprob(env(), &mut self.lp));
        }
        trycpx!({
            let mut status = 0;
            self.lp = CPXcreateprob(env(), &mut status, CString::new("mcf").unwrap().as_ptr());
            status
        });
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
                        self.qterm[idx_i][idx_j] = x;
                        self.qterm[idx_j][idx_i] = x;
                    }
                }
            }

            if cfg!(debug_assertions) {
                for &idx_i in activeinds.iter() {
                    for &idx_j in activeinds.iter() {
                        let (fidx_i, i) = self.index2min[idx_i];
                        let (fidx_j, j) = self.index2min[idx_j];
                        let m_i = &self.minorants[fidx_i][i];
                        let m_j = &self.minorants[fidx_j][j];
                        debug_assert!((self.qterm[idx_i][idx_j] - m_i.linear.dot(&m_j.linear)).abs() < 1e-6);
                    }
                }







|
|







438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
                        self.qterm[idx_i][idx_j] = x;
                        self.qterm[idx_j][idx_i] = x;
                    }
                }
            }

            if cfg!(debug_assertions) {
                for &idx_i in &activeinds {
                    for &idx_j in &activeinds {
                        let (fidx_i, i) = self.index2min[idx_i];
                        let (fidx_j, j) = self.index2min[idx_j];
                        let m_i = &self.minorants[fidx_i][i];
                        let m_j = &self.minorants[fidx_j][j];
                        debug_assert!((self.qterm[idx_i][idx_j] - m_i.linear.dot(&m_j.linear)).abs() < 1e-6);
                    }
                }
Changes to src/master/minimal.rs.
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
    }

    fn eval_model(&self, y: &DVector) -> Real {
        let mut result = NEG_INFINITY;
        for m in &self.minorants {
            result = result.max(m.eval(y));
        }
        return result;
    }

    fn aggregate(&mut self, fidx: usize, mins: &[usize]) -> Result<(usize, DVector)> {
        assert!(fidx == 0);
        if mins.len() == 2 {
            debug!("Aggregate");
            debug!("  {} * {}", self.opt_mult[0], self.minorants[0]);







|







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
    }

    fn eval_model(&self, y: &DVector) -> Real {
        let mut result = NEG_INFINITY;
        for m in &self.minorants {
            result = result.max(m.eval(y));
        }
        result
    }

    fn aggregate(&mut self, fidx: usize, mins: &[usize]) -> Result<(usize, DVector)> {
        assert!(fidx == 0);
        if mins.len() == 2 {
            debug!("Aggregate");
            debug!("  {} * {}", self.opt_mult[0], self.minorants[0]);
209
210
211
212
213
214
215
216
217
218
219
220
            Ok((mins[0], dvec![1.0]))
        } else {
            panic!("No minorants specified to be aggregated");
        }
    }

    fn move_center(&mut self, alpha: Real, d: &DVector) {
        for m in self.minorants.iter_mut() {
            m.move_center(alpha, d);
        }
    }
}







|




209
210
211
212
213
214
215
216
217
218
219
220
            Ok((mins[0], dvec![1.0]))
        } else {
            panic!("No minorants specified to be aggregated");
        }
    }

    fn move_center(&mut self, alpha: Real, d: &DVector) {
        for m in &mut self.minorants {
            m.move_center(alpha, d);
        }
    }
}
Changes to src/master/mod.rs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
// Copyright (c) 2016 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/>
//


//! Bundle master problem solver.
//!
//! This module contains solvers for the bundle master problem, i.e.
//! for solving convex optimization problems of the form
//!
//! \\[ \min \left\\{ \hat{f}(d) + \frac{w}{2} \\|d\\|\^2 \colon d \in [l,u] \right\\}, \\]
//!
|















>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Copyright (c) 2016, 2017 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/>
//

#![cfg_attr(feature = "cargo-clippy", allow(doc_markdown))]
//! Bundle master problem solver.
//!
//! This module contains solvers for the bundle master problem, i.e.
//! for solving convex optimization problems of the form
//!
//! \\[ \min \left\\{ \hat{f}(d) + \frac{w}{2} \\|d\\|\^2 \colon d \in [l,u] \right\\}, \\]
//!
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//! * solving the problem,
//! * 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 master;
pub use self::master::{MasterProblem, Error, Result};

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

mod unconstrained;
pub use self::unconstrained::UnconstrainedMasterProblem;








|
|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//! * solving the problem,
//! * 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::{MasterProblem, Error, Result};

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

mod unconstrained;
pub use self::unconstrained::UnconstrainedMasterProblem;

Changes to src/mcf/problem.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
// Copyright (c) 2016 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/>
//

#[allow(dead_code)]

use {Real, Vector, DVector, Minorant, FirstOrderProblem, SimpleEvaluation};
use mcf;

use std::fs::File;
use std::io::{self, Read};
use std::result;
use std::num::{ParseIntError, ParseFloatError};
|















<
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


17
18
19
20
21
22
23
// Copyright (c) 2016, 2017 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 {Real, Vector, DVector, Minorant, FirstOrderProblem, SimpleEvaluation};
use mcf;

use std::fs::File;
use std::io::{self, Read};
use std::result;
use std::num::{ParseIntError, ParseFloatError};
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
            rhsval: 0.0,
            cbase: cbase,
            c: vec![dvec![]; ncom],
        })
    }

    /// Compute costs for a primal solution.
    pub fn get_primal_costs(&self, fidx: usize, primals: &Vec<DVector>) -> Real {
        if self.multimodel {
            primals[0].iter().enumerate().map(|(i, x)| x * self.cbase[fidx][i]).sum()
        } else {
            let mut sum = 0.0;
            for (fidx, p) in primals.iter().enumerate() {
                for (i, x) in p.iter().enumerate() {
                    sum += x * self.cbase[fidx][i];







|







185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
            rhsval: 0.0,
            cbase: cbase,
            c: vec![dvec![]; ncom],
        })
    }

    /// Compute costs for a primal solution.
    pub fn get_primal_costs(&self, fidx: usize, primals: &[DVector]) -> Real {
        if self.multimodel {
            primals[0].iter().enumerate().map(|(i, x)| x * self.cbase[fidx][i]).sum()
        } else {
            let mut sum = 0.0;
            for (fidx, p) in primals.iter().enumerate() {
                for (i, x) in p.iter().enumerate() {
                    sum += x * self.cbase[fidx][i];
Changes to src/mcf/solver.rs.
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
        let mut net = ptr::null_mut();
        let logfile;

        unsafe {
            loop {
                logfile = CPXfopen(CString::new("mcf.cpxlog").unwrap().as_ptr(),
                                   CString::new("w").unwrap().as_ptr());
                if logfile == ptr::null_mut() {
                    return Err(Error::Cplex(CplexError {
                        code: 0,
                        msg: "Can't open log-file".to_string(),
                    }));
                }
                status = CPXsetlogfile(env(), logfile);
                if status != 0 {







|







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
        let mut net = ptr::null_mut();
        let logfile;

        unsafe {
            loop {
                logfile = CPXfopen(CString::new("mcf.cpxlog").unwrap().as_ptr(),
                                   CString::new("w").unwrap().as_ptr());
                if logfile.is_null() {
                    return Err(Error::Cplex(CplexError {
                        code: 0,
                        msg: "Can't open log-file".to_string(),
                    }));
                }
                status = CPXsetlogfile(env(), logfile);
                if status != 0 {
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
                return Err(Error::Cplex(CplexError {
                    code: status,
                    msg: CString::from_raw(msg).to_string_lossy().into_owned(),
                }));
            }
        }

        return Ok(Solver {
            net: net,
            logfile: logfile,
        });
    }

    pub fn num_nodes(&self) -> usize {
        unsafe { CPXNETgetnumnodes(env(), self.net) as usize }
    }

    pub fn num_arcs(&self) -> usize {







|


|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
                return Err(Error::Cplex(CplexError {
                    code: status,
                    msg: CString::from_raw(msg).to_string_lossy().into_owned(),
                }));
            }
        }

        Ok(Solver {
            net: net,
            logfile: logfile,
        })
    }

    pub fn num_nodes(&self) -> usize {
        unsafe { CPXNETgetnumnodes(env(), self.net) as usize }
    }

    pub fn num_arcs(&self) -> usize {
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
                               self.net,
                               &mut stat as *mut c_int,
                               &mut objval as *mut c_double,
                               sol.as_mut_ptr(),
                               ptr::null_mut(),
                               ptr::null_mut(),
                               ptr::null_mut()));
        return Ok(sol);
    }

    pub fn writelp(&self, filename: &str) -> Result<()> {
        Ok(trycpx!(CPXNETwriteprob(env(),
                                   self.net,
                                   CString::new(filename).unwrap().as_ptr(),
                                   ptr::null_mut())))
    }
}







|









168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
                               self.net,
                               &mut stat as *mut c_int,
                               &mut objval as *mut c_double,
                               sol.as_mut_ptr(),
                               ptr::null_mut(),
                               ptr::null_mut(),
                               ptr::null_mut()));
        Ok(sol)
    }

    pub fn writelp(&self, filename: &str) -> Result<()> {
        Ok(trycpx!(CPXNETwriteprob(env(),
                                   self.net,
                                   CString::new(filename).unwrap().as_ptr(),
                                   ptr::null_mut())))
    }
}
Changes to src/solver.rs.
1
2
3
4
5
6
7
8
// Copyright (c) 2016 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
|







1
2
3
4
5
6
7
8
// Copyright (c) 2016, 2017 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
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
        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;

        // update multiplier from master solution
        for i in 0..self.problem.num_subproblems() {
            for m in self.minorants[i].iter_mut() {
                m.multiplier = self.master.multiplier(m.index);
            }
        }

        debug!("Model result");
        debug!("  cur_val ={}", self.cur_val);
        debug!("  nxt_mod ={}", self.nxt_mod);







|







768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
        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;

        // update multiplier from master solution
        for i in 0..self.problem.num_subproblems() {
            for m in &mut self.minorants[i] {
                m.multiplier = self.master.multiplier(m.index);
            }
        }

        debug!("Model result");
        debug!("  cur_val ={}", self.cur_val);
        debug!("  nxt_mod ={}", self.nxt_mod);
920
921
922
923
924
925
926
927

928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
            if nxt_ub > descent_bnd {
                // upper bound will produce null-step
                if self.cur_val - nxt_lb > (self.cur_val - self.nxt_mod) * self.params.nullstep_factor.max(0.5) {
                    warn!("Relative precision of returned objective interval enforces null-step.");
                    self.iterinfos.push(IterationInfo::UpperBoundNullStep);
                }
            }
        } else {

            // lower bound gives already a null step
            if self.cur_val - nxt_lb > 0.8 * (self.cur_val - self.nxt_mod) {
                // subgradient won't yield much improvement
                warn!("Shallow cut (subgradient won't yield much improvement)");
                self.iterinfos.push(IterationInfo::ShallowCut);
            }
        }

        debug!("Step");
        debug!("  cur_val    ={}", self.cur_val);
        debug!("  nxt_mod    ={}", self.nxt_mod);
        debug!("  nxt_ub     ={}", self.nxt_val);
        debug!("  descent_bnd={}", descent_bnd);

        // do a descent step or null step
        if nxt_ub <= descent_bnd {
            self.descent_step();
            return Ok(Step::Descent);
        } else {
            self.null_step();
            return Ok(Step::Null);
        }
    }

    /**
     * Return the bound on the function value that enforces a
     * nullstep.
     *







|
>

<
|
|
|
<











|


|







920
921
922
923
924
925
926
927
928
929

930
931
932

933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
            if nxt_ub > descent_bnd {
                // upper bound will produce null-step
                if self.cur_val - nxt_lb > (self.cur_val - self.nxt_mod) * self.params.nullstep_factor.max(0.5) {
                    warn!("Relative precision of returned objective interval enforces null-step.");
                    self.iterinfos.push(IterationInfo::UpperBoundNullStep);
                }
            }
        } else if self.cur_val - nxt_lb > 0.8 * (self.cur_val - self.nxt_mod) {
            // TODO: double check with ConicBundle if this test makes sense.
            // lower bound gives already a null step

            // subgradient won't yield much improvement
            warn!("Shallow cut (subgradient won't yield much improvement)");
            self.iterinfos.push(IterationInfo::ShallowCut);

        }

        debug!("Step");
        debug!("  cur_val    ={}", self.cur_val);
        debug!("  nxt_mod    ={}", self.nxt_mod);
        debug!("  nxt_ub     ={}", self.nxt_val);
        debug!("  descent_bnd={}", descent_bnd);

        // do a descent step or null step
        if nxt_ub <= descent_bnd {
            self.descent_step();
            Ok(Step::Descent)
        } else {
            self.null_step();
            Ok(Step::Null)
        }
    }

    /**
     * Return the bound on the function value that enforces a
     * nullstep.
     *
Changes to src/vector.rs.
1
2
3
4
5
6
7
8
// Copyright (c) 2016 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
|







1
2
3
4
5
6
7
8
// Copyright (c) 2016, 2017 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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
        elems: Vec<(usize, Real)>,
    },
}


impl fmt::Display for Vector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Vector::Dense(ref v) => write!(f, "{}", v),
            &Vector::Sparse { size, ref elems } => {
                let mut it = elems.iter();
                try!(write!(f, "{}:(", size));
                if let Some(&(i, x)) = it.next() {
                    try!(write!(f, "{}:{}", i, x));
                    for &(i, x) in it {
                        try!(write!(f, ", {}:{}", i, x));
                    }







|
|
|







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
        elems: Vec<(usize, Real)>,
    },
}


impl fmt::Display for Vector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Vector::Dense(ref v) => write!(f, "{}", v),
            Vector::Sparse { size, ref elems } => {
                let mut it = elems.iter();
                try!(write!(f, "{}:(", size));
                if let Some(&(i, x)) = it.next() {
                    try!(write!(f, "{}:{}", i, x));
                    for &(i, x) in it {
                        try!(write!(f, ", {}:{}", i, x));
                    }
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297

    /**
     * Convert vector to a dense vector.
     *
     * This function always returns a copy of the vector.
     */
    pub fn to_dense(&self) -> DVector {
        match self {
            &Vector::Dense(ref x) => x.clone(),
            &Vector::Sparse { size: n, elems: ref xs } => {
                let mut v = vec![0.0; n];
                for &(i, x) in xs {
                    unsafe { *v.get_unchecked_mut(i) = x };
                }
                DVector(v)
            }
        }
    }
}







|
|
|









279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297

    /**
     * Convert vector to a dense vector.
     *
     * This function always returns a copy of the vector.
     */
    pub fn to_dense(&self) -> DVector {
        match *self {
            Vector::Dense(ref x) => x.clone(),
            Vector::Sparse { size: n, elems: ref xs } => {
                let mut v = vec![0.0; n];
                for &(i, x) in xs {
                    unsafe { *v.get_unchecked_mut(i) = x };
                }
                DVector(v)
            }
        }
    }
}