EllAlgo 1.6.13
Loading...
Searching...
No Matches
ell_core.hpp
Go to the documentation of this file.
1
6// -*- coding: utf-8 -*-
7#pragma once
8
9#include <limits>
10#include <utility>
11#include <valarray>
12
13#include "ell_calc.hpp"
14#include "ell_config.hpp"
15#include "ell_matrix.hpp"
16
44class EllCore {
45 using Vec = std::valarray<double>;
46
47 size_t _n;
48 double _kappa;
49 Matrix _mq;
50 EllCalc _helper;
51 double _tsq{};
52 // Pre-allocated scratch buffers (Rust-style: eliminate per-call allocation)
53 Vec _scratch; // w = L^{-1}g (stable) / grad_t (non-stable)
54 Vec _z; // z = D^{-1}w (stable)
55 Vec _v; // q = L^{-T}z then v (stable rank-1 update)
56
57 public:
58 bool no_defer_trick = false;
59
60 private:
62 auto operator=(const EllCore& E) -> EllCore& = delete;
63
71 EllCore(double kappa, Matrix&& mq, size_t ndim)
72 : _n{ndim},
73 _kappa{kappa},
74 _mq{std::move(mq)},
75 _helper{_n},
76 _scratch(0.0, ndim),
77 _z(0.0, ndim),
78 _v(0.0, ndim) {}
79
80 public:
90 EllCore(const Vec& val, size_t ndim) : EllCore{1.0, Matrix(ndim), ndim} {
91 this->_mq.diagonal() = val;
92 }
93
105 EllCore(const double alpha, const size_t ndim) : EllCore{alpha, Matrix(ndim), ndim} {
106 this->_mq.identity();
107 }
108
118 EllCore(EllCore&& E) = default;
119
123 EllCore& operator=(EllCore&&) = default;
124
129 ~EllCore() = default;
130
138 explicit EllCore(const EllCore& E) = default;
139
145 auto copy() const -> EllCore { return EllCore(*this); }
146
152 constexpr auto tsq() const -> double { return this->_tsq; }
153
160 void set_use_parallel_cut(bool value) { this->_helper.use_parallel_cut = value; }
161
173 template <typename T> auto update_bias_cut(Vec& grad, const T& beta) -> CutStatus {
174 return this->_update_core(grad, beta, [this](const T& beta_l, const double tsq_l) {
175 return this->_update_cut_bias_cut(beta_l, tsq_l);
176 });
177 }
178
190 template <typename T> auto update_central_cut(Vec& grad, const T& beta) -> CutStatus {
191 return this->_update_core(grad, beta, [this](const T& beta_l, const double tsq_l) {
192 return this->_update_cut_central_cut(beta_l, tsq_l);
193 });
194 }
195
207 template <typename T> auto update_q(Vec& grad, const T& beta) -> CutStatus {
208 return this->_update_core(grad, beta, [this](const T& beta_l, const double tsq_l) {
209 return this->_update_cut_q(beta_l, tsq_l);
210 });
211 }
212
224 template <typename T> auto update_stable_bias_cut(Vec& grad, const T& beta) -> CutStatus {
225 return this->_update_stable_core(grad, beta, [this](const T& beta_l, const double tsq_l) {
226 return this->_update_cut_bias_cut(beta_l, tsq_l);
227 });
228 }
229
241 template <typename T> auto update_stable_central_cut(Vec& grad, const T& beta) -> CutStatus {
242 return this->_update_stable_core(grad, beta, [this](const T& beta_l, const double tsq_l) {
243 return this->_update_cut_central_cut(beta_l, tsq_l);
244 });
245 }
246
258 template <typename T> auto update_stable_q(Vec& grad, const T& beta) -> CutStatus {
259 return this->_update_stable_core(grad, beta, [this](const T& beta_l, const double tsq_l) {
260 return this->_update_cut_q(beta_l, tsq_l);
261 });
262 }
263
264 private:
289 template <typename T, typename Fn>
290 auto _update_core(Vec& grad, const T& beta, Fn&& cut_strategy) -> CutStatus {
291 // reuse _v as grad_t = M * g (pre-allocated, no allocation)
292 this->_v = 0.0;
293 for (size_t i = 0; i != this->_n; ++i) {
294 for (size_t j = 0; j != this->_n; ++j) {
295 this->_v[i] += this->_mq(i, j) * grad[j];
296 }
297 }
298
299 const auto omega = (this->_v * grad).sum();
300 this->_tsq = this->_kappa * omega;
301
302 if (omega <= std::numeric_limits<double>::min()) {
303 grad = this->_v;
304 return CutStatus::NoEffect;
305 }
306
307 auto result = std::forward<Fn>(cut_strategy)(beta, this->_tsq);
308 if (result.status != CutStatus::Success) {
309 return result.status;
310 }
311
312 // n (n+1) / 2 + n
313 const auto r = result.sigma / omega;
314 for (size_t i = 0; i != this->_n; ++i) {
315 const auto rQg = r * this->_v[i];
316 for (size_t j = 0; j != i; ++j) {
317 this->_mq(i, j) -= rQg * this->_v[j];
318 this->_mq(j, i) = this->_mq(i, j);
319 }
320 this->_mq(i, i) -= rQg * this->_v[i];
321 }
322
323 this->_kappa *= result.delta;
324
325 if (this->no_defer_trick) {
326 this->_mq *= this->_kappa;
327 this->_kappa = 1.0;
328 }
329
330 grad = this->_v * (result.rho / omega);
331 return result.status;
332 }
333
354 template <typename T, typename Fn>
355 auto _update_stable_core(Vec& g, const T& beta, Fn&& cut_strategy) -> CutStatus {
356 // _scratch = w = L^{-1}g (forward substitution)
357 this->_scratch = g;
358 for (size_t j = 0; j != this->_n - 1; ++j) {
359 for (size_t i = j + 1; i != this->_n; ++i) {
360 this->_mq(j, i) = this->_mq(i, j) * this->_scratch[j];
361 this->_scratch[i] -= this->_mq(j, i);
362 }
363 }
364
365 // _z = D^{-1} * w
366 this->_z = this->_scratch;
367 for (size_t i = 0; i != this->_n; ++i) {
368 this->_z[i] *= this->_mq(i, i);
369 }
370
371 // omega = sum(w_i * z_i)
372 auto omega = 0.0;
373 for (size_t i = 0; i != this->_n; ++i) {
374 omega += this->_z[i] * this->_scratch[i];
375 }
376
377 this->_tsq = this->_kappa * omega;
378
379 if (omega <= std::numeric_limits<double>::min()) {
380 return CutStatus::NoEffect;
381 }
382
383 auto result = std::forward<Fn>(cut_strategy)(beta, this->_tsq);
384 if (result.status != CutStatus::Success) {
385 return result.status;
386 }
387
388 // _v = grad_t = L^{-T} * z (back substitution) — preserved for final g
389 this->_v = this->_z;
390 for (auto i = this->_n - 1; i != 0; --i) {
391 for (auto j = i; j != this->_n; ++j) {
392 this->_v[i - 1] -= this->_mq(j, i - 1) * this->_v[j];
393 }
394 }
395
396 // rank-one LDL^T update — reuse _scratch as working vector v
397 // (_scratch (w) no longer needed; overwrite with gradient)
398 const auto mu = result.sigma / (1.0 - result.sigma);
399 auto oldt = omega / mu;
400 this->_scratch = g; // v = gradient
401 for (size_t j = 0; j != this->_n; ++j) {
402 const auto p = this->_scratch[j];
403 const auto temp = this->_z[j];
404 const auto newt = oldt + p * temp;
405 const auto beta2 = temp / newt;
406 this->_mq(j, j) *= oldt / newt;
407 for (auto k = j + 1; k != this->_n; ++k) {
408 this->_scratch[k] -= this->_mq(j, k);
409 this->_mq(k, j) += beta2 * this->_scratch[k];
410 }
411 oldt = newt;
412 }
413 this->_kappa *= result.delta;
414 // _v still holds grad_t = L^{-T}*z (preserved from back substitution)
415 g = this->_v * (result.rho / omega);
416 return result.status;
417 }
418
426 auto _update_cut_bias_cut(double beta, double tsq) const -> CutResult {
427 return this->_helper.calc_bias_cut(beta, tsq);
428 }
429
439 auto _update_cut_bias_cut(const std::valarray<double>& beta, double tsq) const -> CutResult {
440 if (beta.size() < 2) {
441 return this->_helper.calc_bias_cut(beta[0], tsq);
442 }
443 return this->_helper.calc_parallel_bias_cut(beta[0], beta[1], tsq);
444 }
445
452 auto _update_cut_central_cut(double /*unused*/, double tsq) const -> CutResult {
453 return this->_helper.calc_central_cut(tsq);
454 }
455
465 auto _update_cut_central_cut(const std::valarray<double>& beta, double tsq) const -> CutResult {
466 if (beta.size() < 2) {
467 return this->_helper.calc_central_cut(tsq);
468 }
469 return this->_helper.calc_parallel_central_cut(beta[1], tsq);
470 }
471
479 auto _update_cut_q(double beta, double tsq) const -> CutResult {
480 return this->_helper.calc_bias_cut_q(beta, tsq);
481 }
482
492 auto _update_cut_q(const std::valarray<double>& beta, double tsq) const -> CutResult {
493 if (beta.size() < 2) {
494 return this->_helper.calc_bias_cut_q(beta[0], tsq);
495 }
496 return this->_helper.calc_parallel_bias_cut_q(beta[0], beta[1], tsq);
497 }
498
499}; // } EllCore
double sum(const Arr &a)
Sum of all elements.
Definition arr.hpp:260
Ellipsoid Search Space.
Definition ell_calc.hpp:20
bool use_parallel_cut
Definition ell_calc.hpp:22
auto calc_central_cut(double tsq) const -> CutResult
Central cut.
auto calc_bias_cut_q(double beta, double tsq) const -> CutResult
Deep cut (Q-version for discrete optimization)
auto calc_parallel_central_cut(double beta1, double tsq) const -> CutResult
Parallel central cut.
auto calc_parallel_bias_cut(double beta0, double beta1, double tsq) const -> CutResult
Parallel deep cut.
auto calc_parallel_bias_cut_q(double beta0, double beta1, double tsq) const -> CutResult
Parallel deep cut (Q-version for discrete optimization)
auto calc_bias_cut(double beta, double tsq) const -> CutResult
Deep (bias) cut.
Ellipsoid Search Space Core.
Definition ell_core.hpp:44
EllCore(const Vec &val, size_t ndim)
Definition ell_core.hpp:90
auto update_q(Vec &grad, const T &beta) -> CutStatus
Update ellipsoid core function using the cut(s)
Definition ell_core.hpp:207
auto update_central_cut(Vec &grad, const T &beta) -> CutStatus
Update ellipsoid core function using the central cut(s)
Definition ell_core.hpp:190
auto update_stable_q(Vec &grad, const T &beta) -> CutStatus
Update ellipsoid core function using the cut(s)
Definition ell_core.hpp:258
EllCore(EllCore &&E)=default
Construct a new EllCore object.
auto update_stable_bias_cut(Vec &grad, const T &beta) -> CutStatus
Update ellipsoid core function using the deep cut(s)
Definition ell_core.hpp:224
EllCore & operator=(EllCore &&)=default
Move assignment operator.
bool no_defer_trick
Definition ell_core.hpp:58
auto copy() const -> EllCore
explicitly copy
Definition ell_core.hpp:145
auto update_bias_cut(Vec &grad, const T &beta) -> CutStatus
Update ellipsoid core function using the deep cut(s)
Definition ell_core.hpp:173
EllCore(const double alpha, const size_t ndim)
Construct a new EllCore object.
Definition ell_core.hpp:105
void set_use_parallel_cut(bool value)
Definition ell_core.hpp:160
auto update_stable_central_cut(Vec &grad, const T &beta) -> CutStatus
Update ellipsoid core function using the central cut(s)
Definition ell_core.hpp:241
EllCore(const EllCore &E)=default
Construct a new EllCore object.
~EllCore()=default
Destroy the EllCore object.
constexpr auto tsq() const -> double
Get the squared ellipsoid radius τ²
Definition ell_core.hpp:152
Square matrix with flat std::vector<double> storage.
Definition ell_matrix.hpp:63
void identity()
Set to identity matrix.
Definition ell_matrix.hpp:101
SliceView diagonal()
Mutable view of the diagonal (stride = ndim + 1)
Definition ell_matrix.hpp:108
auto invalid_value() -> T
Return an invalid/sentinel value for type T.
Definition cutting_plane.hpp:27
Ellipsoid computation layer with cut dispatch.
Configuration types and constants for the ellipsoid algorithm.
CutStatus
Status of cutting plane operations.
Definition ell_config.hpp:47
@ Success
Cut was successful and ellipsoid was updated.
@ NoEffect
Cut had no effect on ellipsoid.
Square matrix with flat storage and strided views.
Result of a cutting-plane calculation.
Definition ell_config.hpp:75