9.1 Solving Linear Systems Involving the Basis Matrix¶
A linear optimization problem always has an optimal solution which is also a basic solution. In an optimal basic solution there are exactly
as a matrix consisting of the columns of
or, equivalently,
and
each have a unique solution for all
MOSEK provides functions for solving the linear systems (9.1) and (9.2) for an arbitrary
In the next sections we will show how to use MOSEK to
9.1.1 Basis identification¶
To use the solutions to (9.1) and (9.2) it is important to know how the basis matrix
Internally MOSEK employs the linear optimization problem
where
The basis matrix is constructed of
If variable Task.init_basis_solve
. This function initializes data structures for later use and returns the indexes of the basic variables in the array basis
. The interpretation of the basis
is as follows. If we have
then the
Moreover, the
then the
and the
For instance if
An example
Consider the linear optimization problem:
Suppose a call to Task.init_basis_solve
returns an array basis
so that
basis[0] = 1,
basis[1] = 2.
Then the basis variables are
Please note the ordering of the columns in
fn solve() -> Result<(),String> {
let mut task = match Task::new() {
Some(e) => e,
None => return Err("Failed to create task".to_string()),
}.with_callbacks();
task.put_stream_callback(Streamtype::LOG, |msg| print!("{}",msg))?;
task.put_obj_name("solvebasis")?;
let numcon : i32 = 2;
let numvar : i32 = 2;
let mut w1 = vec![2.0, 6.0];
let mut w2 = vec![1.0, 0.0];
task.input_data(numcon, numvar,
&[1.0, 1.0], // c
0.0, // cfix
&[0,2], // ptrb
&[2,3], // ptre
&[0,1,
0,1], // sub
&[1.0, 1.0,
2.0, 1.0], // val
&[Boundkey::UP,
Boundkey::UP], // bkc
&[0.0,0.0], // blc
&[2.0,6.0], // buc
&[Boundkey::LO,
Boundkey::LO], // bkx
&[0.0, 0.0], // blx
&[0.0,0.0])?; // bux;
task.put_obj_sense(Objsense::MAXIMIZE)?;
let _ = task.optimize()?;
let mut basis = vec![0i32; numcon as usize];
task.init_basis_solve(basis.as_mut_slice())?;
// List basis variables corresponding to columns of B
let mut varsub = vec![0i32, 1];
for i in 0..numcon {
if basis[varsub[i as usize] as usize] < numcon {
println!("Basis variable no {} is xc{}",i,basis[varsub[i as usize] as usize]);
}
else {
println!("Basis variable no {} is x{}",i,basis[i as usize]-numcon);
// solve Bx = w1
// varsub contains index of non-zeros in b.
// On return b contains the solution x and
// varsub the index of the non-zeros in x.
{
let nz = task.solve_with_basis(false, 2, varsub.as_mut_slice(), w1.as_mut_slice())?;
println!("nz = {}",nz);
println!("Solution to Bx = {:?}",w1);
for vsubi in &varsub[0..nz as usize] {
if basis[*vsubi as usize] < numcon {
println!("xc {} = {}",basis[*vsubi as usize],w1[*vsubi as usize]);
}
else {
println!("x{} = {}",basis[*vsubi as usize] - numcon,w1[*vsubi as usize])
}
}
}
// Solve B^Tx = w2
{
varsub[0] = 1;
let nz = task.solve_with_basis(true,1,varsub.as_mut_slice(),w2.as_mut_slice())?;
println!("nz = {}",nz);
println!("Solution to B^Tx = {:?}",w2);
for vsubi in &varsub[0..nz as usize] {
if basis[*vsubi as usize] < numcon {
print!("xc{} = {}",basis[*vsubi as usize],w2[*vsubi as usize]);
}
else {
print!("x{} = {}",basis[*vsubi as usize] - numcon,w2[*vsubi as usize]);
}
}
}
}
}
Ok(())
}
fn main() -> Result<(),String> {
solve()
}
In the example above the linear system is solved using the optimal basis for (9.4) and the original right-hand side of the problem. Thus the solution to the linear system is the optimal solution to the problem. When running the example program the following output is produced.
basis[0] = 1
Basis variable no 0 is xc1.
basis[1] = 2
Basis variable no 1 is x0.
Solution to Bx = b:
x0 = 2.000000e+00
xc1 = -4.000000e+00
Solution to B^Tx = c:
x1 = -1.000000e+00
x0 = 1.000000e+00
Please note that the ordering of the basis variables is
and thus the basis is given by:
It can be verified that
is a solution to
9.1.2 Solving arbitrary linear systems¶
MOSEK can be used to solve an arbitrary (rectangular) linear system
using the Task.solve_with_basis
function without optimizing the problem as in the previous example. This is done by setting up an Task.solve_with_basis
function with the
An example
Below we demonstrate how to solve the linear system
with two inputs
extern crate mosek;
use mosek::{Task,Boundkey,Streamtype,Soltype,Stakey};
const INF : f64 = 0.0;
fn setup(task : & mut mosek::Task,
aval : &[f64],
asub : &[i32],
ptrb : &[i64],
ptre : &[i64],
numvar : i32) -> Result<Vec<i32>,String> {
// mosek.stakey[] skx = new mosek.stakey [numvar];
// mosek.stakey[] skc = new mosek.stakey [numvar];
// for (int i = 0; i < numvar ; ++i) {
// skx[i] = mosek.stakey.bas;
// skc[i] = mosek.stakey.fix;
// }
task.append_vars(numvar)?;
task.append_cons(numvar)?;
task.put_a_col_slice(0,numvar,ptrb,ptre,asub,aval)?;
task.put_con_bound_slice_const(0,numvar,Boundkey::FX,0.0,0.0)?;
task.put_var_bound_slice_const(0,numvar,Boundkey::FR,-INF,INF)?;
/* Define a basic solution by specifying
status keys for variables & constraints. */
task.delete_solution(Soltype::BAS)?;
task.put_skc_slice(Soltype::BAS, 0, numvar, vec![Stakey::FIX; numvar as usize].as_slice())?;
task.put_skx_slice(Soltype::BAS, 0, numvar, vec![Stakey::BAS; numvar as usize].as_slice())?;
let mut basis = vec![0; numvar as usize];
task.init_basis_solve(basis.as_mut_slice())?;
Ok(basis)
}
fn main() -> Result<(),String> {
let numcon : i32 = 2;
let numvar : i32 = 2;
let aval = &[ -1.0 ,
1.0, 1.0 ];
let asub = &[ 1,
0, 1 ];
let ptrb = &[0, 1];
let ptre = &[1, 3];
// int[] bsub = new int[numvar];
// double[] b = new double[numvar];
// int[] basis = new int[numvar];
let mut task = Task::new().unwrap();
// Put A matrix and factor A. Call this function only once for a
// given task.
let basis = setup(& mut task,
aval,
asub,
ptrb,
ptre,
numvar)?;
let mut task = task.with_callbacks();
task.put_stream_callback(Streamtype::LOG, |msg| print!("{}",msg))?;
/* now solve rhs */
let mut b = vec![0.0; numvar as usize];
let mut bsub = vec![0; numvar as usize];
b[0] = 1.0; b[1] = -2.0;
bsub[0] = 0; bsub[1] = 1;
let nz = task.solve_with_basis(false, 2, bsub.as_mut_slice(), b.as_mut_slice())?;
println!("\nSolution to Bx = b:\n");
// Print solution and show correspondents to original variables in
// the problem
for &bsubi in bsub[..nz as usize].iter() {
if basis[bsubi as usize] < numcon {
println!("This should never happen");
}
else {
println!("x{} = {}",basis[bsubi as usize] - numcon, b[bsubi as usize]);
}
}
b[0] = 7.0;
bsub[0] = 0;
let nz = task.solve_with_basis(false, 1, bsub.as_mut_slice(), b.as_mut_slice())?;
println!("Solution to Bx = b:");
// Print solution and show correspondents to original variables in
// the problem
for &bsubi in bsub[..nz as usize].iter() {
if basis[bsubi as usize] < numcon {
println!("This should never happen");
}
else {
println!("x{} = {}",basis[bsubi as usize] - numcon,b[bsubi as usize]);
}
}
Ok(())
}
The most important step in the above example is the definition of the basic solution, where we define the status key for each variable. The actual values of the variables are not important and can be selected arbitrarily, so we set them to zero. All variables corresponding to columns in the linear system we want to solve are set to basic and the slack variables for the constraints, which are all non-basic, are set to their bound.
The program produces the output:
Solution to Bx = b:
x1 = 1
x0 = 3
Solution to Bx = b:
x1 = 7
x0 = 7