EllAlgo 1.6.13
Loading...
Searching...
No Matches
cutting_plane.hpp
Go to the documentation of this file.
1
6#pragma once
7
8#include <cassert>
9#include <cmath>
10#include <tuple>
11#include <type_traits>
12
13#include "ell_config.hpp"
14#include "half_nonnegative.hpp"
15
16template <typename S> using CuttingPlaneArrayType = typename std::remove_reference_t<S>::ArrayType;
17
27template <typename T> inline auto invalid_value() -> T
28 requires std::is_floating_point_v<T>
29{
30 return std::nan("1");
31}
32
39template <typename T> inline auto invalid_value() -> T
40 requires(!std::is_floating_point_v<T>)
41{
42 return T{};
43}
44
103template <typename O, typename S>
104 requires OracleFeas<O, typename S::ArrayType> && SearchSpace<S>
105inline auto cutting_plane_feas(O& omega, S& space, const Options& options = Options())
106 -> std::tuple<CuttingPlaneArrayType<S>, size_t> {
107 for (auto niter = 0U; niter != options.max_iters; ++niter) {
108 const auto cut = omega.assess_feas(space.xc());
109 if (!cut) { // feasible sol'n obtained
110 return {space.xc(), niter};
111 }
112 const auto status = space.update_bias_cut(*cut); // update space
113 if (status != CutStatus::Success || space.tsq() < options.tolerance) {
115 return {std::move(res), niter};
116 }
117 }
119 return {std::move(res), options.max_iters};
120}
121
173template <typename O, typename S, typename N>
176 -> std::tuple<CuttingPlaneArrayType<S>, size_t> {
178 for (auto niter = 0U; niter < options.max_iters; ++niter) {
179 const auto _result1 = omega.assess_optim(space.xc(), gamma);
180 const auto& cut = std::get<0>(_result1);
181 const auto& shrunk = std::get<1>(_result1);
182 const auto status = [&]() {
183 if (shrunk) { // best gamma obtained
184 x_best = space.xc();
185 return space.update_central_cut(cut); // should update_central_cut
186 }
187 return space.update_bias_cut(cut);
188 }();
189 if (status != CutStatus::Success || space.tsq() < options.tolerance) { // no more
190 return {std::move(x_best), niter};
191 }
192 }
193 return {std::move(x_best), options.max_iters};
194} // END
195
211template <typename A> class OptimQState {
212 public:
213 enum class Result { Continue, NoSoln, NoMoreAlt };
214
215 private:
216 A _x_best;
217 bool _retry = false;
218
219 public:
225 explicit OptimQState(A invalid) : _x_best{std::move(invalid)} {}
226
228 auto x_best() const -> const A& { return this->_x_best; }
229
231 auto x_best() -> A& { return this->_x_best; }
232
234 auto retry() const -> bool { return this->_retry; }
235
241 void on_shrunk(A x) {
242 this->_x_best = std::move(x);
243 this->_retry = false;
244 }
245
253 auto on_update(const CutStatus status, const bool more_alt) -> Result {
254 switch (status) {
256 this->_retry = false;
257 return Result::Continue;
259 return Result::NoSoln;
261 if (more_alt) {
262 this->_retry = true;
263 return Result::Continue;
264 }
265 return Result::NoMoreAlt;
267 return Result::Continue;
268 }
269 return Result::Continue;
270 }
271};
272
299template <typename O, typename S, typename N>
302 const Options& options = Options())
303 -> std::tuple<CuttingPlaneArrayType<S>, size_t> {
304 using A = CuttingPlaneArrayType<S>;
306
307 for (auto niter = 0U; niter < options.max_iters; ++niter) {
308 const auto result1 = omega.assess_optim_q(space_q.xc(), gamma, state.retry());
309 const auto& cut = std::get<0>(result1);
310 const auto& shrunk = std::get<1>(result1);
311 if (shrunk) { // best gamma obtained
312 state.on_shrunk(std::move(std::get<2>(result1)));
313 }
314 const auto outcome = state.on_update(space_q.update_q(cut), std::get<3>(result1));
316 return {std::move(state.x_best()), niter};
317 }
319 break; // no more alternative cut
320 }
321 if (space_q.tsq() < options.tolerance) { // no more
322 return {std::move(state.x_best()), niter};
323 }
324 }
325 return {std::move(state.x_best()), options.max_iters};
326} // END
327
342template <typename Oracle, typename Space> //
344 using ArrayType = typename Space::ArrayType;
345
346 Oracle* _omega;
347 Space* _space;
348 Options _options;
349
350 public:
358
367 : _omega{&omega}, _space{&space}, _options{options} {}
368
374 auto x_best() const -> ArrayType { return this->_space->xc(); }
375
383 template <typename Num> auto assess_bs(Num& gamma) -> bool {
384 Space space = this->_space->copy(); // copy
385 this->_omega->update(gamma);
386 const auto result = cutting_plane_feas(*this->_omega, space, this->_options);
387 auto x_feas = std::get<0>(result);
388 if (x_feas.size() != 0U) {
389 this->_space->set_xc(x_feas);
390 return true;
391 }
392 return false;
393 }
394};
395
411template <typename O, typename T>
412 requires OracleBS<O, T>
413inline auto bsearch(O& omega, const std::pair<T, T>& intvl, const Options& options = Options())
414 -> std::tuple<T, size_t> {
415 // assume monotone
416 // auto& [lower, upper] = intvl;
417 auto lower = intvl.first;
418 auto upper = intvl.second;
419 assert(lower <= upper);
420
421 for (auto niter = 0U; niter < options.max_iters; ++niter) {
423 if (tau < options.tolerance) { // no more
424 return {upper, niter};
425 }
426 auto gamma = lower; // l may be `int` or `Fraction`
427 gamma += tau;
428 if (omega.assess_bs(gamma)) { // feasible sol'n obtained
429 upper = gamma;
430 } else {
431 lower = gamma;
432 }
433 }
434 return {upper, options.max_iters};
435}
Binary search adaptor wrapping a cutting-plane feasibility oracle.
Definition cutting_plane.hpp:343
BSearchAdaptor(Oracle &omega, Space &space)
Construct a new bsearch adaptor object.
Definition cutting_plane.hpp:357
BSearchAdaptor(Oracle &omega, Space &space, const Options &options)
Construct a new bsearch adaptor object.
Definition cutting_plane.hpp:366
auto assess_bs(Num &gamma) -> bool
Definition cutting_plane.hpp:383
auto x_best() const -> ArrayType
Get the best x value.
Definition cutting_plane.hpp:374
State machine for the discrete cutting-plane method.
Definition cutting_plane.hpp:211
auto on_update(const CutStatus status, const bool more_alt) -> Result
Transition on the space update result.
Definition cutting_plane.hpp:253
OptimQState(A invalid)
Construct a new OptimQState object.
Definition cutting_plane.hpp:225
Result
Definition cutting_plane.hpp:213
auto x_best() -> A &
Get the best-so-far solution (mutable).
Definition cutting_plane.hpp:231
auto retry() const -> bool
Whether the next assessment is a retry (reuse cached point).
Definition cutting_plane.hpp:234
void on_shrunk(A x)
Transition on a newly obtained (shrunk) best solution.
Definition cutting_plane.hpp:241
auto x_best() const -> const A &
Get the best-so-far solution.
Definition cutting_plane.hpp:228
auto invalid_value() -> T
Return an invalid/sentinel value for type T.
Definition cutting_plane.hpp:27
auto cutting_plane_feas(O &omega, S &space, const Options &options=Options()) -> std::tuple< CuttingPlaneArrayType< S >, size_t >
Find a point in a convex set (defined through a cutting-plane oracle).
Definition cutting_plane.hpp:105
auto cutting_plane_optim_q(O &omega, S &space_q, N &gamma, const Options &options=Options()) -> std::tuple< CuttingPlaneArrayType< S >, size_t >
Cutting-plane method for solving convex discrete optimization problem.
Definition cutting_plane.hpp:301
auto bsearch(O &omega, const std::pair< T, T > &intvl, const Options &options=Options()) -> std::tuple< T, size_t >
Binary search using a cutting-plane oracle.
Definition cutting_plane.hpp:413
auto cutting_plane_optim(O &omega, S &space, N &gamma, const Options &options=Options()) -> std::tuple< CuttingPlaneArrayType< S >, size_t >
Cutting-plane method for solving convex problem.
Definition cutting_plane.hpp:175
typename std::remove_reference_t< S >::ArrayType CuttingPlaneArrayType
Definition cutting_plane.hpp:16
Configuration types and constants for the ellipsoid algorithm.
CutStatus
Status of cutting plane operations.
Definition ell_config.hpp:47
@ NoSoln
No solution exists (infeasible)
@ Success
Cut was successful and ellipsoid was updated.
@ Unknown
Unknown status.
@ NoEffect
Cut had no effect on ellipsoid.
Utility functions for computing half of non-negative numbers.
auto half_nonnegative(N n) noexcept -> typename std::enable_if_t< std::is_integral< N >::value, N >
Compute half of a non-negative integral number.
Definition half_nonnegative.hpp:43
Configuration options for the ellipsoid algorithm.
Definition ell_config.hpp:19