Skip to content

Commit 86ccbd6

Browse files
committed
Implement general activation condition combination logic.
1 parent c793a3a commit 86ccbd6

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

ift/encoder/activation_condition.cc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "ift/encoder/activation_condition.h"
22

3+
#include <algorithm>
4+
35
#include "absl/container/btree_set.h"
46
#include "absl/container/flat_hash_map.h"
57
#include "absl/status/status.h"
@@ -71,6 +73,75 @@ ActivationCondition ActivationCondition::composite_condition(
7173
return conditions;
7274
}
7375

76+
static void Simplify(std::vector<SegmentSet>& conditions) {
77+
if (conditions.size() <= 1) return;
78+
79+
// Conditions can be simplified by removing duplicate and/or subset sub-conditions.
80+
// For example if one sub-condition is a subset of another ((s1) AND (s1 or s2)),
81+
// then the larger sub-condition (s1 or s2) is not necessary and can be dropped.
82+
83+
// Sort by size to ensure that if A is a subset of B, then A comes before B.
84+
std::sort(conditions.begin(), conditions.end(),
85+
[](const SegmentSet& a, const SegmentSet& b) {
86+
return a.size() < b.size();
87+
});
88+
89+
std::vector<uint8_t> redundant(conditions.size(), 0);
90+
for (size_t i = 0; i < conditions.size(); ++i) {
91+
if (redundant[i]) continue;
92+
for (size_t j = i + 1; j < conditions.size(); ++j) {
93+
if (redundant[j]) continue;
94+
if (conditions[i].is_subset_of(conditions[j])) {
95+
redundant[j] = 1;
96+
}
97+
}
98+
}
99+
100+
size_t write_index = 0;
101+
for (size_t i = 0; i < conditions.size(); ++i) {
102+
if (redundant[i]) {
103+
continue;
104+
}
105+
if (write_index != i) {
106+
conditions[write_index] = std::move(conditions[i]);
107+
}
108+
write_index++;
109+
}
110+
conditions.erase(conditions.begin() + write_index, conditions.end());
111+
112+
// Final sort to normalize the order of conditions.
113+
std::sort(conditions.begin(), conditions.end());
114+
}
115+
116+
ActivationCondition ActivationCondition::And(const ActivationCondition& a,
117+
const ActivationCondition& b) {
118+
ActivationCondition condition = a;
119+
condition.conditions_.insert(condition.conditions_.end(),
120+
b.conditions().begin(), b.conditions().end());
121+
Simplify(condition.conditions_);
122+
condition.is_exclusive_ = false;
123+
return condition;
124+
}
125+
126+
ActivationCondition ActivationCondition::Or(const ActivationCondition& a,
127+
const ActivationCondition& b) {
128+
ActivationCondition condition = a;
129+
condition.conditions_.clear();
130+
condition.conditions_.reserve(a.conditions().size() * b.conditions().size());
131+
132+
for (const auto& a_group : a.conditions()) {
133+
for (const auto& b_group : b.conditions()) {
134+
SegmentSet combined_group = a_group;
135+
combined_group.union_set(b_group);
136+
condition.conditions_.push_back(std::move(combined_group));
137+
}
138+
}
139+
140+
Simplify(condition.conditions_);
141+
condition.is_exclusive_ = false;
142+
return condition;
143+
}
144+
74145
std::string ActivationCondition::ToString() const {
75146
std::stringstream out;
76147
out << "if (";

ift/encoder/activation_condition.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ class ActivationCondition {
5151
static ActivationCondition composite_condition(
5252
absl::Span<const ift::common::SegmentSet> groups, patch_id_t activated);
5353

54+
// Returns a new activation condition that activates on (a && b)
55+
//
56+
// The new condition uses the values for the other fields
57+
// from condition a (eg. activated, encoding).
58+
static ActivationCondition And(const ActivationCondition& a,
59+
const ActivationCondition& b);
60+
61+
// Returns a new activation condition that activates on (a || b)
62+
//
63+
// The new condition uses the values for the other fields
64+
// from condition a (eg. activated, encoding).
65+
static ActivationCondition Or(const ActivationCondition& a,
66+
const ActivationCondition& b);
67+
5468
/*
5569
* Converts a list of activation conditions into a list of condition entries
5670
* which are used by the encoder to specify conditions.

ift/encoder/activation_condition_test.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,62 @@ TEST(ActivationConditionTest, MergedProbability) {
373373
.status()));
374374
}
375375

376+
TEST(ActivationConditionTest, And) {
377+
auto a = ActivationCondition::or_segments({1, 2}, 10);
378+
auto b = ActivationCondition::or_segments({3}, 11);
379+
380+
auto combined_ab = ActivationCondition::And(a, b);
381+
EXPECT_EQ(combined_ab.ToString(), "if ((s1 OR s2) AND s3) then p10");
382+
EXPECT_EQ(combined_ab.activated(), 10);
383+
EXPECT_FALSE(combined_ab.IsExclusive());
384+
EXPECT_FALSE(combined_ab.IsFallback());
385+
386+
auto combined_ba = ActivationCondition::And(b, a);
387+
EXPECT_EQ(combined_ab.conditions(), combined_ba.conditions());
388+
EXPECT_EQ(combined_ba.activated(), 11);
389+
}
390+
391+
TEST(ActivationConditionTest, And_Simplification) {
392+
auto a = ActivationCondition::or_segments({1, 2}, 10);
393+
auto b = ActivationCondition::or_segments({1}, 10);
394+
395+
auto combined_ab = ActivationCondition::And(a, b);
396+
EXPECT_EQ(combined_ab.ToString(), "if (s1) then p10");
397+
398+
// common elements but not subsets, no simplification
399+
auto c = ActivationCondition::or_segments({1, 2}, 10);
400+
auto d = ActivationCondition::or_segments({1, 3}, 10);
401+
auto combined_cd = ActivationCondition::And(c, d);
402+
EXPECT_EQ(combined_cd.ToString(), "if ((s1 OR s2) AND (s1 OR s3)) then p10");
403+
}
404+
405+
TEST(ActivationConditionTest, Or) {
406+
auto a = ActivationCondition::and_segments({1, 2}, 10);
407+
auto b = ActivationCondition::and_segments({3, 4}, 11);
408+
409+
auto combined_ab = ActivationCondition::Or(a, b);
410+
EXPECT_EQ(combined_ab.ToString(),
411+
"if ((s1 OR s3) AND (s1 OR s4) AND (s2 OR s3) AND (s2 OR s4)) "
412+
"then p10");
413+
EXPECT_EQ(combined_ab.activated(), 10);
414+
EXPECT_FALSE(combined_ab.IsExclusive());
415+
EXPECT_FALSE(combined_ab.IsFallback());
416+
417+
auto combined_ba = ActivationCondition::Or(b, a);
418+
EXPECT_EQ(combined_ba.activated(), 11);
419+
EXPECT_EQ(combined_ab.conditions(), combined_ba.conditions());
420+
}
421+
422+
TEST(ActivationConditionTest, Or_Simplification) {
423+
auto a = ActivationCondition::and_segments({1, 2}, 10);
424+
auto b = ActivationCondition::and_segments({2, 3}, 10);
425+
426+
auto combined_ab = ActivationCondition::Or(a, b);
427+
EXPECT_EQ(combined_ab.ToString(), "if ((s1 OR s3) AND s2) then p10");
428+
EXPECT_EQ(combined_ab.activated(), 10);
429+
430+
auto combined_ba = ActivationCondition::Or(b, a);
431+
EXPECT_EQ(combined_ab.conditions(), combined_ba.conditions());
432+
}
433+
376434
} // namespace ift::encoder

0 commit comments

Comments
 (0)