Skip to content

Commit 9c0122b

Browse files
authored
Searcher checkpoints (#21)
## Changes * `PostTagSearcher` now uses explicit checkpoints from `EvaluationParameters`. * The trie from explicit checkpoints is only constructed once and shared across all inputs. * There is a separate trie for automatic checkpoints which is unique to each input. ## Comments * We might want to share the automatic checkpoints trie in the future as well, but that will require storing checkpoints source in the trie itself (to distinguish between `"ReachedCycle"` and `"MergedWithAnotherInput"`) conclusions. ## Examples * The following produces 6 outputs, in which 0th and 3rd reach the provided checkpoint: ```c++ PostTagSearcher::EvaluationParameters parameters; parameters.checkpoints = {{{0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0}, 2}}; const auto result = PostTagSearcher().evaluateRange(20, 123, 125, parameters); ```
1 parent 0d8e681 commit 9c0122b

4 files changed

Lines changed: 101 additions & 47 deletions

File tree

libPostTagSystem/PostTagHistory.cpp

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,41 @@ class PostTagHistory::Implementation {
4949
const TagState& init,
5050
const EvaluationLimits& limits,
5151
const CheckpointSpec& checkpointSpec) {
52+
return evaluate(rule, std::vector<TagState>({init}), limits, checkpointSpec).front();
53+
}
54+
55+
std::vector<EvaluationResult> evaluate(const NamedRule& rule,
56+
const std::vector<TagState>& inits,
57+
const EvaluationLimits& limits,
58+
const CheckpointSpec& checkpointSpec) {
5259
const ChunkEvaluationTable chunkEvaluationTable = createChunkEvaluationTable(rule);
5360
if (limits.maxEventCount % chunkEvaluationTable.eventsAtOnce != 0) {
54-
return {ConclusionReason::InvalidInput, {{}, std::numeric_limits<uint8_t>::max()}, 0, 0};
61+
return std::vector<EvaluationResult>(
62+
inits.size(), {ConclusionReason::InvalidInput, {{}, std::numeric_limits<uint8_t>::max()}, 0, 0});
5563
}
56-
auto chunkedState = toChunkedState(init);
57-
CheckpointsTrie checkpointsTrie;
64+
CheckpointsTrie explicitCheckpointsTrie;
5865
for (const auto& checkpoint : checkpointSpec.states) {
59-
checkpointsTrie.insert(toChunkedState(checkpoint));
66+
explicitCheckpointsTrie.insert(toChunkedState(checkpoint));
6067
}
61-
uint64_t maxIntermediateTapeLength = tapeLength(chunkedState);
62-
ConclusionReason conclusionReason;
63-
const auto eventCount = evaluate(chunkEvaluationTable,
64-
&chunkedState,
65-
&conclusionReason,
66-
&maxIntermediateTapeLength,
67-
limits,
68-
&checkpointsTrie,
69-
checkpointSpec.flags);
70-
return {conclusionReason, fromChunkedStateDestructively(&chunkedState), eventCount, maxIntermediateTapeLength};
68+
69+
std::vector<EvaluationResult> results;
70+
results.reserve(inits.size());
71+
for (const auto& init : inits) {
72+
auto chunkedState = toChunkedState(init);
73+
uint64_t maxIntermediateTapeLength = tapeLength(chunkedState);
74+
ConclusionReason conclusionReason;
75+
const auto eventCount = evaluate(chunkEvaluationTable,
76+
&chunkedState,
77+
&conclusionReason,
78+
&maxIntermediateTapeLength,
79+
limits,
80+
explicitCheckpointsTrie,
81+
checkpointSpec.flags);
82+
results.push_back(
83+
{conclusionReason, fromChunkedStateDestructively(&chunkedState), eventCount, maxIntermediateTapeLength});
84+
}
85+
86+
return results;
7187
}
7288

7389
private:
@@ -79,7 +95,7 @@ class PostTagHistory::Implementation {
7995
return foundTable->second;
8096
}
8197

82-
ChunkEvaluationTable createChunkEvaluationTable(const ChunkedRule& rule) {
98+
static ChunkEvaluationTable createChunkEvaluationTable(const ChunkedRule& rule) {
8399
ChunkEvaluationTable table;
84100
table.eventsAtOnce = 8 / rule.inputLength;
85101
table.phaseCount = rule.phaseCount;
@@ -94,7 +110,7 @@ class PostTagHistory::Implementation {
94110
return table;
95111
}
96112

97-
ChunkOutput createChunkOutput(const ChunkedRule& rule, const uint8_t inputTape, const uint8_t inputPhase) const {
113+
static ChunkOutput createChunkOutput(const ChunkedRule& rule, const uint8_t inputTape, const uint8_t inputPhase) {
98114
uint16_t output = 0;
99115
uint8_t outputSize = 0;
100116
auto phase = inputPhase;
@@ -110,7 +126,7 @@ class PostTagHistory::Implementation {
110126
return {output, outputSize, phase};
111127
}
112128

113-
ChunkedState toChunkedState(const TagState& state) const {
129+
static ChunkedState toChunkedState(const TagState& state) {
114130
ChunkedState result;
115131
for (size_t i = 0; i < state.tape.size(); ++i) {
116132
if (i % 8 == 0) result.chunks.push_back(0);
@@ -123,7 +139,7 @@ class PostTagHistory::Implementation {
123139
return result;
124140
}
125141

126-
TagState fromChunkedStateDestructively(ChunkedState* chunkedState) const {
142+
static TagState fromChunkedStateDestructively(ChunkedState* chunkedState) {
127143
std::vector<bool> tape;
128144
if (chunkedState->chunks.size()) {
129145
tape.reserve(8 * (chunkedState->chunks.size() - 1) + chunkedState->lastChunkSize);
@@ -145,26 +161,31 @@ class PostTagHistory::Implementation {
145161
}
146162
}
147163

148-
uint64_t evaluate(const ChunkEvaluationTable& evaluationTable,
149-
ChunkedState* state,
150-
ConclusionReason* conclusionReason,
151-
uint64_t* maxIntermediateTapeLength,
152-
const EvaluationLimits& limits,
153-
CheckpointsTrie* checkpoints,
154-
const CheckpointSpecFlags& checkpointFlags) const {
164+
static uint64_t evaluate(const ChunkEvaluationTable& evaluationTable,
165+
ChunkedState* state,
166+
ConclusionReason* conclusionReason,
167+
uint64_t* maxIntermediateTapeLength,
168+
const EvaluationLimits& limits,
169+
const CheckpointsTrie& explicitCheckpoints,
170+
const CheckpointSpecFlags& checkpointFlags) {
171+
CheckpointsTrie automaticCheckpoints;
155172
uint64_t eventCount;
156173
for (eventCount = 0; eventCount < limits.maxEventCount && state->chunks.size() > 1;
157174
eventCount += evaluationTable.eventsAtOnce) {
158175
if (*maxIntermediateTapeLength > limits.maxTapeLength) {
159176
*conclusionReason = ConclusionReason::MaxTapeLengthExceeded;
160177
return eventCount;
161178
}
162-
if (checkpoints->contains(*state)) {
163-
*conclusionReason = ConclusionReason::ReachedCheckpoint;
179+
if (explicitCheckpoints.contains(*state)) {
180+
*conclusionReason = ConclusionReason::ReachedExplicitCheckpoint;
181+
return eventCount;
182+
}
183+
if (automaticCheckpoints.contains(*state)) {
184+
*conclusionReason = ConclusionReason::ReachedAutomaticCheckpoint;
164185
return eventCount;
165186
}
166187
if (checkpointFlags.powerOfTwoEventCounts && !isPowerOfTwo(eventCount)) {
167-
checkpoints->insert(*state);
188+
automaticCheckpoints.insert(*state);
168189
}
169190
evaluateOnce(evaluationTable, state);
170191
*maxIntermediateTapeLength = std::max(*maxIntermediateTapeLength, tapeLength(*state));
@@ -179,11 +200,11 @@ class PostTagHistory::Implementation {
179200

180201
static inline bool isPowerOfTwo(const uint64_t number) { return number & (number - 1); }
181202

182-
uint64_t tapeLength(const ChunkedState& state) const {
203+
static uint64_t tapeLength(const ChunkedState& state) {
183204
return std::max(0, static_cast<int>(state.chunks.size()) - 1) * 8 + state.lastChunkSize;
184205
}
185206

186-
void evaluateOnce(const ChunkEvaluationTable& evaluationTable, ChunkedState* state) const {
207+
static void evaluateOnce(const ChunkEvaluationTable& evaluationTable, ChunkedState* state) {
187208
const auto nextChunkIndex = evaluationTable.phaseCount * state->chunks.front() + state->phase;
188209
const auto& chunkOutput = evaluationTable.outputs[nextChunkIndex];
189210
state->chunks.pop_front();
@@ -217,4 +238,11 @@ PostTagHistory::EvaluationResult PostTagHistory::evaluate(const NamedRule& rule,
217238
const CheckpointSpec& checkpoints) {
218239
return implementation_->evaluate(rule, init, limits, checkpoints);
219240
}
241+
242+
std::vector<PostTagHistory::EvaluationResult> PostTagHistory::evaluate(const NamedRule& rule,
243+
const std::vector<TagState>& inits,
244+
const EvaluationLimits& limits,
245+
const CheckpointSpec& checkpoints) {
246+
return implementation_->evaluate(rule, inits, limits, checkpoints);
247+
}
220248
} // namespace PostTagSystem

libPostTagSystem/PostTagHistory.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class PostTagHistory {
1313
enum class ConclusionReason {
1414
InvalidInput,
1515
Terminated,
16-
ReachedCheckpoint,
16+
ReachedExplicitCheckpoint,
17+
ReachedAutomaticCheckpoint,
1718
MaxEventCountExceeded,
1819
MaxTapeLengthExceeded
1920
};
@@ -51,6 +52,11 @@ class PostTagHistory {
5152
const EvaluationLimits& limits,
5253
const CheckpointSpec& checkpointSpec = CheckpointSpec());
5354

55+
std::vector<EvaluationResult> evaluate(const NamedRule& rule,
56+
const std::vector<TagState>& inits,
57+
const EvaluationLimits& limits,
58+
const CheckpointSpec& checkpointSpec = CheckpointSpec());
59+
5460
private:
5561
class Implementation;
5662
std::shared_ptr<Implementation> implementation_;

libPostTagSystem/PostTagSearcher.cpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,35 @@ class PostTagSearcher::Implementation {
3737
std::vector<EvaluationResult> evaluateGroup(const std::vector<TagState>& states,
3838
const EvaluationParameters& parameters) {
3939
// TODO(maxitg): Implement groupTimeConstraintNs parameter
40-
// TODO(maxitg): Implement checkpoints parameter
4140

4241
std::vector<EvaluationResult> results;
4342
results.reserve(states.size());
4443
PostTagHistory evaluator;
4544
PostTagHistory::EvaluationLimits limits;
4645
limits.maxEventCount = parameters.maxEventCount;
4746
limits.maxTapeLength = parameters.maxTapeLength;
48-
for (const auto& init : states) {
49-
const auto singleInitResult = evaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {{}, {true}});
47+
const auto singleInitResults =
48+
evaluator.evaluate(PostTagHistory::NamedRule::Post, states, limits, {parameters.checkpoints, {true}});
49+
for (size_t initIndex = 0; initIndex < states.size(); ++initIndex) {
5050
EvaluationResult result;
51-
result.eventCount = singleInitResult.eventCount;
52-
result.maxTapeLength = singleInitResult.maxIntermediateTapeLength;
53-
result.finalTapeLength = singleInitResult.finalState.tape.size();
54-
result.initialState = init;
55-
result.finalState = singleInitResult.finalState;
56-
switch (singleInitResult.conclusionReason) {
51+
result.eventCount = singleInitResults[initIndex].eventCount;
52+
result.maxTapeLength = singleInitResults[initIndex].maxIntermediateTapeLength;
53+
result.finalTapeLength = singleInitResults[initIndex].finalState.tape.size();
54+
result.initialState = states[initIndex];
55+
result.finalState = singleInitResults[initIndex].finalState;
56+
switch (singleInitResults[initIndex].conclusionReason) {
5757
case PostTagHistory::ConclusionReason::Terminated:
5858
result.conclusionReason = ConclusionReason::Terminated;
5959
break;
6060

61-
case PostTagHistory::ConclusionReason::ReachedCheckpoint:
61+
case PostTagHistory::ConclusionReason::ReachedAutomaticCheckpoint:
6262
result.conclusionReason = ConclusionReason::ReachedCycle;
6363
break;
6464

65+
case PostTagHistory::ConclusionReason::ReachedExplicitCheckpoint:
66+
result.conclusionReason = ConclusionReason::ReachedKnownCheckpoint;
67+
break;
68+
6569
case PostTagHistory::ConclusionReason::MaxEventCountExceeded:
6670
result.conclusionReason = ConclusionReason::MaxEventCountExceeded;
6771
break;

libPostTagSystem/test/PostTagSearcher_test.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,20 @@ TEST(PostTagSearcher, emptyCases) {
2727

2828
void compareResults(const TagState& init,
2929
const PostTagSearcher::EvaluationResult& result,
30-
uint64_t eventLimit = std::numeric_limits<uint64_t>::max() - 7) {
30+
const PostTagHistory::EvaluationLimits& limits = PostTagHistory::EvaluationLimits(),
31+
const std::vector<TagState>& checkpoints = {}) {
3132
PostTagHistory singleHistoryEvaluator;
32-
const auto singleResult = singleHistoryEvaluator.evaluate(
33-
PostTagHistory::NamedRule::Post, init, PostTagHistory::EvaluationLimits(eventLimit), {{}, {true}});
33+
const auto singleResult =
34+
singleHistoryEvaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {checkpoints, {true}});
3435

3536
if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::InvalidInput) {
3637
ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::InvalidInput);
3738
} else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::Terminated) {
3839
ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::Terminated);
39-
} else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedCheckpoint) {
40+
} else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedAutomaticCheckpoint) {
4041
ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::ReachedCycle);
42+
} else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedExplicitCheckpoint) {
43+
ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::ReachedKnownCheckpoint);
4144
} else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::MaxEventCountExceeded) {
4245
ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::MaxEventCountExceeded);
4346
}
@@ -100,7 +103,20 @@ TEST(PostTagSearcher, eventLimit) {
100103
{{0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0}, {{0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 2}, parameters);
101104
ASSERT_EQ(result.size(), 2);
102105

103-
compareResults(TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0), result[0], 104);
104-
compareResults(TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 1), result[1], 104);
106+
compareResults(
107+
TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0), result[0], PostTagHistory::EvaluationLimits(104));
108+
compareResults(
109+
TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 1), result[1], PostTagHistory::EvaluationLimits(104));
110+
}
111+
112+
TEST(PostTagSearcher, checkpoints) {
113+
PostTagSearcher searcher;
114+
PostTagSearcher::EvaluationParameters parameters;
115+
parameters.checkpoints = {{{0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0}, 2}};
116+
const auto result = searcher.evaluateRange(20, 123, 125, parameters);
117+
ASSERT_EQ(result.size(), 6);
118+
119+
compareResults(TagState(20, 123, 0), result[0], PostTagHistory::EvaluationLimits(), parameters.checkpoints);
120+
compareResults(TagState(20, 124, 0), result[3], PostTagHistory::EvaluationLimits(), parameters.checkpoints);
105121
}
106122
} // namespace PostTagSystem

0 commit comments

Comments
 (0)