325copyGraphMapped(SourceGraph &src, VertexSelector vertexSelector, EdgeSelector edgeSelector) {
326 DestinationGraph dst;
328 std::vector<typename GraphTraits<DestinationGraph>::VertexIterator> vertexMap(src.nVertices(), dst.vertices().end());
329 for (
const auto &srcVertex: src.vertices()) {
330 if (vertexSelector(srcVertex)) {
331 auto newVertex = dst.insertVertex(
typename DestinationGraph::VertexValue(srcVertex.value()));
332 vertexMap[srcVertex.id()] = newVertex;
336 std::vector<typename GraphTraits<DestinationGraph>::EdgeIterator> edgeMap(src.nEdges(), dst.edges().end());
337 for (
const auto &srcEdge: src.edges()) {
338 const auto dstSource = vertexMap[srcEdge.source()->id()];
339 const auto dstTarget = vertexMap[srcEdge.target()->id()];
340 if (dstSource != dst.vertices().end() && dstTarget != dst.vertices().end() && edgeSelector(srcEdge)) {
341 auto newEdge = dst.insertEdge(dstSource, dstTarget,
typename DestinationGraph::EdgeValue(srcEdge.value()));
342 edgeMap[srcEdge.id()] = newEdge;
346 return {dst, vertexMap, edgeMap};
551 std::vector<size_t> x, y;
554 SolutionProcessor solutionProcessor_;
555 EquivalenceP equivalenceP_;
556 size_t minimumSolutionSize_;
557 size_t maximumSolutionSize_;
558 bool monotonicallyIncreasing_;
559 bool findingCommonSubgraphs_;
572#if SAWYER_VAM_STACK_ALLOCATOR
586#if SAWYER_VAM_STACK_ALLOCATOR
587 std::vector<size_t*> rows_;
588 std::vector<size_t> rowSize_;
591 std::vector<std::vector<size_t> > rows_;
593 size_t lastRowStarted_;
594#ifdef SAWYER_VAM_EXTRA_CHECKS
595 size_t maxRows_, maxCols_;
601 : allocator_(allocator),
602#if SAWYER_VAM_STACK_ALLOCATOR
605 lastRowStarted_((
size_t)(-1)) {
610#if SAWYER_VAM_STACK_ALLOCATOR
611 if (lowWater_ != NULL) {
612 ASSERT_require(!rows_.empty());
613 allocator_.
revert(lowWater_);
615 ASSERT_require((
size_t)std::count(rowSize_.begin(), rowSize_.end(), 0) == rows_.size());
621 void reserveRows(
size_t nrows) {
622 rows_.reserve(nrows);
623#if SAWYER_VAM_STACK_ALLOCATOR
624 rowSize_.reserve(nrows);
626#ifdef SAWYER_VAM_EXTRA_CHECKS
633 void startNewRow(
size_t i,
size_t maxColumns) {
634#ifdef SAWYER_VAM_EXTRA_CHECKS
635 ASSERT_require(i < maxRows_);
636 maxCols_ = maxColumns;
638#if SAWYER_VAM_STACK_ALLOCATOR
639 if (i >= rows_.size()) {
640 rows_.resize(i+1, NULL);
641 rowSize_.resize(i+1, 0);
643 ASSERT_require(rows_[i] == NULL);
644 rows_[i] = allocator_.
reserve(maxColumns);
646 if (i >= rows_.size())
653 void push(
size_t i,
size_t x) {
654 ASSERT_require(i == lastRowStarted_);
655 ASSERT_require(i < rows_.size());
656#ifdef SAWYER_VAM_EXTRA_CHECKS
657 ASSERT_require(size(i) < maxCols_);
659#if SAWYER_VAM_STACK_ALLOCATOR
661 if (lowWater_ == NULL)
663#ifdef SAWYER_VAM_EXTRA_CHECKS
664 ASSERT_require(ptr == rows_[i] + rowSize_[i]);
669 rows_[i].push_back(x);
674 size_t size(
size_t i)
const {
675#if SAWYER_VAM_STACK_ALLOCATOR
676 return i < rows_.size() ? rowSize_[i] : size_t(0);
678 return i < rows_.size() ? rows_[i].size() : size_t(0);
685 boost::iterator_range<const size_t*> get(
size_t i)
const {
686 static const size_t empty = 911;
687 if (i < rows_.size() && size(i) > 0) {
688#if SAWYER_VAM_STACK_ALLOCATOR
689 return boost::iterator_range<const size_t*>(rows_[i], rows_[i] + rowSize_[i]);
691 return boost::iterator_range<const size_t*>(&rows_[i][0], &rows_[i][0] + rows_[i].size());
694 return boost::iterator_range<const size_t*>(&empty, &empty);
714 EquivalenceP equivalenceP = EquivalenceP())
715 : g1(g1), g2(g2), v(g1.nVertices()), w(g2.nVertices()), vNotX(g1.nVertices()), solutionProcessor_(
solutionProcessor),
716 equivalenceP_(equivalenceP), minimumSolutionSize_(1), maximumSolutionSize_(-1), monotonicallyIncreasing_(false),
717 findingCommonSubgraphs_(true), vamAllocator_(SAWYER_VAM_STACK_ALLOCATOR * g2.nVertices()) {}
721 ASSERT_not_reachable(
"copy constructor makes no sense");
724 CommonSubgraphIsomorphism& operator=(
const CommonSubgraphIsomorphism&) {
725 ASSERT_not_reachable(
"assignment operator makes no sense");
833 Vam vam = initializeVam();
851 template<
typename Container>
852 static size_t maxPlusOneOrZero(
const Container &container) {
853 if (container.isEmpty())
856 BOOST_FOREACH (
size_t val, container.values())
857 retval = std::max(retval, val);
863 Vam initializeVam() {
864 Vam vam(vamAllocator_);
865 vam.reserveRows(maxPlusOneOrZero(v));
866 BOOST_FOREACH (
size_t i, v.
values()) {
867 typename GraphA::ConstVertexIterator v1 = g1.findVertex(i);
868 vam.startNewRow(i, w.
size());
869 BOOST_FOREACH (
size_t j, w.
values()) {
870 typename GraphB::ConstVertexIterator w1 = g2.findVertex(j);
871 std::vector<typename GraphA::ConstEdgeIterator> selfEdges1;
872 std::vector<typename GraphB::ConstEdgeIterator> selfEdges2;
873 findEdges(g1, i, i, selfEdges1 );
874 findEdges(g2, j, j, selfEdges2 );
875 if (selfEdges1.size() == selfEdges2.size() &&
876 equivalenceP_.mu(g1, g1.findVertex(i), g2, g2.findVertex(j)) &&
877 equivalenceP_.nu(g1, v1, v1, selfEdges1, g2, w1, w1, selfEdges2))
890 bool isSolutionPossible(
const Vam &vam)
const {
891 if (findingCommonSubgraphs_ && x.size() >= maximumSolutionSize_)
893 size_t largestPossibleSolution = x.size();
894 BOOST_FOREACH (
size_t i, vNotX.
values()) {
895 if (vam.size(i) > 0) {
896 ++largestPossibleSolution;
897 if ((findingCommonSubgraphs_ && largestPossibleSolution >= minimumSolutionSize_) ||
898 (!findingCommonSubgraphs_ && largestPossibleSolution >= g1.nVertices()))
908 size_t pickVertex(
const Vam &vam)
const {
912 size_t shortestRowLength(-1), retval(-1);
913 BOOST_FOREACH (
size_t i, vNotX.
values()) {
914 size_t vs = vam.size(i);
915 if (vs > 0 && vs < shortestRowLength) {
916 shortestRowLength = vs;
920 ASSERT_require2(retval !=
size_t(-1),
"cannot be reached if isSolutionPossible returned true");
925 void extendSolution(
size_t i,
size_t j) {
926 ASSERT_require(x.size() == y.size());
927 ASSERT_require(std::find(x.begin(), x.end(), i) == x.end());
928 ASSERT_require(std::find(y.begin(), y.end(), j) == y.end());
929 ASSERT_require(vNotX.
exists(i));
936 void retractSolution() {
937 ASSERT_require(x.size() == y.size());
938 ASSERT_require(!x.empty());
940 ASSERT_forbid(vNotX.
exists(i));
948 template<
class Graph>
950 findEdges(
const Graph &g,
size_t sourceVertex,
size_t targetVertex,
951 std::vector<typename Graph::ConstEdgeIterator> &result )
const {
952 BOOST_FOREACH (
const typename Graph::Edge &candidate, g.findVertex(sourceVertex)->outEdges()) {
953 if (candidate.target()->id() == targetVertex)
954 result.push_back(g.findEdge(candidate.id()));
961 bool edgesAreSuitable(
size_t i,
size_t iUnused,
size_t j,
size_t jUnused)
const {
962 ASSERT_require(i != iUnused);
963 ASSERT_require(j != jUnused);
966 std::vector<typename GraphA::ConstEdgeIterator> edges1;
967 std::vector<typename GraphB::ConstEdgeIterator> edges2;
968 findEdges(g1, i, iUnused, edges1 );
969 findEdges(g2, j, jUnused, edges2 );
970 if (edges1.size() != edges2.size())
972 findEdges(g1, iUnused, i, edges1 );
973 findEdges(g2, jUnused, j, edges2 );
974 if (edges1.size() != edges2.size())
979 if (edges1.empty() && edges2.empty())
983 typename GraphA::ConstVertexIterator v1 = g1.findVertex(i), v2 = g1.findVertex(iUnused);
984 typename GraphB::ConstVertexIterator w1 = g2.findVertex(j), w2 = g2.findVertex(jUnused);
985 return equivalenceP_.nu(g1, v1, v2, edges1, g2, w1, w2, edges2);
989 void refine(
const Vam &vam, Vam &refined ) {
990 refined.reserveRows(maxPlusOneOrZero(vNotX));
991 BOOST_FOREACH (
size_t i, vNotX.
values()) {
992 size_t rowLength = vam.size(i);
993 refined.startNewRow(i, rowLength);
994 BOOST_FOREACH (
size_t j, vam.get(i)) {
995 if (j != y.back() && edgesAreSuitable(x.back(), i, y.back(), j))
1002 bool isSolutionValidSize()
const {
1003 if (findingCommonSubgraphs_) {
1004 return x.size() >= minimumSolutionSize_ && x.size() <= maximumSolutionSize_;
1006 return x.size() == g1.nVertices();
1016 equivalenceP_.progress(level);
1017 if (isSolutionPossible(vam)) {
1018 size_t i = pickVertex(vam);
1019 BOOST_FOREACH (
size_t j, vam.get(i)) {
1020 extendSolution(i, j);
1021 Vam refined(vamAllocator_);
1022 refine(vam, refined);
1023 if (recurse(refined, level+1) ==
CSI_ABORT)
1029 if (findingCommonSubgraphs_) {
1031 ASSERT_require(vNotX.
exists(i));
1038 }
else if (isSolutionValidSize()) {
1039 ASSERT_require(x.size() == y.size());
1040 if (monotonicallyIncreasing_)
1041 minimumSolutionSize_ = x.size();
1042 if (solutionProcessor_(g1, x, g2, y) ==
CSI_ABORT)
1267 static const size_t NO_ID = (size_t)(-1);
1275 traversal.
start(root);
1277 std::reverse(flowlist.begin(), flowlist.end());
1281 std::vector<size_t> rflowlist(g.
nVertices(), NO_ID);
1282 for (
size_t i=0; i<flowlist.size(); ++i)
1283 rflowlist[flowlist[i]] = i;
1286 std::vector<size_t> idom(flowlist.size());
1287 for (
size_t i=0; i<flowlist.size(); ++i)
1290 bool changed =
true;
1293 for (
size_t vertex_i=0; vertex_i < flowlist.size(); ++vertex_i) {
1294 VertexIterator vertex = g.
findVertex(flowlist[vertex_i]);
1297#ifndef SAWYER_NDEBUG
1298 for (
size_t i=0; i<idom.size(); ++i)
1299 ASSERT_require(idom[i] <= i);
1309 size_t newIdom = idom[vertex_i];
1311 boost::iterator_range<EdgeIterator> edges = previousEdges<EdgeIterator>(vertex, Direction());
1312 for (EdgeIterator edge=edges.begin(); edge!=edges.end(); ++edge) {
1313 VertexIterator predecessor = previousVertex<VertexIterator>(edge, Direction());
1314 size_t predecessor_i = rflowlist[predecessor->id()];
1315 if (NO_ID == predecessor_i)
1317 if (predecessor_i == vertex_i)
1319 if (predecessor_i > vertex_i)
1323 if (newIdom == vertex_i) {
1324 newIdom = predecessor_i;
1326 size_t tmpIdom = predecessor_i;
1327 while (newIdom != tmpIdom) {
1328 while (newIdom > tmpIdom)
1329 newIdom = idom[newIdom];
1330 while (tmpIdom > newIdom)
1331 tmpIdom = idom[tmpIdom];
1336 if (idom[vertex_i] != newIdom) {
1337 idom[vertex_i] = newIdom;
1344 std::vector<VertexIterator> retval;
1346 for (
size_t i=0; i<flowlist.size(); ++i) {
1348 retval[flowlist[i]] = g.
findVertex(flowlist[idom[i]]);