// 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 {DVector, Minorant, Real};
use std::error::Error;
use std::fmt;
use std::result;
/// Error type for master problems.
#[derive(Debug)]
pub enum MasterProblemError {
/// Extension of the subgradient failed with some user error.
SubgradientExtension(Box<dyn Error>),
/// No minorants available when solving the master problem
NoMinorants,
/// An error in the solver backend occurred.
Solver(Box<dyn Error>),
/// A custom error (specific to the master problem solver) occurred.
Custom(Box<dyn Error>),
}
impl fmt::Display for MasterProblemError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
use self::MasterProblemError::*;
match self {
SubgradientExtension(err) => write!(fmt, "Subgradient extension failed: {}", err),
NoMinorants => write!(fmt, "No minorants when solving the master problem"),
Solver(err) => write!(fmt, "Solver error: {}", err),
Custom(err) => err.fmt(fmt),
}
}
}
impl Error for MasterProblemError {
fn cause(&self) -> Option<&Error> {
use self::MasterProblemError::*;
match self {
SubgradientExtension(err) | Solver(err) | Custom(err) => Some(err.as_ref()),
NoMinorants => None,
}
}
}
/// Result type of master problems.
pub type Result<T> = result::Result<T, MasterProblemError>;
pub trait MasterProblem {
/// Unique index for a minorant.
type MinorantIndex: Copy + Eq;
/// Set the number of subproblems.
fn set_num_subproblems(&mut self, n: usize) -> Result<()>;
/// Set the lower and upper bounds of the variables.
fn set_vars(&mut self, nvars: usize, lb: Option<DVector>, ub: Option<DVector>) -> Result<()>;
/// Return the current number of minorants of subproblem `fidx`.
fn num_minorants(&self, fidx: usize) -> usize;
/// Return the current weight of the quadratic term.
fn weight(&self) -> Real;
/// Set the weight of the quadratic term, must be > 0.
fn set_weight(&mut self, weight: Real) -> Result<()>;
/// Set the maximal number of inner iterations.
fn set_max_updates(&mut self, max_updates: usize) -> Result<()>;
/// Return the current number of inner iterations.
fn cnt_updates(&self) -> usize;
/// Add or movesome variables with bounds.
///
/// If an index is specified, existing variables are moved,
/// otherwise new variables are generated.
fn add_vars(
&mut self,
bounds: &[(Option<usize>, Real, Real)],
extend_subgradient: &mut FnMut(usize, Self::MinorantIndex, &[usize]) -> result::Result<DVector, Box<dyn Error>>,
) -> Result<()>;
/// Add a new minorant to the model.
///
/// The function returns a unique (among all minorants of all
/// subproblems) index of the minorant. This index must remain
/// valid until the minorant is aggregated.
fn add_minorant(&mut self, fidx: usize, minorant: Minorant) -> Result<Self::MinorantIndex>;
/// Solve the master problem.
fn solve(&mut self, cur_value: Real) -> Result<()>;
/// Aggregate the given minorants according to the current
/// solution.
///
/// The (indices of the) minorants to be aggregated get invalid
/// after this operation. The function returns the index of the
/// aggregated minorant along with the coefficients of the convex
/// combination. The index of the new aggregated minorant might or
/// might not be one of indices of the original minorants.
///
/// # Error The indices of the minorants `mins` must belong to
/// subproblem `fidx`.
fn aggregate(&mut self, fidx: usize, mins: &[usize]) -> Result<(Self::MinorantIndex, DVector)>;
/// Return the (primal) optimal solution $\\|d\^*\\|$.
fn get_primopt(&self) -> DVector;
/// Return the value of the linear model in the optimal solution.
///
/// This is the term $\langle g\^*, d\^* \rangle$ where $g^\*$ is
/// the optimal aggregated subgradient of the current
/// cutting-plane model and $d\^*$ is the (primal) optimal solution.
fn get_primoptval(&self) -> Real;
/// Return $\\|g^\*\\|_2\^2$.
///
/// $g\^*$ is the optimal aggregated subgradient.
fn get_dualoptnorm2(&self) -> Real;
/// Return the multiplier associated with a minorant.
fn multiplier(&self, min: Self::MinorantIndex) -> Real;
/// Move the center of the master problem to $\alpha \cdot d$.
fn move_center(&mut self, alpha: Real, d: &DVector);
}