RsBundle  Check-in [8b2fc81132]

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

Overview
Comment:Update dependencies to cplex-sys.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8b2fc8113235a6d0fd295f77b388c3af8ad32fa9
User & Date: fifr 2017-04-04 06:57:55.806
Context
2017-04-18
12:59
solver: add `solver_iter` method. check-in: 10b0be88b8 user: fifr tags: trunk
2017-04-04
06:57
Update dependencies to cplex-sys. check-in: 8b2fc81132 user: fifr tags: trunk
2017-03-17
09:31
Remove duplicate `extern`. check-in: 8458c760ee user: fifr tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to Cargo.toml.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[package]
name = "bundle"
version = "0.3.0"
authors = ["Frank Fischer <frank-fischer@shadow-soft.de>"]

[dependencies]
libc = "^0.2.6"
quick-error = "^1.1.0"
log = "^0.3.6"
const-cstr = "^0.2.1"
cplex-sys = { version = "^0.1", path = "../cplex-sys" }

[dev-dependencies]
env_logger = "^0.4.1"










|



1
2
3
4
5
6
7
8
9
10
11
12
13
14
[package]
name = "bundle"
version = "0.3.0"
authors = ["Frank Fischer <frank-fischer@shadow-soft.de>"]

[dependencies]
libc = "^0.2.6"
quick-error = "^1.1.0"
log = "^0.3.6"
const-cstr = "^0.2.1"
cplex-sys = { version = "^0.2", path = "../cplex-sys" }

[dev-dependencies]
env_logger = "^0.4.1"
Changes to src/master/cpx.rs.
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
//

//! Master problem implementation using CPLEX.

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

use cplex_sys::*;

use std::result;
use std::ffi::CString;
use std::ptr;
use std::os::raw::{c_char, c_int};
use std::f64::{self, NEG_INFINITY};

quick_error! {
    /// A solver error.
    #[derive(Debug)]
    pub enum Error {
        Cplex(err: CplexError) {
            cause(err)
            description(err.description())
            display("{}", err)
            from()
        }

        NoMinorants {
            description("No minorants")
            display("Solver Error: no minorants when solving the master problem")
        }
    }
}

pub type Result<T> = result::Result<T, Error>;

pub struct CplexMaster {
    lp: *mut CPXLp,

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

    /// List of free minorant indices.
    freeinds: Vec<usize>,








|











|

|
|
|




|







|







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

//! Master problem implementation using CPLEX.

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

use cplex_sys as cpx;

use std::result;
use std::ffi::CString;
use std::ptr;
use std::os::raw::{c_char, c_int};
use std::f64::{self, NEG_INFINITY};

quick_error! {
    /// A solver error.
    #[derive(Debug)]
    pub enum Error {
        Cplex(err: cpx::Error) {
            cause(err)
                description(err.description())
                display("{}", err)
                from()
        }

        NoMinorants {
            description("No minorants")
                display("Solver Error: no minorants when solving the master problem")
        }
    }
}

pub type Result<T> = result::Result<T, Error>;

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

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

    /// List of free minorant indices.
    freeinds: Vec<usize>,

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
    opt_mults: Vec<DVector>,
    /// Optimal aggregated minorant.
    opt_minorant: Minorant,
}

impl Drop for CplexMaster {
    fn drop(&mut self) {
        unsafe { CPXfreeprob(env(), &mut self.lp) };
    }
}


impl UnconstrainedMasterProblem for CplexMaster {
    type Error = Error;








|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
    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) };
    }
}


