From c98903825dc0ac79716447ecdbf7aa5dbfaf0eac Mon Sep 17 00:00:00 2001 From: Max Piskunov Date: Sun, 28 Feb 2021 19:55:19 -0600 Subject: [PATCH 1/4] Separate checkpoint tries into explicit and automatic. --- libPostTagSystem/PostTagHistory.cpp | 19 ++++++++++++------- libPostTagSystem/PostTagHistory.hpp | 3 ++- libPostTagSystem/PostTagSearcher.cpp | 9 ++++++--- .../test/PostTagSearcher_test.cpp | 4 +++- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/libPostTagSystem/PostTagHistory.cpp b/libPostTagSystem/PostTagHistory.cpp index 8dcbace..10d65d3 100644 --- a/libPostTagSystem/PostTagHistory.cpp +++ b/libPostTagSystem/PostTagHistory.cpp @@ -54,9 +54,9 @@ class PostTagHistory::Implementation { return {ConclusionReason::InvalidInput, {{}, std::numeric_limits::max()}, 0, 0}; } auto chunkedState = toChunkedState(init); - CheckpointsTrie checkpointsTrie; + CheckpointsTrie explicitCheckpointsTrie; for (const auto& checkpoint : checkpointSpec.states) { - checkpointsTrie.insert(toChunkedState(checkpoint)); + explicitCheckpointsTrie.insert(toChunkedState(checkpoint)); } uint64_t maxIntermediateTapeLength = tapeLength(chunkedState); ConclusionReason conclusionReason; @@ -65,7 +65,7 @@ class PostTagHistory::Implementation { &conclusionReason, &maxIntermediateTapeLength, limits, - &checkpointsTrie, + explicitCheckpointsTrie, checkpointSpec.flags); return {conclusionReason, fromChunkedStateDestructively(&chunkedState), eventCount, maxIntermediateTapeLength}; } @@ -150,8 +150,9 @@ class PostTagHistory::Implementation { ConclusionReason* conclusionReason, uint64_t* maxIntermediateTapeLength, const EvaluationLimits& limits, - CheckpointsTrie* checkpoints, + const CheckpointsTrie& explicitCheckpoints, const CheckpointSpecFlags& checkpointFlags) const { + CheckpointsTrie automaticCheckpoints; uint64_t eventCount; for (eventCount = 0; eventCount < limits.maxEventCount && state->chunks.size() > 1; eventCount += evaluationTable.eventsAtOnce) { @@ -159,12 +160,16 @@ class PostTagHistory::Implementation { *conclusionReason = ConclusionReason::MaxTapeLengthExceeded; return eventCount; } - if (checkpoints->contains(*state)) { - *conclusionReason = ConclusionReason::ReachedCheckpoint; + if (explicitCheckpoints.contains(*state)) { + *conclusionReason = ConclusionReason::ReachedExplicitCheckpoint; + return eventCount; + } + if (automaticCheckpoints.contains(*state)) { + *conclusionReason = ConclusionReason::ReachedAutomaticCheckpoint; return eventCount; } if (checkpointFlags.powerOfTwoEventCounts && !isPowerOfTwo(eventCount)) { - checkpoints->insert(*state); + automaticCheckpoints.insert(*state); } evaluateOnce(evaluationTable, state); *maxIntermediateTapeLength = std::max(*maxIntermediateTapeLength, tapeLength(*state)); diff --git a/libPostTagSystem/PostTagHistory.hpp b/libPostTagSystem/PostTagHistory.hpp index ef7f46a..539d370 100644 --- a/libPostTagSystem/PostTagHistory.hpp +++ b/libPostTagSystem/PostTagHistory.hpp @@ -13,7 +13,8 @@ class PostTagHistory { enum class ConclusionReason { InvalidInput, Terminated, - ReachedCheckpoint, + ReachedExplicitCheckpoint, + ReachedAutomaticCheckpoint, MaxEventCountExceeded, MaxTapeLengthExceeded }; diff --git a/libPostTagSystem/PostTagSearcher.cpp b/libPostTagSystem/PostTagSearcher.cpp index e176e65..fea9093 100644 --- a/libPostTagSystem/PostTagSearcher.cpp +++ b/libPostTagSystem/PostTagSearcher.cpp @@ -37,7 +37,6 @@ class PostTagSearcher::Implementation { std::vector evaluateGroup(const std::vector& states, const EvaluationParameters& parameters) { // TODO(maxitg): Implement groupTimeConstraintNs parameter - // TODO(maxitg): Implement checkpoints parameter std::vector results; results.reserve(states.size()); @@ -46,7 +45,7 @@ class PostTagSearcher::Implementation { limits.maxEventCount = parameters.maxEventCount; limits.maxTapeLength = parameters.maxTapeLength; for (const auto& init : states) { - const auto singleInitResult = evaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {{}, {true}}); + const auto singleInitResult = evaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {parameters.checkpoints, {true}}); EvaluationResult result; result.eventCount = singleInitResult.eventCount; result.maxTapeLength = singleInitResult.maxIntermediateTapeLength; @@ -58,10 +57,14 @@ class PostTagSearcher::Implementation { result.conclusionReason = ConclusionReason::Terminated; break; - case PostTagHistory::ConclusionReason::ReachedCheckpoint: + case PostTagHistory::ConclusionReason::ReachedAutomaticCheckpoint: result.conclusionReason = ConclusionReason::ReachedCycle; break; + case PostTagHistory::ConclusionReason::ReachedExplicitCheckpoint: + result.conclusionReason = ConclusionReason::ReachedKnownCheckpoint; + break; + case PostTagHistory::ConclusionReason::MaxEventCountExceeded: result.conclusionReason = ConclusionReason::MaxEventCountExceeded; break; diff --git a/libPostTagSystem/test/PostTagSearcher_test.cpp b/libPostTagSystem/test/PostTagSearcher_test.cpp index 3e6ec96..d7ee85a 100644 --- a/libPostTagSystem/test/PostTagSearcher_test.cpp +++ b/libPostTagSystem/test/PostTagSearcher_test.cpp @@ -36,8 +36,10 @@ void compareResults(const TagState& init, ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::InvalidInput); } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::Terminated) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::Terminated); - } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedCheckpoint) { + } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedAutomaticCheckpoint) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::ReachedCycle); + } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedExplicitCheckpoint) { + ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::ReachedKnownCheckpoint); } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::MaxEventCountExceeded) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::MaxEventCountExceeded); } From bc6e4207004c83db658d0497ff9a5e778dca28b5 Mon Sep 17 00:00:00 2001 From: Max Piskunov Date: Mon, 1 Mar 2021 09:26:39 -0600 Subject: [PATCH 2/4] Perform enumeration in PostTagHistory to eliminate recreation of the trie for each init. --- libPostTagSystem/PostTagHistory.cpp | 73 ++++++++++++++++++---------- libPostTagSystem/PostTagHistory.hpp | 5 ++ libPostTagSystem/PostTagSearcher.cpp | 17 ++++--- 3 files changed, 62 insertions(+), 33 deletions(-) diff --git a/libPostTagSystem/PostTagHistory.cpp b/libPostTagSystem/PostTagHistory.cpp index 10d65d3..3d55044 100644 --- a/libPostTagSystem/PostTagHistory.cpp +++ b/libPostTagSystem/PostTagHistory.cpp @@ -49,25 +49,41 @@ class PostTagHistory::Implementation { const TagState& init, const EvaluationLimits& limits, const CheckpointSpec& checkpointSpec) { + return evaluate(rule, std::vector({init}), limits, checkpointSpec).front(); + } + + std::vector evaluate(const NamedRule& rule, + const std::vector& inits, + const EvaluationLimits& limits, + const CheckpointSpec& checkpointSpec) { const ChunkEvaluationTable chunkEvaluationTable = createChunkEvaluationTable(rule); if (limits.maxEventCount % chunkEvaluationTable.eventsAtOnce != 0) { - return {ConclusionReason::InvalidInput, {{}, std::numeric_limits::max()}, 0, 0}; + return std::vector( + inits.size(), {ConclusionReason::InvalidInput, {{}, std::numeric_limits::max()}, 0, 0}); } - auto chunkedState = toChunkedState(init); CheckpointsTrie explicitCheckpointsTrie; for (const auto& checkpoint : checkpointSpec.states) { explicitCheckpointsTrie.insert(toChunkedState(checkpoint)); } - uint64_t maxIntermediateTapeLength = tapeLength(chunkedState); - ConclusionReason conclusionReason; - const auto eventCount = evaluate(chunkEvaluationTable, - &chunkedState, - &conclusionReason, - &maxIntermediateTapeLength, - limits, - explicitCheckpointsTrie, - checkpointSpec.flags); - return {conclusionReason, fromChunkedStateDestructively(&chunkedState), eventCount, maxIntermediateTapeLength}; + + std::vector results; + results.reserve(inits.size()); + for (const auto& init : inits) { + auto chunkedState = toChunkedState(init); + uint64_t maxIntermediateTapeLength = tapeLength(chunkedState); + ConclusionReason conclusionReason; + const auto eventCount = evaluate(chunkEvaluationTable, + &chunkedState, + &conclusionReason, + &maxIntermediateTapeLength, + limits, + explicitCheckpointsTrie, + checkpointSpec.flags); + results.push_back( + {conclusionReason, fromChunkedStateDestructively(&chunkedState), eventCount, maxIntermediateTapeLength}); + } + + return results; } private: @@ -79,7 +95,7 @@ class PostTagHistory::Implementation { return foundTable->second; } - ChunkEvaluationTable createChunkEvaluationTable(const ChunkedRule& rule) { + static ChunkEvaluationTable createChunkEvaluationTable(const ChunkedRule& rule) { ChunkEvaluationTable table; table.eventsAtOnce = 8 / rule.inputLength; table.phaseCount = rule.phaseCount; @@ -94,7 +110,7 @@ class PostTagHistory::Implementation { return table; } - ChunkOutput createChunkOutput(const ChunkedRule& rule, const uint8_t inputTape, const uint8_t inputPhase) const { + static ChunkOutput createChunkOutput(const ChunkedRule& rule, const uint8_t inputTape, const uint8_t inputPhase) { uint16_t output = 0; uint8_t outputSize = 0; auto phase = inputPhase; @@ -110,7 +126,7 @@ class PostTagHistory::Implementation { return {output, outputSize, phase}; } - ChunkedState toChunkedState(const TagState& state) const { + static ChunkedState toChunkedState(const TagState& state) { ChunkedState result; for (size_t i = 0; i < state.tape.size(); ++i) { if (i % 8 == 0) result.chunks.push_back(0); @@ -123,7 +139,7 @@ class PostTagHistory::Implementation { return result; } - TagState fromChunkedStateDestructively(ChunkedState* chunkedState) const { + static TagState fromChunkedStateDestructively(ChunkedState* chunkedState) { std::vector tape; if (chunkedState->chunks.size()) { tape.reserve(8 * (chunkedState->chunks.size() - 1) + chunkedState->lastChunkSize); @@ -145,13 +161,13 @@ class PostTagHistory::Implementation { } } - uint64_t evaluate(const ChunkEvaluationTable& evaluationTable, - ChunkedState* state, - ConclusionReason* conclusionReason, - uint64_t* maxIntermediateTapeLength, - const EvaluationLimits& limits, - const CheckpointsTrie& explicitCheckpoints, - const CheckpointSpecFlags& checkpointFlags) const { + static uint64_t evaluate(const ChunkEvaluationTable& evaluationTable, + ChunkedState* state, + ConclusionReason* conclusionReason, + uint64_t* maxIntermediateTapeLength, + const EvaluationLimits& limits, + const CheckpointsTrie& explicitCheckpoints, + const CheckpointSpecFlags& checkpointFlags) { CheckpointsTrie automaticCheckpoints; uint64_t eventCount; for (eventCount = 0; eventCount < limits.maxEventCount && state->chunks.size() > 1; @@ -184,11 +200,11 @@ class PostTagHistory::Implementation { static inline bool isPowerOfTwo(const uint64_t number) { return number & (number - 1); } - uint64_t tapeLength(const ChunkedState& state) const { + static uint64_t tapeLength(const ChunkedState& state) { return std::max(0, static_cast(state.chunks.size()) - 1) * 8 + state.lastChunkSize; } - void evaluateOnce(const ChunkEvaluationTable& evaluationTable, ChunkedState* state) const { + static void evaluateOnce(const ChunkEvaluationTable& evaluationTable, ChunkedState* state) { const auto nextChunkIndex = evaluationTable.phaseCount * state->chunks.front() + state->phase; const auto& chunkOutput = evaluationTable.outputs[nextChunkIndex]; state->chunks.pop_front(); @@ -222,4 +238,11 @@ PostTagHistory::EvaluationResult PostTagHistory::evaluate(const NamedRule& rule, const CheckpointSpec& checkpoints) { return implementation_->evaluate(rule, init, limits, checkpoints); } + +std::vector PostTagHistory::evaluate(const NamedRule& rule, + const std::vector& inits, + const EvaluationLimits& limits, + const CheckpointSpec& checkpoints) { + return implementation_->evaluate(rule, inits, limits, checkpoints); +} } // namespace PostTagSystem diff --git a/libPostTagSystem/PostTagHistory.hpp b/libPostTagSystem/PostTagHistory.hpp index 539d370..6cbc6ef 100644 --- a/libPostTagSystem/PostTagHistory.hpp +++ b/libPostTagSystem/PostTagHistory.hpp @@ -52,6 +52,11 @@ class PostTagHistory { const EvaluationLimits& limits, const CheckpointSpec& checkpointSpec = CheckpointSpec()); + std::vector evaluate(const NamedRule& rule, + const std::vector& inits, + const EvaluationLimits& limits, + const CheckpointSpec& checkpointSpec = CheckpointSpec()); + private: class Implementation; std::shared_ptr implementation_; diff --git a/libPostTagSystem/PostTagSearcher.cpp b/libPostTagSystem/PostTagSearcher.cpp index fea9093..a6c06a8 100644 --- a/libPostTagSystem/PostTagSearcher.cpp +++ b/libPostTagSystem/PostTagSearcher.cpp @@ -44,15 +44,16 @@ class PostTagSearcher::Implementation { PostTagHistory::EvaluationLimits limits; limits.maxEventCount = parameters.maxEventCount; limits.maxTapeLength = parameters.maxTapeLength; - for (const auto& init : states) { - const auto singleInitResult = evaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {parameters.checkpoints, {true}}); + const auto singleInitResults = + evaluator.evaluate(PostTagHistory::NamedRule::Post, states, limits, {parameters.checkpoints, {true}}); + for (int initIndex = 0; initIndex < states.size(); ++initIndex) { EvaluationResult result; - result.eventCount = singleInitResult.eventCount; - result.maxTapeLength = singleInitResult.maxIntermediateTapeLength; - result.finalTapeLength = singleInitResult.finalState.tape.size(); - result.initialState = init; - result.finalState = singleInitResult.finalState; - switch (singleInitResult.conclusionReason) { + result.eventCount = singleInitResults[initIndex].eventCount; + result.maxTapeLength = singleInitResults[initIndex].maxIntermediateTapeLength; + result.finalTapeLength = singleInitResults[initIndex].finalState.tape.size(); + result.initialState = states[initIndex]; + result.finalState = singleInitResults[initIndex].finalState; + switch (singleInitResults[initIndex].conclusionReason) { case PostTagHistory::ConclusionReason::Terminated: result.conclusionReason = ConclusionReason::Terminated; break; From a61c855ec797fe8162064220a8b3fcebb908e4ba Mon Sep 17 00:00:00 2001 From: Max Piskunov Date: Mon, 1 Mar 2021 09:27:00 -0600 Subject: [PATCH 3/4] Add test. --- .../test/PostTagSearcher_test.cpp | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/libPostTagSystem/test/PostTagSearcher_test.cpp b/libPostTagSystem/test/PostTagSearcher_test.cpp index d7ee85a..ce2de71 100644 --- a/libPostTagSystem/test/PostTagSearcher_test.cpp +++ b/libPostTagSystem/test/PostTagSearcher_test.cpp @@ -27,10 +27,11 @@ TEST(PostTagSearcher, emptyCases) { void compareResults(const TagState& init, const PostTagSearcher::EvaluationResult& result, - uint64_t eventLimit = std::numeric_limits::max() - 7) { + const PostTagHistory::EvaluationLimits& limits = PostTagHistory::EvaluationLimits(), + const std::vector& checkpoints = {}) { PostTagHistory singleHistoryEvaluator; - const auto singleResult = singleHistoryEvaluator.evaluate( - PostTagHistory::NamedRule::Post, init, PostTagHistory::EvaluationLimits(eventLimit), {{}, {true}}); + const auto singleResult = + singleHistoryEvaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {checkpoints, {true}}); if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::InvalidInput) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::InvalidInput); @@ -102,7 +103,20 @@ TEST(PostTagSearcher, eventLimit) { {{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); ASSERT_EQ(result.size(), 2); - compareResults(TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0), result[0], 104); - compareResults(TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 1), result[1], 104); + compareResults( + TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0), result[0], PostTagHistory::EvaluationLimits(104)); + compareResults( + TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 1), result[1], PostTagHistory::EvaluationLimits(104)); +} + +TEST(PostTagSearcher, checkpoints) { + PostTagSearcher searcher; + PostTagSearcher::EvaluationParameters parameters; + parameters.checkpoints = {{{0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0}, 2}}; + const auto result = searcher.evaluateRange(20, 123, 125, parameters); + ASSERT_EQ(result.size(), 6); + + compareResults(TagState(20, 123, 0), result[0], PostTagHistory::EvaluationLimits(), parameters.checkpoints); + compareResults(TagState(20, 124, 0), result[3], PostTagHistory::EvaluationLimits(), parameters.checkpoints); } } // namespace PostTagSystem From 3dba8e5124ea9563170ec83334298ab711eeb68a Mon Sep 17 00:00:00 2001 From: Max Piskunov Date: Mon, 1 Mar 2021 09:28:18 -0600 Subject: [PATCH 4/4] Fix integer comparison. --- libPostTagSystem/PostTagSearcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libPostTagSystem/PostTagSearcher.cpp b/libPostTagSystem/PostTagSearcher.cpp index a6c06a8..5ecc032 100644 --- a/libPostTagSystem/PostTagSearcher.cpp +++ b/libPostTagSystem/PostTagSearcher.cpp @@ -46,7 +46,7 @@ class PostTagSearcher::Implementation { limits.maxTapeLength = parameters.maxTapeLength; const auto singleInitResults = evaluator.evaluate(PostTagHistory::NamedRule::Post, states, limits, {parameters.checkpoints, {true}}); - for (int initIndex = 0; initIndex < states.size(); ++initIndex) { + for (size_t initIndex = 0; initIndex < states.size(); ++initIndex) { EvaluationResult result; result.eventCount = singleInitResults[initIndex].eventCount; result.maxTapeLength = singleInitResults[initIndex].maxIntermediateTapeLength;