231 enum class Mode { Classic, Time, Sample };
242 double maximum_time = 30000.0,
int maximum_samples = 450,
246 maximum_accepted_samples_(maximum_accepted_samples),
247 maximum_time_(maximum_time),
248 maximum_samples_(maximum_samples),
252 samplingdetail::require_square_nonnegative(matrix_,
"AdaPartSampler");
253 if (n_ > 0) samplingdetail::require_full_support(matrix_,
"AdaPartSampler");
258 const double z_ub = samplingdetail::soules_bound(matrix_);
259 long accepted = 0, total = 0;
260 const std::clock_t start = std::clock();
264 const long kMaxDraws = 1000000;
266 if (mode_ ==
Mode::Classic && accepted >= maximum_accepted_samples_)
break;
268 throw InputError(
"perm_adapart: only " + std::to_string(accepted) +
" of the " +
269 std::to_string(maximum_accepted_samples_) +
270 " required acceptances were obtained in " +
271 std::to_string(total) +
272 " draws. Use the exact engine.");
273 if (mode_ ==
Mode::Sample && total >= maximum_samples_)
break;
274 if (mode_ ==
Mode::Time && elapsed_ms(start) >= maximum_time_)
break;
275 accepted += sample(start);
278 value_ = (total > 0) ? z_ub *
static_cast<double>(accepted) /
static_cast<double>(total)
284 double value()
const {
return value_; }
287 typedef std::vector<std::size_t> Assignment;
289 static double elapsed_ms(std::clock_t start) {
290 return 1000.0 *
static_cast<double>(std::clock() - start) / CLOCKS_PER_SEC;
293 bool within_time(std::clock_t start)
const {
295 return elapsed_ms(start) < maximum_time_;
299 bool any_free(
const std::set<Assignment>& s_set)
const {
300 for (std::set<Assignment>::const_iterator it = s_set.begin(); it != s_set.end(); ++it)
301 if (has_free(*it))
return true;
305 bool has_free(
const Assignment& t)
const {
306 for (std::size_t j = 0; j < t.size(); ++j)
307 if (t[j] == n_)
return true;
312 Matrix<double> modify_matrix(
const Matrix<double>& m,
const Assignment& t)
const {
313 Matrix<double> out(n_, n_, 0.0);
314 std::vector<bool> row_used(n_,
false);
315 for (std::size_t j = 0; j < t.size(); ++j)
316 if (t[j] != n_) row_used[t[j]] =
true;
317 for (std::size_t j = 0; j < t.size(); ++j) {
319 out(t[j], j) = m(t[j], j);
321 for (std::size_t i = 0; i < n_; ++i)
322 if (!row_used[i]) out(i, j) = m(i, j);
333 std::size_t select_column(
const Matrix<double>& s_matrix,
double removed_ub,
double ub,
334 const Assignment& s_sub,
double* new_ub)
const {
335 double best = std::numeric_limits<double>::infinity();
336 std::size_t best_col = 0;
338 for (std::size_t i = 0; i < n_; ++i) {
339 if (s_sub[i] != n_)
continue;
341 for (std::size_t j = 0; j < n_; ++j) {
342 Assignment a(n_, n_);
344 total += samplingdetail::soules_bound(modify_matrix(s_matrix, a));
346 if (!found || total < best) {
356 *new_ub = ub - removed_ub + best;
361 std::size_t compute_probabilities(
const std::set<Assignment>& s_set,
double zub_s) {
362 std::vector<double> p;
363 p.reserve(s_set.size() + 1);
365 for (std::set<Assignment>::const_iterator it = s_set.begin(); it != s_set.end(); ++it) {
366 const double b = samplingdetail::soules_bound(modify_matrix(matrix_, *it));
370 if (sum > 0.0 && zub_s > 0.0) {
372 for (std::size_t i = 0; i < p.size(); ++i) {
376 p.push_back(1.0 - norm);
381 for (std::size_t i = 0; i < p.size(); ++i) total += std::fabs(p[i]);
383 for (std::size_t i = 0; i < p.size(); ++i) p[i] = std::fabs(p[i]) / total;
385 const double u = uniform_();
387 for (std::size_t i = 0; i < p.size(); ++i) {
389 if (u <= cum)
return i;
395 std::set<Assignment> subset(
const std::set<Assignment>& s_set, std::size_t c)
const {
396 std::set<Assignment>::const_iterator it = s_set.begin();
397 std::advance(it,
static_cast<long>(c));
398 Assignment s_inter = *it;
399 std::size_t free_count = 0, free_pos = 0;
400 for (std::size_t j = 0; j < s_inter.size(); ++j)
401 if (s_inter[j] == n_) {
405 if (free_count == 1) {
406 std::vector<bool> used(n_,
false);
407 for (std::size_t j = 0; j < s_inter.size(); ++j)
408 if (s_inter[j] != n_) used[s_inter[j]] =
true;
409 for (std::size_t v = 0; v < n_; ++v)
411 s_inter[free_pos] = v;
415 std::set<Assignment> out;
421 int sample(std::clock_t start) {
422 std::set<Assignment> s_set;
423 s_set.insert(Assignment(n_, n_));
424 while (any_free(s_set) && within_time(start)) {
425 const Assignment s_init = *s_set.begin();
426 const double zub_s = samplingdetail::soules_bound(modify_matrix(matrix_, s_init));
429 while ((ub >= zub_s || init) && within_time(start)) {
433 std::vector<Assignment> s_list;
434 for (std::set<Assignment>::const_iterator it = s_set.begin(); it != s_set.end();
436 if (has_free(*it)) s_list.push_back(*it);
437 if (s_list.empty())
break;
438 const std::size_t pick =
439 static_cast<std::size_t
>(uniform_() *
static_cast<double>(s_list.size()));
440 const Assignment s_sub = s_list[pick < s_list.size() ? pick : s_list.size() - 1];
442 const Matrix<double> sub_matrix = modify_matrix(matrix_, s_sub);
445 const double sub_ub = samplingdetail::soules_bound(sub_matrix);
447 const std::size_t j = select_column(sub_matrix, sub_ub, ub, s_sub, &new_ub);
448 for (std::size_t i = 0; i < n_; ++i) {
450 for (std::size_t k = 0; k < s_sub.size(); ++k)
451 if (s_sub[k] == i) taken =
true;
453 Assignment s_add = s_sub;
457 const bool no_progress = new_ub >= ub;
463 if (no_progress)
break;
465 const std::size_t c = compute_probabilities(s_set, zub_s);
466 if (c == s_set.size())
return 0;
467 s_set = subset(s_set, c);
473 return std::generate_canonical<double, 53>(rng_);
476 Matrix<double> matrix_;
478 int maximum_accepted_samples_;
479 double maximum_time_;
480 int maximum_samples_;
482 std::mt19937_64 rng_;
495 enum class Mode { Classic, Time, Sample };
508 double alpha2 = 0.000001,
double epsilon = 0.1,
510 double maximum_time = 30000.0, std::uint64_t seed = 0)
517 number_of_samples_(number_of_samples),
518 maximum_time_(maximum_time),
520 c_matrix_(matrix.rows(), matrix.cols(), 0.0),
521 rescaling_constant_(1.0),
523 samplingdetail::require_square_nonnegative(matrix_,
"HuberLawSampler");
524 if (n_ > 0) samplingdetail::require_full_support(matrix_,
"HuberLawSampler");
530 const long k =
static_cast<long>(14.0 * std::pow(delta_, -2.0) *
531 std::log(2.0 / epsilon_));
532 const std::clock_t start = std::clock();
533 long accepted = 0, total = 0;
535 const long kMaxDraws = 1000000;
539 throw InputError(
"perm_huberlaw: only " + std::to_string(accepted) +
" of the " +
541 " required acceptances were obtained in " +
542 std::to_string(total) +
543 " draws. Relax delta or use the exact engine.");
544 if (mode_ ==
Mode::Sample && total >= number_of_samples_)
break;
545 if (mode_ ==
Mode::Time && elapsed_ms(start) >= maximum_time_)
break;
546 accepted += sample();
549 value_ = (total > 0) ?
static_cast<double>(accepted) /
static_cast<double>(total) *
556 double value()
const {
return value_; }
559 static double elapsed_ms(std::clock_t start) {
560 return 1000.0 *
static_cast<double>(std::clock() - start) / CLOCKS_PER_SEC;
564 static double h(
double r) {
565 if (r >= 1.0)
return r + 0.5 * std::log(r) + M_E - 1.0;
566 return 1.0 + (M_E - 1.0) * r;
570 std::vector<double> precomputing(
const Matrix<double>& m, std::size_t j)
const {
571 std::vector<double> hr(n_, 0.0), c(n_, 0.0);
572 double hr_product = 1.0;
573 for (std::size_t i = 0; i < n_; ++i) {
576 for (std::size_t k = 0; k < n_; ++k) rowsum += m(i, k);
577 hr[i] = h(rowsum - c[i]);
580 const double exp_factor = std::exp(
static_cast<double>(n_) - 1.0);
581 std::vector<double> out(n_, 0.0);
582 for (std::size_t i = 0; i < n_; ++i)
583 out[i] = (hr[i] != 0.0) ? hr_product / hr[i] * c[i] / exp_factor : 0.0;
589 Matrix<double> m = c_matrix_;
590 for (std::size_t j = 0; j < n_; ++j) {
591 std::vector<double> p = precomputing(m, j);
592 double rowprod = 1.0;
593 for (std::size_t i = 0; i < n_; ++i) {
595 for (std::size_t k = 0; k < n_; ++k) rowsum += m(i, k);
596 rowprod *= h(rowsum);
598 const double ub = rowprod / std::exp(
static_cast<double>(n_));
599 std::vector<double> prob(n_ + 1, 0.0);
601 for (std::size_t i = 0; i < n_; ++i) {
602 prob[i] = (ub > 0.0) ? p[i] / ub : 0.0;
605 prob[n_] = 1.0 - psum;
606 if (prob[n_] < 0.0) {
608 for (std::size_t i = 0; i < n_; ++i) prob[i] /= psum;
611 const double u = uniform_();
612 std::size_t selected = n_;
614 for (std::size_t i = 0; i <= n_; ++i) {
621 if (selected == n_)
return 0;
622 Matrix<double> next(n_, n_, 0.0);
623 for (std::size_t a = 0; a < n_; ++a)
624 for (std::size_t b = 0; b < n_; ++b)
625 if (a != selected && b != j) next(a, b) = m(a, b);
626 next(selected, j) = m(selected, j);
634 Matrix<double> make_doubly_stochastic(
const Matrix<double>& m, std::vector<double>* x,
635 std::vector<double>* y)
const {
636 Matrix<double> result = m;
639 double max_row_error = std::numeric_limits<double>::infinity();
640 double max_col_error = std::numeric_limits<double>::infinity();
644 const std::size_t kMaxSweeps = 10000;
645 std::size_t sweeps = 0;
646 while (max_row_error > alpha2_ || max_col_error > alpha2_) {
647 if (++sweeps > kMaxSweeps)
649 "make_doubly_stochastic: did not converge in " +
650 std::to_string(kMaxSweeps) +
" sweeps (row error " +
651 std::to_string(max_row_error) +
", column error " +
652 std::to_string(max_col_error) +
" against a tolerance of " +
653 std::to_string(alpha2_) +
654 "). The usual cause is a matrix without total support.");
655 for (std::size_t j = 0; j < n_; ++j) {
657 for (std::size_t i = 0; i < n_; ++i) s += result(i, j);
659 for (std::size_t i = 0; i < n_; ++i) result(i, j) /= s;
663 for (std::size_t i = 0; i < n_; ++i) {
665 for (std::size_t j = 0; j < n_; ++j) s += result(i, j);
667 for (std::size_t j = 0; j < n_; ++j) result(i, j) /= s;
673 for (std::size_t j = 0; j < n_; ++j) {
675 for (std::size_t i = 0; i < n_; ++i) s += result(i, j);
676 max_col_error = std::max(max_col_error, std::fabs(s - 1.0));
678 for (std::size_t i = 0; i < n_; ++i) {
680 for (std::size_t j = 0; j < n_; ++j) s += result(i, j);
681 max_row_error = std::max(max_row_error, std::fabs(s - 1.0));
689 Matrix<double> log_matrix(n_, n_, 0.0);
690 double max_element = 0.0;
691 for (std::size_t i = 0; i < n_; ++i)
692 for (std::size_t j = 0; j < n_; ++j) {
694 log_matrix(i, j) = std::log(matrix_(i, j));
695 max_element = std::max(max_element, matrix_(i, j));
697 if (!(max_element > 0.0))
698 throw InputError(
"HuberLawSampler: the matrix is identically zero");
700 const std::vector<std::size_t> assignment =
701 samplingdetail::max_weight_assignment(log_matrix);
703 Matrix<double> m_scaled(n_, n_, 0.0);
704 for (std::size_t i = 0; i < n_; ++i)
705 for (std::size_t j = 0; j < n_; ++j) m_scaled(i, j) = matrix_(i, j) / max_element;
709 for (std::size_t i = 0; i < n_; ++i) alpha3 *= m_scaled(i, assignment[i]);
710 const double alpha1 = alpha3 * delta_ / 3.0 / samplingdetail::factorial_plain(n_);
711 for (std::size_t i = 0; i < n_; ++i)
712 for (std::size_t j = 0; j < n_; ++j)
713 m_scaled(i, j) = std::max(m_scaled(i, j), alpha1);
715 std::vector<double> x, y;
716 const Matrix<double> ds = make_doubly_stochastic(m_scaled, &x, &y);
718 std::vector<double> z(n_, 1.0);
719 for (std::size_t i = 0; i < n_; ++i) {
721 for (std::size_t j = 0; j < n_; ++j) rowmax = std::max(rowmax, ds(i, j));
722 z[i] = (rowmax > 0.0) ? 1.0 / rowmax : 1.0;
724 for (std::size_t i = 0; i < n_; ++i)
725 for (std::size_t j = 0; j < n_; ++j) c_matrix_(i, j) = z[i] * ds(i, j);
727 double h_product = 1.0;
728 for (std::size_t i = 0; i < n_; ++i) {
730 for (std::size_t j = 0; j < n_; ++j) rowsum += c_matrix_(i, j);
731 h_product *= h(rowsum) / M_E;
733 double diagonal_product = 1.0;
734 for (std::size_t i = 0; i < n_; ++i) diagonal_product *= x[i] * y[i] * z[i];
735 rescaling_constant_ = h_product / diagonal_product *
736 std::pow(max_element,
static_cast<double>(n_));
740 return std::generate_canonical<double, 53>(rng_);
743 Matrix<double> matrix_;
749 int number_of_samples_;
750 double maximum_time_;
751 std::mt19937_64 rng_;
752 Matrix<double> c_matrix_;
753 double rescaling_constant_;