impl UnconstrainedMasterProblem for CplexMaster {
    type Error = Error;

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    }

    fn num_subproblems(&self) -> usize {
        self.minorants.len()
    }

    fn set_num_subproblems(&mut self, n: usize) -> Result<()> {
        trycpx!(CPXsetintparam(env(), Param::Qpmethod.to_c(), Alg::Barrier.to_c()));
        trycpx!(CPXsetdblparam(env(), Param::Barepcomp.to_c(), 1e-12));

        self.min2index = vec![vec![]; n];
        self.index2min.clear();
        self.freeinds.clear();
        self.minorants = vec![vec![]; n];
        self.opt_mults = vec![dvec![]; n];








|
|







108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    }

    fn num_subproblems(&self) -> usize {
        self.minorants.len()
    }

    fn set_num_subproblems(&mut self, n: usize) -> Result<()> {
        trycpx!(cpx::setintparam(cpx::env(), cpx::Param::Qpmethod.to_c(), cpx::Alg::Barrier.to_c()));
        trycpx!(cpx::setdblparam(cpx::env(), cpx::Param::Barepcomp.to_c(), 1e-12));

        self.min2index = vec![vec![]; n];
        self.index2min.clear();
        self.freeinds.clear();
        self.minorants = vec![vec![]; n];
        self.opt_mults = vec![dvec![]; n];

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    }

    fn solve(&mut self, eta: &DVector, _fbound: Real, _augbound: Real, _relprec: Real) -> Result<()> {
        if self.force_update || !self.updateinds.is_empty() {
            try!(self.init_qp());
        }

        let nvars = unsafe { CPXgetnumcols(env(), self.lp) as usize };
        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()));
        }

        trycpx!(CPXqpopt(env(), self.lp));
        let mut sol = vec![0.0; nvars];
        trycpx!(CPXgetx(env(), self.lp, sol.as_mut_ptr(), 0, nvars as c_int - 1));

        let mut idx = 0;
        let mut mults = Vec::with_capacity(nvars);
        let mut mins = Vec::with_capacity(nvars);
        for fidx in 0..self.minorants.len() {
            for i in 0..self.minorants[fidx].len() {
                self.opt_mults[fidx][i] = sol[idx];







|













|


|

|







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    }

    fn solve(&mut self, eta: &DVector, _fbound: Real, _augbound: Real, _relprec: Real) -> Result<()> {
        if self.force_update || !self.updateinds.is_empty() {
            try!(self.init_qp());
        }

        let nvars = unsafe { cpx::getnumcols(cpx::env(), self.lp) as usize };
        if nvars == 0 {
            return Err(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!(cpx::chgobj(cpx::env(), self.lp, nvars as c_int, inds.as_ptr(), c.as_ptr()));
        }

        trycpx!(cpx::qpopt(cpx::env(), self.lp));
        let mut sol = vec![0.0; nvars];
        trycpx!(cpx::getx(cpx::env(), self.lp, sol.as_mut_ptr(), 0, nvars as c_int - 1));

        let mut idx = 0;
        let mut mults = Vec::with_capacity(nvars);
        let mut mins = Vec::with_capacity(nvars);
        for fidx in 0..self.minorants.len() {
            for i in 0..self.minorants[fidx].len() {
                self.opt_mults[fidx][i] = sol[idx];
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
    }
}


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

        if self.force_update {
            self.updateinds.clear();
            for inds in &self.min2index {
                self.updateinds.extend(inds.iter());







|



|







373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
    }
}


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

        if self.force_update {
            self.updateinds.clear();
            for inds in &self.min2index {
                self.updateinds.extend(inds.iter());
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
                for _ in 0..self.minorants[i].len() {
                    rmatind.push(nvars as c_int);
                    rmatval.push(1.0);
                    nvars += 1;
                }
            }

            trycpx!(CPXaddrows(env(),
                               self.lp,
                               nvars as c_int,
                               nfun as c_int,
                               nvars as c_int,
                               rhs.as_ptr(),
                               sense.as_ptr(),
                               rmatbeg.as_ptr(),
                               rmatind.as_ptr(),
                               rmatval.as_ptr(),
                               ptr::null(),
                               ptr::null()));
        }

        // build quadratic term
        {
            self.qterm.resize(self.index2min.len(), dvec![]);
            for i in 0..self.qterm.len() {
                self.qterm[i].resize(self.index2min.len(), 0.0);







|
|
|
|
|
|
|
|
|
|
|
|







409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
                for _ in 0..self.minorants[i].len() {
                    rmatind.push(nvars as c_int);
                    rmatval.push(1.0);
                    nvars += 1;
                }
            }

            trycpx!(cpx::addrows(cpx::env(),
                                 self.lp,
                                 nvars as c_int,
                                 nfun as c_int,
                                 nvars as c_int,
                                 rhs.as_ptr(),
                                 sense.as_ptr(),
                                 rmatbeg.as_ptr(),
                                 rmatind.as_ptr(),
                                 rmatval.as_ptr(),
                                 ptr::null(),
                                 ptr::null()));
        }

        // build quadratic term
        {
            self.qterm.resize(self.index2min.len(), dvec![]);
            for i in 0..self.qterm.len() {
                self.qterm[i].resize(self.index2min.len(), 0.0);
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
            }
            maxq *= 1e-8;

            // update coefficients
            for (i, &idx_i) in activeinds.iter().enumerate() {
                for (j, &idx_j) in activeinds.iter().enumerate() {
                    if i != j {
                        trycpx!(CPXchgqpcoef(env(),
                                             self.lp,
                                             i as c_int,
                                             j as c_int,
                                             self.qterm[idx_i][idx_j]));
                    } else {
                        trycpx!(CPXchgqpcoef(env(),
                                             self.lp,
                                             i as c_int,
                                             j as c_int,
                                             self.qterm[idx_i][idx_j] + maxq));
                    }
                }
            }
        }

        self.updateinds.clear();
        self.force_update = false;

        Ok(())
    }
}







|
|
|
|
|

|
|
|
|
|











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
            }
            maxq *= 1e-8;

            // update coefficients
            for (i, &idx_i) in activeinds.iter().enumerate() {
                for (j, &idx_j) in activeinds.iter().enumerate() {
                    if i != j {
                        trycpx!(cpx::chgqpcoef(cpx::env(),
                                               self.lp,
                                               i as c_int,
                                               j as c_int,
                                               self.qterm[idx_i][idx_j]));
                    } else {
                        trycpx!(cpx::chgqpcoef(cpx::env(),
                                               self.lp,
                                               i as c_int,
                                               j as c_int,
                                               self.qterm[idx_i][idx_j] + maxq));
                    }
                }
            }
        }

        self.updateinds.clear();
        self.force_update = false;

        Ok(())
    }
}
Changes to src/mcf/solver.rs.
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
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
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
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
//
// 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};

