Program Listing for File cutting_plane.hpp

Return to documentation for file (ellcpp/cutting_plane.hpp)

// -*- coding: utf-8 -*-
#pragma once

#include "cut_config.hpp"
#include "half_nonnegative.hpp"
#include <cassert>
#include <cmath>
#include <tuple>


template <typename Oracle, typename Space>
auto cutting_plane_feas(
    Oracle&& Omega, Space&& S, const Options& options = Options()) -> CInfo
{
    auto feasible = false;
    auto status = CUTStatus::success;

    auto niter = 0U;
    while (++niter != options.max_it)
    {
        const auto cut = Omega(S.xc()); // query the oracle at S.xc()
        if (!cut)
        { // feasible sol'n obtained
            feasible = true;
            break;
        }
        const auto [cutstatus, tsq] = S.update(*cut); // update S
        if (cutstatus != CUTStatus::success)
        {
            status = cutstatus;
            break;
        }
        if (tsq < options.tol)
        { // no more
            status = CUTStatus::smallenough;
            break;
        }
    }
    return {feasible, niter, status};
}

template <typename Oracle, typename Space, typename opt_type>
auto cutting_plane_dc(
    Oracle&& Omega, Space&& S, opt_type&& t, const Options& options = Options())
{
    const auto t_orig = t;
    decltype(S.xc()) x_best;
    auto status = CUTStatus::success;

    auto niter = 0U;
    while (++niter != options.max_it)
    {
        const auto [cut, shrunk] = Omega(S.xc(), t);
        if (shrunk)
        { // best t obtained
            x_best = S.xc();
        }
        const auto [cutstatus, tsq] = S.update(cut);
        if (cutstatus != CUTStatus::success) // ???
        {
            status = cutstatus;
            break;
        }
        if (tsq < options.tol)
        { // no more
            status = CUTStatus::smallenough;
            break;
        }
    }
    return std::make_tuple(
        std::move(x_best), CInfo {t != t_orig, niter, status});
} // END

// #include <boost/numeric/ublas/symmetric.hpp>
// namespace bnu = boost::numeric::ublas;
// #include <xtensor-blas/xlinalg.hpp>
// #include <xtensor/xarray.hpp>

template <typename Oracle, typename Space, typename opt_type>
auto cutting_plane_q(
    Oracle&& Omega, Space&& S, opt_type&& t, const Options& options = Options())
{
    const auto t_orig = t;
    decltype(S.xc()) x_best;
    auto status = CUTStatus::nosoln; // note!!!

    auto niter = 0U;
    while (++niter != options.max_it)
    {
        auto retry = (status == CUTStatus::noeffect);
        const auto [cut, x0, shrunk, more_alt] = Omega(S.xc(), t, retry);
        if (shrunk)
        { // best t obtained
            // t = t1;
            x_best = x0;
        }
        const auto [cutstatus, tsq] = S.update(cut);
        if (cutstatus == CUTStatus::noeffect)
        {
            if (!more_alt)
            {
                break; // no more alternative cut
            }
        }
        if (cutstatus == CUTStatus::nosoln)
        {
            status = cutstatus;
            break;
        }
        if (tsq < options.tol)
        {
            status = CUTStatus::smallenough;
            break;
        }
    }
    return std::make_tuple(
        std::move(x_best), CInfo {t != t_orig, niter, status});
} // END

template <typename Oracle, typename Space>
auto bsearch(Oracle&& Omega, Space&& I, const Options& options = Options())
    -> CInfo
{
    // assume monotone
    // auto& [lower, upper] = I;
    auto& lower = I.first;
    auto& upper = I.second;
    assert(lower <= upper);
    const auto u_orig = upper;
    auto niter = 0U;
    auto status = CUTStatus::success;

    for (; niter != options.max_it; ++niter)
    {
        auto tau = algo::half_nonnegative(upper - lower);
        if (tau < options.tol)
        {
            status = CUTStatus::smallenough;
            break;
        }

        auto t = lower; // l may be `int` or `Fraction`
        t += tau;
        if (Omega(t))
        { // feasible sol'n obtained
            upper = t;
        }
        else
        {
            lower = t;
        }
    }
    return {upper != u_orig, niter + 1, status};
}

template <typename Oracle, typename Space> //
class bsearch_adaptor
{
  private:
    Oracle& _P;
    Space& _S;
    const Options _options;

  public:
    bsearch_adaptor(Oracle& P, Space& S)
        : bsearch_adaptor {P, S, Options()}
    {
    }

    bsearch_adaptor(Oracle& P, Space& S, const Options& options)
        : _P {P}
        , _S {S}
        , _options {options}
    {
    }

    [[nodiscard]] auto x_best() const
    {
        return this->_S.xc();
    }

    template <typename opt_type>
    auto operator()(const opt_type& t) -> bool
    {
        Space S = this->_S.copy();
        this->_P.update(t);
        const auto ell_info = cutting_plane_feas(this->_P, S, this->_options);
        if (ell_info.feasible)
        {
            this->_S.set_xc(S.xc());
        }
        return ell_info.feasible;
    }
};