175 std::vector<double>* c,
double tolerance = 1e-10,
176 std::size_t max_iterations = 1000) {
177 const std::size_t n = in.
rows();
180 bool converged =
false;
181 double last_error = std::numeric_limits<double>::infinity();
182 for (std::size_t it = 0; it < max_iterations; ++it) {
183 for (std::size_t i = 0; i < n; ++i) {
185 for (std::size_t j = 0; j < n; ++j) s += in(i, j) * (*c)[j];
186 (*r)[i] = (s != 0.0) ? 1.0 / s : 0.0;
188 for (std::size_t j = 0; j < n; ++j) {
190 for (std::size_t i = 0; i < n; ++i) s += in(i, j) * (*r)[i];
191 (*c)[j] = (s != 0.0) ? 1.0 / s : 0.0;
194 for (std::size_t i = 0; i < n; ++i) {
196 for (std::size_t j = 0; j < n; ++j) s += in(i, j) * (*c)[j];
197 worst = std::max(worst, std::fabs((*r)[i] * s - 1.0));
199 if (worst < tolerance) {
206 throw InputError(
"sinkhorn_scaling: did not converge to a doubly stochastic matrix in " +
207 std::to_string(max_iterations) +
" sweeps (margin error " +
208 std::to_string(last_error) +
" against a tolerance of " +
209 std::to_string(tolerance) +
210 "). The usual cause is a matrix without total support.");
212 for (std::size_t i = 0; i < n; ++i)
213 for (std::size_t j = 0; j < n; ++j) (*B)(i, j) = (*r)[i] * in(i, j) * (*c)[j];
224 std::size_t max_iterations = 1000) {
225 approxdetail::require_nonnegative_square(m,
"perm_heur");
226 const std::size_t n = m.
rows();
232 std::vector<double> r, c;
235 std::vector<double> rowsum(n, 0.0);
236 for (std::size_t i = 0; i < n; ++i)
237 for (std::size_t j = 0; j < n; ++j) rowsum[i] += B(i, j);
239 const double nd =
static_cast<double>(n);
240 double rowprod = 1.0, logsum = 0.0;
241 for (std::size_t i = 0; i < n; ++i) {
242 rowprod *= rowsum[i];
243 logsum += std::log(rowsum[i]);
245 const double fact = approxdetail::factorial_d(n);
246 const double p_meanfield = fact * (rowprod / std::pow(nd, nd));
247 const double cap = std::exp(logsum / nd);
248 const double p_gurvits = fact * std::pow(cap / nd, nd);
249 const double p_est = 0.5 * (p_meanfield + p_gurvits);
252 for (std::size_t i = 0; i < n; ++i) scale *= 1.0 / r[i];
253 for (std::size_t j = 0; j < n; ++j) scale *= 1.0 / c[j];
254 return p_est * scale;
277 std::size_t max_iteration = 200000) {
278 approxdetail::require_nonnegative_square(m,
"perm_bethe");
279 const std::size_t n = m.
rows();
284 const double kMin = 2.220446049250314e-16;
288 for (std::size_t i = 0; i < n; ++i)
289 for (std::size_t j = 0; j < n; ++j) s(i, j) = std::sqrt(m(i, j));
297 for (std::size_t i = 0; i < n; ++i) {
299 for (std::size_t j = 0; j < n; ++j) d += s(i, j) * l(i, j);
300 d -= s(i, i) * l(i, i);
301 for (std::size_t j = 0; j < n; ++j) (*r1)(i, j) = (d != 0.0) ? s(i, j) / d : 0.0;
303 for (std::size_t j = 0; j < n; ++j) {
305 for (std::size_t i = 0; i < n; ++i) d += s(i, j) * (*r1)(i, j);
306 d -= s(j, j) * (*r1)(j, j);
307 for (std::size_t i = 0; i < n; ++i) (*l1)(i, j) = (d != 0.0) ? s(i, j) / d : 0.0;
312 update(l_past, &r, &l);
313 for (std::size_t it = 0; it < max_iteration; ++it) {
315 for (std::size_t i = 0; i < n; ++i)
316 for (std::size_t j = 0; j < n; ++j) {
317 const double a = r_past(i, j) - r(i, j), b = l_past(i, j) - l(i, j);
318 change += a * a + b * b;
320 if (change <= epsilon)
break;
323 update(l_past, &r, &l);
329 for (std::size_t i = 0; i < n; ++i) {
331 for (std::size_t j = 0; j < n; ++j) t += s(i, j) * l(i, j);
332 logv += std::log(std::max(t, kMin));
334 for (std::size_t j = 0; j < n; ++j) {
336 for (std::size_t i = 0; i < n; ++i) t += s(i, j) * r(i, j);
337 logv += std::log(std::max(t, kMin));
339 for (std::size_t i = 0; i < n; ++i)
340 for (std::size_t j = 0; j < n; ++j)
341 logv -= std::log(std::max(r(i, j) * l(i, j) + 1.0, kMin));
343 const double out = std::exp(logv);
345 return std::isfinite(out) ? out : 0.0;
411 const std::vector<std::size_t>& mult,
412 double tolerance = 1e-11,
413 std::size_t max_iterations = 10000) {
415 const std::size_t n = a.
rows();
416 const std::size_t h = a.
cols();
417 if (n == 0 || h == 0)
return out;
419 for (std::size_t i = 0; i < n; ++i)
420 for (std::size_t j = 0; j < h; ++j)
422 throw InputError(
"perm_spm: the matrix must be non-negative; entry (" +
423 std::to_string(i) +
", " + std::to_string(j) +
") is " +
424 std::to_string(a(i, j)));
426 std::vector<std::size_t> m = mult;
429 throw InputError(
"perm_spm: without column multiplicities the matrix must be square;"
430 " it is " + std::to_string(n) +
"x" + std::to_string(h));
434 throw InputError(
"perm_spm: the multiplicity vector has " + std::to_string(m.size()) +
435 " entries against " + std::to_string(h) +
" columns");
436 std::size_t total = 0;
437 for (std::size_t j = 0; j < h; ++j) total += m[j];
439 throw InputError(
"perm_spm: the column multiplicities must sum to the number of rows"
440 " (sum(m) = " + std::to_string(total) +
" against " + std::to_string(n) +
441 " rows). The integrand is homogeneous of degree " + std::to_string(n) +
442 ", so every other coefficient of it is exactly zero");
449 std::vector<std::size_t> keep;
450 for (std::size_t j = 0; j < h; ++j)
451 if (m[j] > 0) keep.push_back(j);
452 const std::size_t hk = keep.size();
453 std::vector<double> mk(hk);
454 for (std::size_t l = 0; l < hk; ++l) mk[l] = static_cast<double>(m[keep[l]]);
456 std::vector<double> ak(n * hk);
457 for (std::size_t i = 0; i < n; ++i)
458 for (std::size_t l = 0; l < hk; ++l) ak[i * hk + l] = a(i, keep[l]);
462 std::vector<double> xik(hk, 1.0), s(n, 0.0), colsum(hk, 0.0);
463 bool converged =
false;
464 double margin = std::numeric_limits<double>::infinity();
465 for (std::size_t it = 0; it < max_iterations && !converged; ++it) {
466 for (std::size_t i = 0; i < n; ++i) {
468 for (std::size_t l = 0; l < hk; ++l) acc += ak[i * hk + l] * xik[l];
472 for (std::size_t l = 0; l < hk; ++l) {
474 for (std::size_t i = 0; i < n; ++i) acc += ak[i * hk + l] / s[i];
475 colsum[l] = xik[l] * acc;
476 margin = std::max(margin, std::fabs(colsum[l] - mk[l]));
478 if (margin < tolerance) {
482 double logmean = 0.0;
483 for (std::size_t l = 0; l < hk; ++l) {
484 xik[l] *= mk[l] / colsum[l];
485 logmean += std::log(xik[l]);
487 logmean /=
static_cast<double>(hk);
488 const double scale = std::exp(logmean);
489 for (std::size_t l = 0; l < hk; ++l) xik[l] /= scale;
492 throw InputError(
"perm_spm: the scaling to row sums 1 and column sums m did not converge"
493 " in " + std::to_string(max_iterations) +
" sweeps (margin error " +
494 std::to_string(margin) +
" against a tolerance of " +
495 std::to_string(tolerance) +
"). The expansion assumes the saddle point,"
496 " so no value is returned. The usual cause is a matrix without total"
499 out.
xi.assign(h, 0.0);
500 for (std::size_t l = 0; l < hk; ++l) out.
xi[keep[l]] = xik[l];
502 for (std::size_t i = 0; i < n; ++i) {
504 for (std::size_t l = 0; l < hk; ++l) acc += ak[i * hk + l] * xik[l];
507 std::vector<double> p(n * hk);
508 for (std::size_t i = 0; i < n; ++i)
509 for (std::size_t l = 0; l < hk; ++l) p[i * hk + l] = ak[i * hk + l] * xik[l] / s[i];
511 double log_capacity = 0.0;
512 for (std::size_t i = 0; i < n; ++i) log_capacity += std::log(s[i]);
513 for (std::size_t l = 0; l < hk; ++l) log_capacity -= mk[l] * std::log(xik[l]);
520 double log_det = 0.0;
522 const std::size_t k = hk - 1;
523 std::vector<double> hred(k * k);
524 for (std::size_t l = 0; l < k; ++l)
525 for (std::size_t j = 0; j < k; ++j) {
527 for (std::size_t i = 0; i < n; ++i) dot += p[i * hk + l] * p[i * hk + j];
528 hred[l * k + j] = (l == j ? mk[l] : 0.0) - dot;
530 log_det = approxdetail::log_det_cholesky(hred, k,
"perm_spm");
533 double log_fact = 0.0;
534 for (std::size_t l = 0; l < hk; ++l) log_fact += std::lgamma(mk[l] + 1.0);
537 out.
log_value = log_fact - 0.5 *
static_cast<double>(hk - 1) * std::log(2.0 * M_PI) +
538 log_capacity - 0.5 * log_det;