use cplex_sys::*;

use std::ptr;
use std::ffi::CString;
use std::result;

use std::os::raw::{c_char, c_int, c_double};

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Cplex(err: CplexError) {
            cause(err)
                description(err.description())
                display("{}", err)
                from()
        }
    }
}

pub type Result<T> = result::Result<T, Error>;

pub struct Solver {
    net: *mut CPXNet,
    logfile: *mut CPXFile,
}


impl Drop for Solver {
    fn drop(&mut self) {
        unsafe {
            CPXNETfreeprob(env(), &mut self.net);
            CPXfclose(self.logfile);
        }
    }
}

impl Solver {
    pub fn new(nnodes: usize) -> Result<Solver> {
        let mut status: c_int;
        let mut net = ptr::null_mut();
        let logfile;

        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(never_loop))]
            loop {
                logfile = CPXfopen(const_cstr!("mcf.cpxlog").as_ptr(), const_cstr!("w").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 {
                    break;
                }

                net = CPXNETcreateprob(env(), &mut status, const_cstr!("mcf").as_ptr());
                if status != 0 {
                    break;
                }
                status = CPXNETaddnodes(env(), net, nnodes as c_int, ptr::null(), ptr::null());
                if status != 0 {
                    break;
                }
                status = CPXNETchgobjsen(env(), net, ObjectiveSense::Minimize.to_c());
                if status != 0 {
                    break;
                }
                break;
            }

            if status != 0 {
                let msg = CString::new(vec![0; MESSAGE_BUF_SIZE]).unwrap().into_raw();
                CPXgeterrorstring(env(), status, msg);
                CPXNETfreeprob(env(), &mut net);
                CPXfclose(logfile);
                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 {
        unsafe { CPXNETgetnumarcs(env(), self.net) as usize }
    }

    pub fn set_balance(&mut self, node: usize, supply: Real) -> Result<()> {
        let n = node as c_int;
        let s = supply as c_double;
        Ok(trycpx!(CPXNETchgsupply(env(), self.net, 1, &n, &s as *const c_double)))
    }

    pub fn set_objective(&mut self, obj: &DVector) -> Result<()> {
        let inds = (0..obj.len() as c_int).collect::<Vec<_>>();
        Ok(trycpx!(CPXNETchgobj(env(),
                                self.net,
                                obj.len() as c_int,
                                inds.as_ptr(),
                                obj.as_ptr())))
    }

    pub fn add_arc(&mut self, src: usize, snk: usize, cost: Real, cap: Real) -> Result<()> {
        let f = src as c_int;
        let t = snk as c_int;
        let c = cost as c_double;
        let u = cap as c_double;
        let name = CString::new(format!("x{}#{}_{}", self.num_arcs() + 1, f + 1, t + 1)).unwrap();
        let cname = name.as_ptr();
        Ok(trycpx!(CPXNETaddarcs(env(),
                                 self.net,
                                 1,
                                 &f,
                                 &t,
                                 ptr::null(),
                                 &u,
                                 &c,
                                 &cname as *const *const c_char)))
    }

    pub fn solve(&mut self) -> Result<()> {
        Ok(trycpx!(CPXNETprimopt(env(), self.net)))
    }

    pub fn objective(&self) -> Result<Real> {
        let mut objval: c_double = 0.0;
        trycpx!(CPXNETgetobjval(env(), self.net, &mut objval as *mut c_double));
        Ok(objval)
    }

    pub fn get_solution(&self) -> Result<DVector> {
        let mut sol = dvec![0.0; self.num_arcs()];
        let mut stat: c_int = 0;
        let mut objval: c_double = 0.0;
        trycpx!(CPXNETsolution(env(),
                               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())))
    }
}







|










|











|
|






|
|













|

|




|
>
>
>
>
>



|
<



<
<
<
<
|







|
|
|
|
|













|



|





|




