5#ifndef LINE_API_RETRIEVAL_RETRIEVAL_RAYINT_H
6#define LINE_API_RETRIEVAL_RETRIEVAL_RAYINT_H
101Matrix<T> rayint_hessian(
const Matrix<T>& g,
const std::vector<T>& w,
const std::vector<T>& xi) {
102 const std::size_t K = g.
rows();
103 const std::size_t h = xi.size();
107 for (std::size_t k = 0; k < K; ++k) {
109 for (std::size_t j = 0; j < h; ++j) {
110 a[j] = g(k, j) * xi[j];
113 for (std::size_t j = 0; j < h; ++j) {
114 const T aj = a[j] / s;
115 hess(j, j) += w[k] * aj;
116 for (std::size_t l = 0; l < h; ++l) hess(j, l) -= w[k] * aj * (a[l] / s);
124std::vector<T> rayint_solve(
const Matrix<T>& a,
const std::vector<T>& b) {
126 const std::size_t h = b.size();
128 for (std::size_t i = 0; i < h; ++i) {
129 for (std::size_t j = 0; j < h; ++j) m(i, j) = a(i, j);
132 for (std::size_t c = 0; c < h; ++c) {
134 for (std::size_t i = c + 1; i < h; ++i)
135 if (abs(m(i, c)) > abs(m(piv, c))) piv = i;
136 if (num_traits<T>::to_double(abs(m(piv, c))) == 0.0)
137 throw NumericError(
"retrieval_rayint: the saddle-point Hessian is singular; the ray map "
138 "is degenerate here");
140 for (std::size_t j = 0; j <= h; ++j) {
141 const T tmp = m(c, j);
145 for (std::size_t i = c + 1; i < h; ++i) {
146 const T f = m(i, c) / m(c, c);
147 for (std::size_t j = c; j <= h; ++j) m(i, j) -= f * m(c, j);
151 for (std::size_t ii = h; ii-- > 0;) {
153 for (std::size_t j = ii + 1; j < h; ++j) s -= m(ii, j) * x[j];
154 x[ii] = s / m(ii, ii);
161T rayint_logdet(
const Matrix<T>& a) {
164 const std::size_t h = a.rows();
165 const T half = num_traits<T>::from_rational(1, 2);
166 const T two = num_traits<T>::from_int(2);
167 Matrix<T> l(h, h, num_traits<T>::from_int(0));
168 T ld = num_traits<T>::from_int(0);
169 for (std::size_t i = 0; i < h; ++i) {
170 for (std::size_t j = 0; j <= i; ++j) {
171 T s = half * (a(i, j) + a(j, i));
172 for (std::size_t k = 0; k < j; ++k) s -= l(i, k) * l(j, k);
174 if (num_traits<T>::to_double(s) <= 0.0)
175 throw NumericError(
"retrieval_rayint: the saddle-point Hessian is not positive "
176 "definite; the ray map is singular here");
178 ld += two * log(l(i, j));
180 l(i, j) = s / l(j, j);
193std::vector<T> rayint_saddle(
const Matrix<T>& g,
const std::vector<T>& w,
194 const std::vector<T>& tgt, std::size_t& iterations) {
198 const std::size_t K = g.rows();
199 const std::size_t h = tgt.size();
200 const T one = num_traits<T>::from_int(1);
202 T wsum = num_traits<T>::from_int(0);
203 for (std::size_t k = 0; k < K; ++k) wsum += w[k];
204 T tsum = num_traits<T>::from_int(0);
205 for (std::size_t j = 0; j < h; ++j) tsum += tgt[j];
206 T slack = one - tsum / wsum;
207 const T tiny = num_traits<T>::from_double(1e-9);
208 if (num_traits<T>::to_double(slack) < 1e-9) slack = tiny;
210 std::vector<T> th(h);
211 for (std::size_t j = 0; j < h; ++j) {
212 T gb = num_traits<T>::from_int(0);
213 for (std::size_t k = 0; k < K; ++k) gb += w[k] * g(k, j);
214 th[j] = log(tgt[j] / (gb * slack));
218 for (std::size_t j = 0; j < h; ++j)
219 tmax = std::max(tmax, std::abs(num_traits<T>::to_double(tgt[j])));
221 std::vector<T> xi(h);
223 for (it = 1; it <= 200; ++it) {
224 for (std::size_t j = 0; j < h; ++j) xi[j] = exp(th[j]);
225 std::vector<T> grad(h, num_traits<T>::from_int(0));
226 for (std::size_t k = 0; k < K; ++k) {
228 for (std::size_t j = 0; j < h; ++j) s += g(k, j) * xi[j];
229 for (std::size_t j = 0; j < h; ++j) grad[j] += w[k] * g(k, j) * xi[j] / s;
232 for (std::size_t j = 0; j < h; ++j) {
234 gmax = std::max(gmax, std::abs(num_traits<T>::to_double(grad[j])));
236 if (gmax <= 1e-12 * tmax)
break;
237 const Matrix<T> hess = rayint_hessian(g, w, xi);
238 std::vector<T> d = rayint_solve(hess, grad);
240 for (std::size_t j = 0; j < h; ++j) {
242 dmax = std::max(dmax, std::abs(num_traits<T>::to_double(d[j])));
246 while (stepd * dmax > 2.0) {
248 step = step / num_traits<T>::from_int(2);
250 for (std::size_t j = 0; j < h; ++j) th[j] += step * d[j];
253 for (std::size_t j = 0; j < h; ++j) xi[j] = exp(th[j]);
259T rayint_logfact(
int x) {
261 T s = num_traits<T>::from_int(0);
262 for (
int i = 2; i <= x; ++i) s += log(num_traits<T>::from_int(i));
268RetrievalRayintResult<T> rayint_run(
const Matrix<T>* gmat,
269 const std::function<Matrix<T>(
const std::vector<T>&)>* gfun,
270 int n,
const std::vector<int>& m, std::size_t nquad) {
273 const std::size_t h = m.size();
274 if (h == 0)
throw InputError(
"retrieval_rayint: the capacity vector must not be empty");
277 for (std::size_t j = 0; j < h; ++j) {
278 if (m[j] < 0)
throw InputError(
"retrieval_rayint: list capacities must be non-negative");
281 if (gmat !=
nullptr && gmat->cols() != h)
282 throw InputError(
"retrieval_rayint: gamma and m disagree on the number of lists");
284 RetrievalRayintResult<T> out;
285 out.xi.assign(h, num_traits<T>::from_int(0));
286 out.logdet_sigma = num_traits<T>::from_int(0);
289 out.e = num_traits<T>::from_int(0);
290 out.log_e = num_traits<T>::from_double(-std::numeric_limits<double>::infinity());
291 out.method =
"boundary";
295 out.e = num_traits<T>::from_int(1);
296 out.log_e = num_traits<T>::from_int(0);
297 out.method =
"boundary";
301 throw InputError(
"retrieval_rayint: the expansion requires sum(m) < n; at sum(m) = n the "
302 "saddle point escapes to infinity, use cache_erec for a full cache");
305 std::vector<std::size_t> keep;
306 for (std::size_t j = 0; j < h; ++j)
307 if (m[j] > 0) keep.push_back(j);
308 const std::size_t hk = keep.size();
312 std::vector<T> tgt(hk);
313 const bool discrete = (gmat !=
nullptr);
315 const std::size_t K = gmat->rows();
317 for (std::size_t k = 0; k < K; ++k)
318 for (std::size_t a = 0; a < hk; ++a) g(k, a) = (*gmat)(k, keep[a]);
319 w.assign(K, num_traits<T>::from_int(1));
320 for (std::size_t a = 0; a < hk; ++a) tgt[a] = num_traits<T>::from_int(m[keep[a]]);
322 const std::size_t K = nquad;
324 for (std::size_t k = 0; k < K; ++k)
325 v[k] = num_traits<T>::from_rational(
static_cast<long>(k),
static_cast<long>(K - 1));
326 const Matrix<T> gfull = (*gfun)(v);
327 if (gfull.rows() != K)
328 throw InputError(
"retrieval_rayint: the profile must return one row per evaluation point");
329 if (gfull.cols() != h)
330 throw InputError(
"retrieval_rayint: the profile must return one column per cache list");
332 for (std::size_t k = 0; k < K; ++k)
333 for (std::size_t a = 0; a < hk; ++a) g(k, a) = gfull(k, keep[a]);
334 w.assign(K, num_traits<T>::from_int(0));
335 for (std::size_t k = 0; k < K; ++k) {
336 const long c = (k == 0 || k == K - 1) ? 1 : ((k % 2 == 1) ? 4 : 2);
337 w[k] = num_traits<T>::from_rational(c,
static_cast<long>(3 * (K - 1)));
339 for (std::size_t a = 0; a < hk; ++a)
340 tgt[a] = num_traits<T>::from_rational(m[keep[a]], n);
343 std::size_t iters = 0;
344 const std::vector<T> xi = detail::rayint_saddle(g, w, tgt, iters);
346 const std::size_t K = g.rows();
347 const T one = num_traits<T>::from_int(1);
349 for (std::size_t k = 0; k < K; ++k) {
351 for (std::size_t a = 0; a < hk; ++a) acc += g(k, a) * xi[a];
354 const Matrix<T> hess = detail::rayint_hessian(g, w, xi);
355 const T logdet = detail::rayint_logdet(hess);
357 const T half = num_traits<T>::from_rational(1, 2);
358 const T log2pi = num_traits<T>::from_double(std::log(2.0 * M_PI));
359 const T hkT = num_traits<T>::from_int(
static_cast<long>(hk));
363 T phi = num_traits<T>::from_int(0);
364 for (std::size_t k = 0; k < K; ++k) phi += log(s[k]);
365 for (std::size_t a = 0; a < hk; ++a) phi -= tgt[a] * log(xi[a]);
366 log_et = -half * hkT * log2pi + phi - half * logdet;
367 out.method =
"saddle";
369 T phi = num_traits<T>::from_int(0);
370 for (std::size_t k = 0; k < K; ++k) phi += w[k] * log(s[k]);
371 for (std::size_t a = 0; a < hk; ++a) phi -= tgt[a] * log(xi[a]);
372 const T nT = num_traits<T>::from_int(n);
373 log_et = -half * hkT * log(nT) - half * hkT * log2pi + nT * phi - half * logdet +
374 half * log(s[K - 1] / s[0]);
375 out.method =
"rayint";
378 T logfact = num_traits<T>::from_int(0);
379 for (std::size_t j = 0; j < h; ++j) logfact += detail::rayint_logfact<T>(m[j]);
380 out.log_e = log_et + logfact;
383 out.e = exp(out.log_e);
385 for (std::size_t a = 0; a < hk; ++a) out.xi[keep[a]] = xi[a];
386 out.logdet_sigma = logdet;
387 out.iterations = iters;
388 int mmin = m[keep[0]];
389 for (std::size_t a = 1; a < hk; ++a) mmin = std::min(mmin, m[keep[a]]);
390 out.relerr_est = 0.14 * (1.0 /
static_cast<double>(mmin) +
391 1.0 /
static_cast<double>(n - msum));
406 "retrieval_rayint is a Laplace approximation built out of logs, exps and a "
407 "square root: it is meaningless at exact arithmetic, and widening the type "
408 "sharpens the saddle solve but never the O(1/n) model error. Use cache_erec "
409 "or retrieval_nc for the exact constant");
410 if (gamma.
rows() == 0)
throw InputError(
"retrieval_rayint: gamma must be a non-empty n x h matrix");
411 return detail::rayint_run<T>(&gamma,
nullptr,
static_cast<int>(gamma.
rows()), m, 0);
424 const std::vector<int>& m,
int n,
425 std::size_t nquad = 4097) {
427 "retrieval_rayint is a Laplace approximation built out of logs, exps and a "
428 "square root: it is meaningless at exact arithmetic. Use cache_erec or "
429 "retrieval_nc for the exact constant");
430 const std::size_t nq = std::max<std::size_t>(5, 2 * (nquad / 2) + 1);
431 return detail::rayint_run<T>(
nullptr, &gfun, n, m, nq);
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
RetrievalRayintResult< T > retrieval_rayint(const Matrix< T > &gamma, const std::vector< int > &m)
Discrete (saddle) form.
Number-type abstraction for the templated API port.
Outcome of the expansion.
std::size_t iterations
Newton iterations used.
T logdet_sigma
log det of the Hessian in log xi.
double relerr_est
Estimated relative error, 0.14*(1/min_j m_j + 1/(n - sum_j m_j)).
T e
Normalizing constant, same normalization as cache_erec (may overflow; use log_e).
T log_e
Natural logarithm of e, safe for large n.
std::vector< T > xi
Saddle point xi_j, one entry per list (0 for a list of zero capacity).
std::string method
"saddle", "rayint" or "boundary".