Skip to content

Commit

Permalink
TPA nested loops: PR comments fixes, quickfix of loop detection, remo…
Browse files Browse the repository at this point in the history
…ved code duplications
  • Loading branch information
BritikovKI committed Sep 17, 2024
1 parent f33d78a commit ba6b53f
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 261 deletions.
58 changes: 32 additions & 26 deletions src/TransformationUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,57 +88,63 @@ std::unique_ptr<TransitionSystem> toTransitionSystem(ChcDirectedGraph const & gr
return ts;
}

bool strongConnection(std::unordered_set<int> & visitedVertices, std::unordered_set<int> & verticesOnStack,

// This function follows a Tarjan's strong connection detection algorithm:
// https://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
// If some vertice is added on stack and it was already visited, then the strong connection is detected
// It was updated to remember the nodes which participate in the loop (lines 123, 127)
void strongConnection(std::unordered_set<SymRef, SymRefHash> & visitedVertices, std::unordered_set<SymRef, SymRefHash> & verticesOnStack,
AdjacencyListsGraphRepresentation const & graphRepresentation, ChcDirectedGraph const & graph,
SymRef node) {
visitedVertices.insert(node.x);
verticesOnStack.insert(node.x);
SymRef node, std::vector<EId> & loop) {
visitedVertices.insert(node);
verticesOnStack.insert(node);
auto const & outEdges = graphRepresentation.getOutgoingEdgesFor(node);
if (size(outEdges) <= 1) {
verticesOnStack.erase(node.x);
return false;
verticesOnStack.erase(node);
return;
}

for (EId eid : outEdges) {
if (graph.getTarget(eid) != node) {
auto nextVertex = graph.getTarget(eid);
if (visitedVertices.find(nextVertex.x) == visitedVertices.end()) {
bool loopFound =
strongConnection(visitedVertices, verticesOnStack, graphRepresentation, graph, nextVertex);
if (loopFound) { return true; }
} else if (verticesOnStack.find(nextVertex.x) != verticesOnStack.end()) {
return true;
if (visitedVertices.find(nextVertex) == visitedVertices.end()) {
strongConnection(visitedVertices, verticesOnStack, graphRepresentation, graph, nextVertex, loop);
if (!loop.empty() && graph.getTarget(eid) != graph.getTarget(loop[0]) &&
graph.getTarget(eid) == graph.getSource(loop[loop.size() - 1])) {
loop.push_back(eid);
return;
}
} else if (verticesOnStack.find(nextVertex) != verticesOnStack.end()) {
loop.push_back(eid);
return;
}
}
}

verticesOnStack.erase(node.x);

return false;
verticesOnStack.erase(node);
}

bool TarjanLoopDetection(ChcDirectedGraph const & graph) {
std::vector<EId> detectLoop(const ChcDirectedGraph & graph) {
std::vector<EId> loop;
if (graph.getVertices().size() <= 3) { return loop; }
auto graphRepresentation = AdjacencyListsGraphRepresentation::from(graph);
auto vertices = reversePostOrder(graph, graphRepresentation);
std::unordered_set<int> visitedVertices;
std::unordered_set<int> verticesOnStack;

for (uint i = 1; i < vertices.size() - 1; i++) {
if (visitedVertices.find(vertices[i].x) == visitedVertices.end()) {
bool loop_detected =
strongConnection(visitedVertices, verticesOnStack, graphRepresentation, graph, vertices[i]);
if (loop_detected) { return true; }
std::unordered_set<SymRef, SymRefHash> visitedVertices;
std::unordered_set<SymRef, SymRefHash> verticesOnStack;
for (uint i = 1; i < vertices.size() - 1 && loop.size() == 0; i++) {
if (visitedVertices.find(vertices[i]) == visitedVertices.end()) {
strongConnection(visitedVertices, verticesOnStack, graphRepresentation, graph, vertices[i], loop);
}
}
return false;
return loop;
}

bool isTransitionSystemDAG(ChcDirectedGraph const & graph) {
if (graph.getVertices().size() < 3) { return false; }
auto graphRepresentation = AdjacencyListsGraphRepresentation::from(graph);
auto vertices = reversePostOrder(graph, graphRepresentation);
assert(graph.getEntry() == vertices[0]);
bool hasLoop = TarjanLoopDetection(graph);
bool hasLoop = detectLoop(graph).size() > 0;
if (hasLoop) { return false; }
for (unsigned i = 1; i < vertices.size() - 1; ++i) {
auto current = vertices[i];
Expand Down
2 changes: 2 additions & 0 deletions src/TransformationUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ std::unique_ptr<SystemType> systemTypeFrom(vec<PTRef> const & stateVars, vec<PTR
PTRef transitionFormulaInSystemType(SystemType const & systemType, EdgeVariables const & edgeVariables, PTRef edgeLabel,
Logic & logic);

std::vector<EId> detectLoop(const ChcDirectedGraph & graph);

#endif // OPENSMT_TRANSFORMATIONUTILS_H
23 changes: 11 additions & 12 deletions src/engine/TPA.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,6 @@ VerificationResult TPAEngine::solve(ChcDirectedHyperGraph const & graph) {
auto translator = std::move(transformationResult.second);
if (transformedGraph->isNormalGraph()) {
auto normalGraph = transformedGraph->toNormalGraph();
if (options.hasOption(Options::SIMPLIFY_NESTED)) {
NestedLoopTransformation transformation;
auto preTranslator = transformation.transform(*normalGraph);
auto res = solve(*normalGraph);
if (shouldComputeWitness()) {
auto prewit = preTranslator->translate(res);
return shouldComputeWitness() ? translator->translate(std::move(prewit)) : std::move(prewit);
}
return res;
}
auto res = solve(*normalGraph);
return shouldComputeWitness() ? translator->translate(std::move(res)) : std::move(res);
}
Expand Down Expand Up @@ -84,8 +74,17 @@ VerificationResult TPAEngine::solve(const ChcDirectedGraph & graph) {
assert(false);
throw std::logic_error("Unreachable!");
}
} else if (isTransitionSystemDAG(graph) && not options.hasOption(Options::FORCE_TS)) {
return solveTransitionSystemGraph(graph);
} else if ((isTransitionSystemDAG(graph) && not options.hasOption(Options::FORCE_TS)) ||
options.hasOption(Options::SIMPLIFY_NESTED)) {
if (options.hasOption(Options::SIMPLIFY_NESTED)) {
NestedLoopTransformation transformation;
auto normalGraph = graph;
auto preTranslator = transformation.transform(normalGraph);
auto res = solveTransitionSystemGraph(normalGraph);
return preTranslator->translate(res);
} else {
return solveTransitionSystemGraph(graph);
}
}
// Translate CHCGraph into transition system
SingleLoopTransformation transformation;
Expand Down
107 changes: 107 additions & 0 deletions src/transformers/LoopTransformation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//
// Created by Konstantin Britikov on 17.09.24.
//

#ifndef GOLEM_LOOPTRANSFORMATION_H
#define GOLEM_LOOPTRANSFORMATION_H


#include "TransitionSystem.h"
#include "TransformationUtils.h"
#include "Witnesses.h"
#include "graph/ChcGraph.h"


class LoopTransformation {

public:
// Helper types
struct VarPosition {
SymRef vertex;
uint32_t pos;

inline bool operator==(VarPosition other) const { return vertex == other.vertex and pos == other.pos; }
};
struct VarPositionHasher {
std::size_t operator()(VarPosition pos) const {
std::hash<std::uint32_t> hasher;
return hasher(pos.vertex.x) ^ hasher(pos.pos);
}
};

using LocationVarMap = std::unordered_map<SymRef, PTRef, SymRefHash>;
using PositionVarMap = std::unordered_map<VarPosition, PTRef, VarPositionHasher>;

};


namespace {
using LocationVarMap = LoopTransformation::LocationVarMap;
using PositionVarMap = LoopTransformation::PositionVarMap;
using VarPosition = LoopTransformation::VarPosition;

struct EdgeTranslator {
ChcDirectedGraph const & graph;
LocationVarMap const & locationVarMap;
PositionVarMap const & positionVarMap;

mutable vec<PTRef> auxiliaryVariablesSeen;

PTRef translateEdge(DirectedEdge const & edge) const;
};

PTRef EdgeTranslator::translateEdge(DirectedEdge const & edge) const {
TermUtils::substitutions_map substitutionsMap;
Logic & logic = graph.getLogic();
auto source = edge.from;
auto target = edge.to;
TimeMachine timeMachine(logic);

auto edgeVariables = getVariablesFromEdge(logic, graph, edge.id);
for (PTRef auxVar : edgeVariables.auxiliaryVars) {
this->auxiliaryVariablesSeen.push(auxVar);
}

// TODO: prepare the substitution map in advance!
auto const & stateVars = edgeVariables.stateVars;
for (unsigned int i = 0; i < stateVars.size(); ++i) {
VarPosition varPosition{source, i};
auto it = positionVarMap.find(varPosition);
assert(it != positionVarMap.end());
substitutionsMap.insert({stateVars[i], it->second});
}

auto const & nextStateVars = edgeVariables.nextStateVars;
for (unsigned int i = 0; i < nextStateVars.size(); ++i) {
VarPosition varPosition{target, i};
auto it = positionVarMap.find(varPosition);
assert(it != positionVarMap.end());
substitutionsMap.insert({nextStateVars[i], timeMachine.sendVarThroughTime(it->second, 1)});
}

// Translate the constraint
PTRef translatedConstraint = TermUtils(logic).varSubstitute(edge.fla.fla, substitutionsMap);
PTRef sourceLocationVar = source == graph.getEntry() ? logic.getTerm_true() : locationVarMap.at(source);
PTRef targetLocationVar = locationVarMap.at(target);
PTRef updatedLocation = [&]() {
vec<PTRef> args;
args.capacity(locationVarMap.size() * 2);
for (auto && entry : locationVarMap) {
if (entry.second != targetLocationVar) {
args.push(logic.mkNot(timeMachine.sendVarThroughTime(entry.second, 1)));
}
if (entry.second != sourceLocationVar) { args.push(logic.mkNot(entry.second)); }
}
return logic.mkAnd(std::move(args));
}();
// We used to add equalities that restricted the values of all variables other than target ones to their default
// values. The paper uses frame equalities that keep the values from previous step. Now, we do not restrict
// the values of these variables in any way.
// This is sound because we still force the right variables to be considered using the location variables.
vec<PTRef> components{sourceLocationVar, translatedConstraint, timeMachine.sendVarThroughTime(targetLocationVar, 1),
updatedLocation};
return logic.mkAnd(std::move(components));
}

} // namespace
#endif // GOLEM_LOOPTRANSFORMATION_H
116 changes: 0 additions & 116 deletions src/transformers/NestedLoopTransformation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,6 @@
#include "graph/ChcGraph.h"
#include "utils/SmtSolver.h"

namespace {
using LocationVarMap = NestedLoopTransformation::LocationVarMap;
using PositionVarMap = NestedLoopTransformation::PositionVarMap;
using VarPosition = NestedLoopTransformation::VarPosition;

struct EdgeTranslator {
ChcDirectedGraph const & graph;
LocationVarMap const & locationVarMap;
PositionVarMap const & positionVarMap;

mutable vec<PTRef> auxiliaryVariablesSeen;

PTRef translateEdge(DirectedEdge const & edge) const;
};

PTRef EdgeTranslator::translateEdge(DirectedEdge const & edge) const {
TermUtils::substitutions_map substitutionsMap;
Logic & logic = graph.getLogic();
auto source = edge.from;
auto target = edge.to;
TimeMachine timeMachine(logic);

auto edgeVariables = getVariablesFromEdge(logic, graph, edge.id);
for (PTRef auxVar : edgeVariables.auxiliaryVars) {
this->auxiliaryVariablesSeen.push(auxVar);
}

// TODO: prepare the substitution map in advance!
auto const & stateVars = edgeVariables.stateVars;
for (unsigned int i = 0; i < stateVars.size(); ++i) {
VarPosition varPosition{source, i};
auto it = positionVarMap.find(varPosition);
assert(it != positionVarMap.end());
substitutionsMap.insert({stateVars[i], it->second});
}

auto const & nextStateVars = edgeVariables.nextStateVars;
for (unsigned int i = 0; i < nextStateVars.size(); ++i) {
VarPosition varPosition{target, i};
auto it = positionVarMap.find(varPosition);
assert(it != positionVarMap.end());
substitutionsMap.insert({nextStateVars[i], timeMachine.sendVarThroughTime(it->second, 1)});
}

// Translate the constraint
PTRef translatedConstraint = TermUtils(logic).varSubstitute(edge.fla.fla, substitutionsMap);
PTRef sourceLocationVar = source == graph.getEntry() ? logic.getTerm_true() : locationVarMap.at(source);
PTRef targetLocationVar = locationVarMap.at(target);
PTRef updatedLocation = [&]() {
vec<PTRef> args;
args.capacity(locationVarMap.size() * 2);
for (auto && entry : locationVarMap) {
if (entry.second != targetLocationVar) {
args.push(logic.mkNot(timeMachine.sendVarThroughTime(entry.second, 1)));
}
if (entry.second != sourceLocationVar) { args.push(logic.mkNot(entry.second)); }
}
return logic.mkAnd(std::move(args));
}();
// We used to add equalities that restricted the values of all variables other than target ones to their default
// values. The paper uses frame equalities that keep the values from previous step. Now, we do not restrict
// the values of these variables in any way.
// This is sound because we still force the right variables to be considered using the location variables.
vec<PTRef> components{sourceLocationVar, translatedConstraint, timeMachine.sendVarThroughTime(targetLocationVar, 1),
updatedLocation};
return logic.mkAnd(std::move(components));
}

} // namespace

std::unique_ptr<NestedLoopTransformation::WitnessBackTranslator>
NestedLoopTransformation::transform(ChcDirectedGraph & graph) {
auto vertices = graph.getVertices();
Expand All @@ -97,52 +27,6 @@ NestedLoopTransformation::transform(ChcDirectedGraph & graph) {
std::move(originalGraph), graph, std::move(locationVars), std::move(argVars), simplifiedNodes);
}

void strongConnection(std::unordered_set<int> & visitedVertices, std::unordered_set<int> & verticesOnStack,
AdjacencyListsGraphRepresentation const & graphRepresentation, ChcDirectedGraph const & graph,
SymRef node, std::vector<EId> & loop) {
visitedVertices.insert(node.x);
verticesOnStack.insert(node.x);
auto const & outEdges = graphRepresentation.getOutgoingEdgesFor(node);
if (size(outEdges) <= 1) {
verticesOnStack.erase(node.x);
return;
}

for (EId eid : outEdges) {
if (graph.getTarget(eid) != node) {
auto nextVertex = graph.getTarget(eid);
if (visitedVertices.find(nextVertex.x) == visitedVertices.end()) {
strongConnection(visitedVertices, verticesOnStack, graphRepresentation, graph, nextVertex, loop);
if (!loop.empty() && graph.getTarget(eid) != graph.getTarget(loop[0]) &&
graph.getTarget(eid) == graph.getSource(loop[loop.size() - 1])) {
loop.push_back(eid);
return;
}
} else if (verticesOnStack.find(nextVertex.x) != verticesOnStack.end()) {
loop.push_back(eid);
return;
}
}
}

verticesOnStack.erase(node.x);
}

std::vector<EId> NestedLoopTransformation::detectLoop(const ChcDirectedGraph & graph) {
std::vector<EId> loop;
if (graph.getVertices().size() <= 3) { return loop; }
auto graphRepresentation = AdjacencyListsGraphRepresentation::from(graph);
auto vertices = reversePostOrder(graph, graphRepresentation);
std::unordered_set<int> visitedVertices;
std::unordered_set<int> verticesOnStack;
for (uint i = 1; i < vertices.size() - 1; i++) {
if (visitedVertices.find(vertices[i].x) == visitedVertices.end()) {
strongConnection(visitedVertices, verticesOnStack, graphRepresentation, graph, vertices[i], loop);
}
}
return loop;
}

SymRef NestedLoopTransformation::simplifyLoop(ChcDirectedGraph & graph, std::vector<EId> loop,
LocationVarMap & locationVars, PositionVarMap & argVars) {
Logic & logic = graph.getLogic();
Expand Down
Loading

0 comments on commit ba6b53f

Please sign in to comment.