|
|
|
|
|









|
|
|
|
|
|
|
|
|



|




|







|
|
|
|
|
|
|
|




|
|
|
|


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

use cplex_sys as cpx;

use std::ptr;
use std::ffi::CString;
use std::result;

use std::os::raw::{c_char, c_int, c_double};

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Cplex(err: cpx::Error) {
            cause(err)
                description(err.description())
                display("{}", err)
                from()
        }
    }
}

pub type Result<T> = result::Result<T, Error>;

pub struct Solver {
    net: *mut cpx::Net,
    logfile: *mut cpx::File,
}


impl Drop for Solver {
    fn drop(&mut self) {
        unsafe {
            cpx::NETfreeprob(cpx::env(), &mut self.net);
            cpx::fclose(self.logfile);
        }
    }
}

impl Solver {
    pub fn new(nnodes: usize) -> Result<Solver> {
        let mut status: c_int;
        let mut net = ptr::null_mut();
        let logfile;

        unsafe {
            #[cfg_attr(feature = "cargo-clippy", allow(never_loop))]
            loop {
                logfile = cpx::fopen(const_cstr!("mcf.cpxlog").as_ptr(), const_cstr!("w").as_ptr());
                if logfile.is_null() {
                    return Err(Error::Cplex(cpx::Error {
                        code: 0,
                        msg: "Can't open log-file".to_string(),
                    }));
                }
                status = cpx::setlogfile(cpx::env(), logfile);
                if status != 0 {
                    break;
                }

                net = cpx::NETcreateprob(cpx::env(), &mut status, const_cstr!("mcf").as_ptr());
                if status != 0 {
                    break;
                }
                status = cpx::NETaddnodes(cpx::env(), net, nnodes as c_int, ptr::null(), ptr::null());

                if status != 0 {
                    break;
                }




                status = cpx::NETchgobjsen(cpx::env(), net, cpx::ObjectiveSense::Minimize.to_c());
                if status != 0 {
                    break;
                }
                break;
            }

            if status != 0 {
                let msg = CString::new(vec![0; cpx::MESSAGE_BUF_SIZE]).unwrap().into_raw();
                cpx::geterrorstring(cpx::env(), status, msg);
                cpx::NETfreeprob(cpx::env(), &mut net);
                cpx::fclose(logfile);
                return Err(Error::Cplex(cpx::Error {
                    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 { cpx::NETgetnumnodes(cpx::env(), self.net) as usize }
    }

    pub fn num_arcs(&self) -> usize {
        unsafe { cpx::NETgetnumarcs(cpx::env(), self.net) as usize }
    }

    pub fn set_balance(&mut self, node: usize, supply: Real) -> Result<()> {
        let n = node as c_int;
        let s = supply as c_double;
        Ok(trycpx!(cpx::NETchgsupply(cpx::env(), self.net, 1, &n, &s as *const c_double)))
    }

    pub fn set_objective(&mut self, obj: &DVector) -> Result<()> {
        let inds = (0..obj.len() as c_int).collect::<Vec<_>>();
        Ok(trycpx!(cpx::NETchgobj(cpx::env(),
                                  self.net,
                                  obj.len() as c_int,
                                  inds.as_ptr(),
                                  obj.as_ptr())))
    }

    pub fn add_arc(&mut self, src: usize, snk: usize, cost: Real, cap: Real) -> Result<()> {
        let f = src as c_int;
        let t = snk as c_int;
        let c = cost as c_double;
        let u = cap as c_double;
        let name = CString::new(format!("x{}#{}_{}", self.num_arcs() + 1, f + 1, t + 1)).unwrap();
        let cname = name.as_ptr();
        Ok(trycpx!(cpx::NETaddarcs(cpx::env(),
                                   self.net,
                                   1,
                                   &f,
                                   &t,
                                   ptr::null(),
                                   &u,
                                   &c,
                                   &cname as *const *const c_char)))
    }

    pub fn solve(&mut self) -> Result<()> {
        Ok(trycpx!(cpx::NETprimopt(cpx::env(), self.net)))
    }

    pub fn objective(&self) -> Result<Real> {
        let mut objval: c_double = 0.0;
        trycpx!(cpx::NETgetobjval(cpx::env(), self.net, &mut objval as *mut c_double));
        Ok(objval)
    }

    pub fn get_solution(&self) -> Result<DVector> {
        let mut sol = dvec![0.0; self.num_arcs()];
        let mut stat: c_int = 0;
        let mut objval: c_double = 0.0;
        trycpx!(cpx::NETsolution(cpx::env(),
                                 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!(cpx::NETwriteprob(cpx::env(),
                                     self.net,
                                     CString::new(filename).unwrap().as_ptr(),
                                     ptr::null_mut())))
    }
}