LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
ctmc_memory_gate.h
Go to the documentation of this file.
1
#ifndef LINE_API_MC_CTMC_MEMORY_GATE_H
2
#define LINE_API_MC_CTMC_MEMORY_GATE_H
3
4
/**
5
* @file
6
* @ingroup api_mc
7
* Host-aware memory pre-gate for SolverCTMC.
8
*
9
* Port of `matlab/src/api/mc/ctmc_memory_gate.m`, mirrored by the JAR
10
* (`MemoryGuard.gate`) and native Python (`ctmc_memory_gate`).
11
*
12
* THE POLICY IS REFUSAL, NOT A WARNING. When the predicted peak exceeds the
13
* safe budget the solve is REFUSED with an error naming the two numbers, and
14
* only an explicit `force` downgrades that to a warning. A warning the caller
15
* can walk past is the wrong contract here: the failure mode it precedes is the
16
* OOM killer taking the whole process, which produces no diagnostic at all and,
17
* observed on 2026-08-01, took down five sessions and the terminal hosting
18
* them. Refusing costs the caller a re-run with another solver; not refusing
19
* costs it everything running on the machine.
20
*
21
* MDD AND CFTP ARE NEVER GATED. Both are served by their own analyzers and
22
* return before any explicit-generator path, so the gate is not on their route.
23
* That exemption is load-bearing rather than incidental: `mdd` holds the
24
* reachable set in a decision diagram whose size is governed by the diagram's
25
* compression, not by the state count, so it routinely solves models this
26
* estimator scores at exp(200). Gating on explicit size would clamp exactly the
27
* method that exists to beat it. Keep any new non-explicit method on the same
28
* side of the gate.
29
*
30
* The predictor is the power law of the reference, `bytes = alpha * N^beta`,
31
* evaluated in log space. MATLAB and Python calibrate alpha/beta once per host
32
* by profiling a sparse LU and cache the fit; this port uses the reference's
33
* FALLBACK coefficients unconditionally, which is the same branch those two
34
* take when calibration cannot run. That is deliberate: the fallback is the
35
* conservative end of the fitted range, it is deterministic across hosts and
36
* runs, and a gate that silently changes its verdict after a background
37
* profiling step is worse than one that is slightly pessimistic.
38
*/
39
40
#include <cmath>
41
#include <sstream>
42
#include <string>
43
44
#if defined(__unix__) || defined(__APPLE__)
45
#include <unistd.h>
46
#endif
47
48
namespace
line
{
49
namespace
mc
{
50
51
/// 8 bytes of value plus 8 amortized for the index, per stored nonzero.
52
constexpr
double
CTMC_BYTES_PER_NZ
= 16.0;
53
/// Fraction of available memory the solver may target.
54
constexpr
double
CTMC_DEFAULT_SAFETY_FRACTION
= 0.6;
55
/// Fallback power-law coefficients, identical to MATLAB and Python.
56
constexpr
double
CTMC_FALLBACK_ALPHA
=
CTMC_BYTES_PER_NZ
* 8.0;
57
constexpr
double
CTMC_FALLBACK_BETA
= 1.3;
58
/// Conservative available-memory default when the host probe fails.
59
constexpr
double
CTMC_FALLBACK_AVAIL_BYTES
= 1.0 * 1024.0 * 1024.0 * 1024.0;
60
61
/** The gate verdict, plus the message the caller reports either way. */
62
struct
CtmcGateResult
{
63
bool
ok
=
true
;
64
std::string
message
;
65
double
predicted_gb
= 0.0;
66
double
budget_gb
= 0.0;
67
};
68
69
/**
70
* Available physical memory in bytes.
71
*
72
* Never throws: a probe failure returns the conservative fallback, because a
73
* gate that cannot measure the host must not thereby become permissive.
74
*/
75
inline
double
ctmc_available_memory_bytes
() {
76
#if defined(_SC_AVPHYS_PAGES) && defined(_SC_PAGE_SIZE)
77
const
long
pages = ::sysconf(_SC_AVPHYS_PAGES);
78
const
long
page_size = ::sysconf(_SC_PAGE_SIZE);
79
if
(pages > 0 && page_size > 0)
80
return
static_cast<
double
>
(pages) *
static_cast<
double
>
(page_size);
81
#endif
82
return
CTMC_FALLBACK_AVAIL_BYTES
;
83
}
84
85
/**
86
* Decide whether a state space of log-size `log_nstates` can be solved here.
87
*
88
* @param log_nstates natural log of the worst-case state count
89
* @param force true downgrades a refusal to a warning
90
* @param safety_fraction fraction of available memory the solve may target
91
*/
92
inline
CtmcGateResult
ctmc_memory_gate
(
double
log_nstates,
bool
force =
false
,
93
double
safety_fraction =
CTMC_DEFAULT_SAFETY_FRACTION
) {
94
CtmcGateResult
res;
95
const
double
avail =
ctmc_available_memory_bytes
();
96
const
double
budget = safety_fraction * avail;
97
98
// Compared in LOG space: the predicted byte count of an intractable model
99
// overflows a double long before it exceeds the budget, and inf > x is a
100
// comparison that has already lost the margin it was meant to report.
101
const
double
log_pred = std::log(
CTMC_FALLBACK_ALPHA
) +
CTMC_FALLBACK_BETA
* log_nstates;
102
const
double
log_budget = std::log(budget > 1.0 ? budget : 1.0);
103
104
res.
predicted_gb
= std::exp(log_pred < 700.0 ? log_pred : 700.0) / (1024.0 * 1024.0 * 1024.0);
105
res.
budget_gb
= budget / (1024.0 * 1024.0 * 1024.0);
106
if
(log_pred <= log_budget)
return
res;
107
108
// An intractable model predicts a byte count with hundreds of digits, and
109
// printing it in full says nothing a magnitude does not. Above a petabyte
110
// the figure is reported as a power of ten.
111
std::ostringstream pred;
112
const
double
log10_gb = (log_pred - std::log(1024.0 * 1024.0 * 1024.0)) / std::log(10.0);
113
if
(log10_gb > 6.0) {
114
pred.setf(std::ios::fixed);
115
pred.precision(1);
116
pred <<
"1e"
<< log10_gb;
117
}
else
{
118
pred.setf(std::ios::fixed);
119
pred.precision(2);
120
pred << res.
predicted_gb
;
121
}
122
123
std::ostringstream os;
124
os.setf(std::ios::fixed);
125
os.precision(2);
126
os <<
"CTMC predicted peak memory ~"
<< pred.str() <<
" GB exceeds the safe budget ~"
127
<< res.
budget_gb
<<
" GB ("
<<
static_cast<
int
>
(100.0 * safety_fraction + 0.5) <<
"% of "
128
<< avail / (1024.0 * 1024.0 * 1024.0)
129
<<
" GB available). Reduce the state space (e.g. lower 'cutoff'), use the 'mdd' method, "
130
"use another solver (MVA/NC/FLD), or set force=true to override."
;
131
res.
message
= os.str();
132
res.
ok
= force;
133
return
res;
134
}
135
136
}
// namespace mc
137
}
// namespace line
138
139
#endif
// LINE_API_MC_CTMC_MEMORY_GATE_H
line::mc
Definition
ctmc_bicgstab.h:59
line::mc::CTMC_DEFAULT_SAFETY_FRACTION
constexpr double CTMC_DEFAULT_SAFETY_FRACTION
Fraction of available memory the solver may target.
Definition
ctmc_memory_gate.h:54
line::mc::CTMC_BYTES_PER_NZ
constexpr double CTMC_BYTES_PER_NZ
8 bytes of value plus 8 amortized for the index, per stored nonzero.
Definition
ctmc_memory_gate.h:52
line::mc::CTMC_FALLBACK_ALPHA
constexpr double CTMC_FALLBACK_ALPHA
Fallback power-law coefficients, identical to MATLAB and Python.
Definition
ctmc_memory_gate.h:56
line::mc::ctmc_available_memory_bytes
double ctmc_available_memory_bytes()
Available physical memory in bytes.
Definition
ctmc_memory_gate.h:75
line::mc::CTMC_FALLBACK_AVAIL_BYTES
constexpr double CTMC_FALLBACK_AVAIL_BYTES
Conservative available-memory default when the host probe fails.
Definition
ctmc_memory_gate.h:59
line::mc::ctmc_memory_gate
CtmcGateResult ctmc_memory_gate(double log_nstates, bool force=false, double safety_fraction=CTMC_DEFAULT_SAFETY_FRACTION)
Decide whether a state space of log-size log_nstates can be solved here.
Definition
ctmc_memory_gate.h:92
line::mc::CTMC_FALLBACK_BETA
constexpr double CTMC_FALLBACK_BETA
Definition
ctmc_memory_gate.h:57
line
Definition
aoi_dist2ph.h:52
line::mc::CtmcGateResult
The gate verdict, plus the message the caller reports either way.
Definition
ctmc_memory_gate.h:62
line::mc::CtmcGateResult::ok
bool ok
Definition
ctmc_memory_gate.h:63
line::mc::CtmcGateResult::message
std::string message
Definition
ctmc_memory_gate.h:64
line::mc::CtmcGateResult::predicted_gb
double predicted_gb
Definition
ctmc_memory_gate.h:65
line::mc::CtmcGateResult::budget_gb
double budget_gb
Definition
ctmc_memory_gate.h:66
include
line
api
mc
ctmc_memory_gate.h
Generated by
1.18.0