LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
ctmc_memory_gate.m
1function [ok, msg] = ctmc_memory_gate(logNstates, force, verbose, safetyFraction)
2% CTMC_MEMORY_GATE Hardware-aware, profiling-calibrated CTMC memory pre-gate.
3%
4% [ok,msg] = ctmc_memory_gate(logNstates, force, verbose, safetyFraction)
5%
6% Decides whether a CTMC steady-state solve of a state space of worst-case
7% size exp(logNstates) is safe on the current host. The budget is a fraction
8% of the memory actually available (see lineGetAvailableMemory); the per-state
9% cost is calibrated by profiling the local sparse LU once and caching the
10% fitted power law per machine in tempdir.
11%
12% ok is false only when the predicted peak footprint exceeds the budget and
13% force is not set. This replaces the historical hard-coded threshold.
14
15if nargin < 2 || isempty(force); force = false; end
16if nargin < 3 || isempty(verbose); verbose = false; end
17if nargin < 4 || isempty(safetyFraction); safetyFraction = 0.6; end
18
19avail = lineGetAvailableMemory();
20budget = safetyFraction * avail;
21calib = ctmc_get_calibration(verbose);
22
23logPred = log(calib.alpha_mem) + calib.beta_mem * logNstates;
24logBudget = log(max(budget, 1));
25predGB = exp(min(logPred, 700)) / 1024^3;
26budgetGB = budget / 1024^3;
27
28ok = true;
29msg = '';
30if logPred > logBudget
31 msg = sprintf(['CTMC predicted peak memory ~%.2f GB exceeds the safe ' ...
32 'budget ~%.2f GB (%.0f%% of %.2f GB available). Reduce the state ' ...
33 'space (e.g. lower ''cutoff''), use another solver (MVA/NC/FLD), or ' ...
34 'set force=true to override.'], predGB, budgetGB, ...
35 100*safetyFraction, avail/1024^3);
36 if ~force
37 ok = false;
38 return
39 end
40 if verbose
41 line_printf('Warning (forced): %s\n', msg);
42 end
43elseif verbose && logPred > log(max(0.5*budget, 1))
44 line_printf('CTMC predicted peak memory ~%.2f GB (budget ~%.2f GB).\n', ...
45 predGB, budgetGB);
46end
47end
48
49% ------------------------------------------------------------------------
50function calib = ctmc_get_calibration(verbose)
51% Return calibrated power-law coefficients {alpha_mem,beta_mem,alpha_t,beta_t}
52% for factor bytes and factorization time, using a per-machine tempdir cache.
53BYTES_PER_NZ = 16;
54G1 = 40; G2 = 80;
55FALLBACK_ALPHA = BYTES_PER_NZ * 8;
56FALLBACK_BETA = 1.3;
57
58sig = ctmc_machine_signature();
59cachefile = fullfile(tempdir, 'line_ctmc_calib_matlab.json');
60
61if exist(cachefile, 'file')
62 try
63 txt = fileread(cachefile);
64 data = jsondecode(txt);
65 if isfield(data, 'sig') && strcmp(data.sig, sig)
66 calib = data;
67 return
68 end
69 catch
70 end
71end
72
73try
74 [n1, b1, t1] = ctmc_profile_point(G1, BYTES_PER_NZ);
75 [n2, b2, t2] = ctmc_profile_point(G2, BYTES_PER_NZ);
76 [am, bm] = ctmc_fit_power_law(n1, b1, n2, b2);
77 [at, bt] = ctmc_fit_power_law(n1, max(t1,1e-9), n2, max(t2,1e-9));
78 calib = struct('sig', sig, 'alpha_mem', am, 'beta_mem', bm, ...
79 'alpha_t', at, 'beta_t', bt, 'timestamp', posixtime(datetime('now')));
80 try
81 fid = fopen(cachefile, 'w');
82 if fid > 0
83 fwrite(fid, jsonencode(calib));
84 fclose(fid);
85 end
86 catch
87 end
88 if verbose
89 line_printf('CTMC calibration: bytes ~ %.3g*N^%.3f\n', am, bm);
90 end
91catch ME
92 if verbose
93 line_printf('CTMC calibration failed (%s); using fallback model\n', ME.message);
94 end
95 calib = struct('sig', sig, 'alpha_mem', FALLBACK_ALPHA, ...
96 'beta_mem', FALLBACK_BETA, 'alpha_t', 0, 'beta_t', 1, 'timestamp', posixtime(datetime('now')));
97end
98end
99
100% ------------------------------------------------------------------------
101function sig = ctmc_machine_signature()
102try
103 ncores = feature('numcores');
104catch
105 ncores = 1;
106end
107sig = sprintf('%s|%d|%s', computer('arch'), ncores, computer);
108end
109
110% ------------------------------------------------------------------------
111function [n, bytes, secs] = ctmc_profile_point(g, bytesPerNz)
112% Factorize the nonsingular block of a g-by-g nearest-neighbour lattice
113% generator (n=g*g states). This QBD-like structure is representative of
114% multi-station queueing generators and cheap and safe to factorize.
115A = ctmc_lattice_generator(g);
116n = size(A, 1);
117t0 = tic;
118[L, U] = lu(A);
119secs = toc(t0);
120bytes = bytesPerNz * (nnz(L) + nnz(U));
121end
122
123% ------------------------------------------------------------------------
124function A = ctmc_lattice_generator(g)
125n = g * g;
126idx = reshape(1:n, g, g);
127src = []; dst = [];
128% right neighbour
129s = idx(:,1:end-1); d = idx(:,2:end);
130src = [src; s(:)]; dst = [dst; d(:)];
131% left neighbour
132s = idx(:,2:end); d = idx(:,1:end-1);
133src = [src; s(:)]; dst = [dst; d(:)];
134% down neighbour
135s = idx(1:end-1,:); d = idx(2:end,:);
136src = [src; s(:)]; dst = [dst; d(:)];
137% up neighbour
138s = idx(2:end,:); d = idx(1:end-1,:);
139src = [src; s(:)]; dst = [dst; d(:)];
140Q = sparse(src, dst, 1, n, n);
141rowsum = full(sum(Q, 2));
142Q = Q - spdiags(rowsum, 0, n, n);
143A = Q(1:n-1, 1:n-1); % drop last state -> nonsingular
144end
145
146% ------------------------------------------------------------------------
147function [alpha, beta] = ctmc_fit_power_law(x1, y1, x2, y2)
148if x1 <= 0 || x2 <= 0 || y1 <= 0 || y2 <= 0 || x1 == x2
149 error('ctmc_memory_gate:degenerateFit', 'degenerate power-law fit');
150end
151beta = log(y2 / y1) / log(x2 / x1);
152alpha = y1 / (x1 ^ beta);
153end
Definition Station.m:245