61 : storage_(storage), time_limit_(time_limit) {}
65 num_potential_subsets_ = 0;
66 num_potential_supersets_ = 0;
86 void SetWorkLimit(uint64_t work_limit) { work_limit_ = work_limit; }
102 const std::function<
void(
int subset,
int superset)>& process);
112 one_watcher_.clear();
113 is_in_superset_.resize(0);
130 const Storage& storage_;
143 bool CanBeSubset()
const {
return order <= 1; }
144 bool CanBeSuperset()
const {
return order >= 1; }
147 bool operator<(
const Candidate& other)
const {
148 return std::tie(size, order) < std::tie(other.size, other.order);
151 std::vector<Candidate> candidates_;
153 int num_potential_subsets_ = 0;
154 int num_potential_supersets_ = 0;
155 uint64_t work_done_ = 0;
156 uint64_t work_limit_ = std::numeric_limits<uint64_t>::max();
159 bool stop_with_current_subset_ =
false;
160 bool stop_with_current_superset_ =
false;
161 std::vector<uint64_t> signatures_;
162 std::vector<std::vector<int>> one_watcher_;
163 std::vector<int> superset_elements_;
164 Bitset64<int> is_in_superset_;
283 const std::function<
void(
int subset,
int superset)>& process) {
285 if (candidates_.size() <= 1)
return;
286 if (num_potential_subsets_ == 0)
return;
287 if (num_potential_supersets_ == 0)
return;
291 DCHECK(signatures_.empty());
292 DCHECK(one_watcher_.empty());
295 constexpr int64_t kCheckTimeLimitInterval = 1000;
296 int64_t next_time_limit_check = kCheckTimeLimitInterval;
300 std::stable_sort(candidates_.begin(), candidates_.end());
301 for (
const Candidate& candidate : candidates_) {
302 const auto& candidate_elements = storage_[candidate.index];
303 const int candidate_index = signatures_.size();
305 const auto [signature, max_element] =
307 signatures_.push_back(signature);
308 DCHECK_EQ(is_in_superset_.size(), one_watcher_.size());
309 if (max_element >= is_in_superset_.size()) {
310 is_in_superset_.resize(max_element + 1);
311 one_watcher_.resize(max_element + 1);
314 stop_with_current_superset_ =
false;
315 if (candidate.CanBeSuperset()) {
316 const Candidate& superset = candidate;
319 DCHECK(std::all_of(is_in_superset_.begin(), is_in_superset_.end(),
320 [](
bool b) { return !b; }));
323 work_done_ += 2 * superset.size;
324 if (work_done_ > work_limit_)
return Stop();
325 if (work_done_ > next_time_limit_check) {
326 if (time_limit_->LimitReached())
return Stop();
327 next_time_limit_check = work_done_ + kCheckTimeLimitInterval;
337 superset_elements_.assign(candidate_elements.begin(),
338 candidate_elements.end());
339 for (
const int e : superset_elements_) {
340 is_in_superset_.Set(e);
343 const uint64_t superset_signature = signatures_.back();
344 const auto is_in_superset_view = is_in_superset_.const_view();
345 for (
const int superset_e : superset_elements_) {
346 work_done_ += one_watcher_[superset_e].size();
347 for (
int i = 0;
i < one_watcher_[superset_e].size(); ++
i) {
348 const int c_index = one_watcher_[superset_e][
i];
349 const Candidate& subset = candidates_[c_index];
350 DCHECK_LE(subset.size, superset.size);
353 if ((signatures_[c_index] & ~superset_signature) != 0)
continue;
356 bool is_included =
true;
357 work_done_ += subset.size;
358 if (work_done_ > work_limit_)
return Stop();
359 if (work_done_ > next_time_limit_check) {
360 if (time_limit_->LimitReached())
return Stop();
361 next_time_limit_check = work_done_ + kCheckTimeLimitInterval;
363 for (
const int subset_e : storage_[subset.index]) {
364 if (!is_in_superset_view[subset_e]) {
369 if (!is_included)
continue;
371 stop_with_current_subset_ =
false;
372 process(subset.index, superset.index);
375 if (work_done_ > work_limit_)
return Stop();
376 if (work_done_ > next_time_limit_check) {
377 if (time_limit_->LimitReached())
return Stop();
378 next_time_limit_check = work_done_ + kCheckTimeLimitInterval;
381 if (stop_with_current_subset_) {
383 std::swap(one_watcher_[superset_e][
i],
384 one_watcher_[superset_e].back());
385 one_watcher_[superset_e].pop_back();
388 if (stop_with_current_superset_)
break;
390 if (stop_with_current_superset_)
break;
394 for (
const int e : superset_elements_) {
395 is_in_superset_.ClearBucket(e);
403 if (candidate.CanBeSubset() && !stop_with_current_superset_) {
405 int best_choice = -1;
406 work_done_ += candidate.size;
407 if (work_done_ > work_limit_)
return Stop();
408 for (
const int e : candidate_elements) {
410 DCHECK_LT(e, one_watcher_.size());
411 if (best_choice == -1 ||
412 one_watcher_[e].size() < one_watcher_[best_choice].size()) {
416 DCHECK_NE(best_choice, -1);
417 one_watcher_[best_choice].push_back(candidate_index);
482 absl::Span<const int> superset,
int* next_index_to_try,
483 const std::function<
void(
int subset)>&
process) {
485 constexpr int64_t kCheckTimeLimitInterval = 1000;
486 int64_t next_time_limit_check = kCheckTimeLimitInterval;
490 const auto [superset_signature, max_element] =
492 if (max_element >= is_in_superset_.size()) {
493 is_in_superset_.resize(max_element + 1);
497 work_done_ += 2 * superset.size();
498 if (work_done_ > work_limit_)
return Stop();
499 if (work_done_ > next_time_limit_check) {
500 if (time_limit_->LimitReached())
return Stop();
501 next_time_limit_check = work_done_ + kCheckTimeLimitInterval;
505 DCHECK(std::all_of(is_in_superset_.begin(), is_in_superset_.end(),
506 [](
bool b) { return !b; }));
507 for (
const int e : superset) {
508 is_in_superset_.Set(e);
511 stop_with_current_superset_ =
false;
512 const auto is_in_superset_view = is_in_superset_.const_view();
513 for (; *next_index_to_try < superset.size(); ++*next_index_to_try) {
514 const int superset_e = superset[*next_index_to_try];
515 if (superset_e >= one_watcher_.size())
continue;
516 auto cached_span = one_watcher_[superset_e];
517 for (
int i = 0;
i < cached_span.size(); ++
i) {
522 const auto [subset_index, other_e, subset_signature] = cached_span[
i];
523 if ((subset_signature & ~superset_signature) != 0)
continue;
524 if (!is_in_superset_view[other_e])
continue;
527 const absl::Span<const int> subset = storage_[subset_index];
528 if (subset.size() > superset.size())
continue;
535 bool is_included =
true;
536 work_done_ += subset.size();
537 if (work_done_ > work_limit_)
return Stop();
538 if (work_done_ > next_time_limit_check) {
539 if (time_limit_->LimitReached())
return Stop();
540 next_time_limit_check = work_done_ + kCheckTimeLimitInterval;
542 for (
const int subset_e : subset) {
543 if (!is_in_superset_view[subset_e]) {
548 if (!is_included)
continue;
550 stop_with_current_subset_ =
false;
556 if (work_done_ > work_limit_)
return Stop();
557 if (work_done_ > next_time_limit_check) {
558 if (time_limit_->LimitReached())
return Stop();
559 next_time_limit_check = work_done_ + kCheckTimeLimitInterval;
562 if (stop_with_current_subset_) {
563 one_watcher_.RemoveBySwap(superset_e,
i);
564 cached_span.remove_suffix(1);
567 if (stop_with_current_superset_)
break;
569 if (stop_with_current_superset_)
break;
573 for (
const int e : superset) {
574 is_in_superset_.ClearBucket(e);