/*
* Copyright (c) 2019 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/>
*/
//! An asynchronous first-order oracle.
use crate::{Aggregatable, DVector, Minorant, Real};
use crossbeam::channel::Sender;
use std::sync::Arc;
/// Evaluation result.
///
/// The result of an evaluation is new information to be made
/// available to the solver and the master problem. There are
/// essentially two types of information:
///
/// 1. The (exact) function value of a sub-function at some point.
/// 2. A minorant of some sub-function.
#[derive(Debug)]
pub enum EvalResult<I, P> {
/// The objective value at some point.
ObjectiveValue { index: I, value: Real },
/// A minorant with an associated primal.
Minorant { index: I, minorant: Minorant, primal: P },
}
pub type ResultSender<I, P, E> = Sender<Result<EvalResult<I, P>, E>>;
/// Trait for implementing a first-order problem description.
///
/// All computations made by an implementation are supposed to
/// be asynchronous. Hence, the interface is slightly different
/// compared with [`crate::FirstOrderProblem`].
pub trait FirstOrderProblem {
/// Error raised by this oracle.
type Err: Send + 'static;
/// The primal information associated with a minorant.
type Primal: Aggregatable + Send + 'static;
/// Return the number of variables.
fn num_variables(&self) -> usize;
/// Return the lower bounds on the variables.
///
/// If no lower bounds a specified, $-\infty$ is assumed.
///
/// The lower bounds must be less then or equal the upper bounds.
fn lower_bounds(&self) -> Option<Vec<Real>> {
None
}
/**
* Return the upper bounds on the variables.
*
* If no lower bounds a specified, $+\infty$ is assumed.
*
* The upper bounds must be greater than or equal the upper bounds.
*/
fn upper_bounds(&self) -> Option<Vec<Real>> {
None
}
/// Return the number of subproblems.
fn num_subproblems(&self) -> usize {
1
}
/// Start background processes.
///
/// This method is called right before the solver starts the solution process.
/// It can be used to setup any background tasks required for the evaluation
/// of the subfunctions.
///
/// Remember that background processes should be cleanup when the problem
/// is deleted (e.g. by implementing the [`Drop`] trait).
///
/// The default implementation does nothing.
fn start(&mut self) {}
/// Stop background processes.
///
/// This method is called right after the solver stops the solution process.
/// It can be used to stop any background tasks required for the evaluation
/// of the subfunctions.
///
/// A correct implementation of should cleanup all processes from the [`Drop`]
/// thread.
///
/// The default implementation does nothing.
fn stop(&mut self) {}
/// Start the evaluation of the i^th subproblem at the given point.
///
/// The results of the evaluation should be passed to the provided channel.
/// In order to work correctly, the results must contain (an upper bound on)
/// the objective value at $y$ as well as at least one subgradient centered
/// at $y$ eventually.
fn evaluate<I: Send + Copy + 'static>(
&mut self,
i: usize,
y: Arc<DVector>,
index: I,
tx: ResultSender<I, Self::Primal, Self::Err>,
) -> Result<(), Self::Err>;
}