2 * SubCircuit -- An implementation of the Ullmann Subgraph Isomorphism
3 * algorithm for coarse grain logic networks
5 * Copyright (C) 2013 Claire Xenia Wolf <claire@yosyshq.com>
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include "subcircuit.h"
29 # include "kernel/yosys.h"
30 # define my_printf YOSYS_NAMESPACE_PREFIX log
32 # define my_printf printf
35 using namespace SubCircuit
;
38 static std::string
my_stringf(const char *fmt
, ...)
45 if (vasprintf(&str
, fmt
, ap
) < 0)
57 # define my_stringf YOSYS_NAMESPACE_PREFIX stringf
60 SubCircuit::Graph::Graph(const Graph
&other
, const std::vector
<std::string
> &otherNodes
)
62 allExtern
= other
.allExtern
;
64 std::map
<int, int> other2this
;
65 for (int i
= 0; i
< int(otherNodes
.size()); i
++) {
66 assert(other
.nodeMap
.count(otherNodes
[i
]) > 0);
67 other2this
[other
.nodeMap
.at(otherNodes
[i
])] = i
;
68 nodeMap
[otherNodes
[i
]] = i
;
71 std::map
<int, int> edges2this
;
72 for (auto &i1
: other2this
)
73 for (auto &i2
: other
.nodes
[i1
.first
].ports
)
74 for (auto &i3
: i2
.bits
)
75 if (edges2this
.count(i3
.edgeIdx
) == 0) {
76 int next_idx
= edges2this
.size();
77 edges2this
[i3
.edgeIdx
] = next_idx
;
80 edges
.resize(edges2this
.size());
81 for (auto &it
: edges2this
) {
82 for (auto &bit
: other
.edges
[it
.first
].portBits
)
83 if (other2this
.count(bit
.nodeIdx
) > 0)
84 edges
[it
.second
].portBits
.insert(BitRef(other2this
[bit
.nodeIdx
], bit
.portIdx
, bit
.bitIdx
));
85 edges
[it
.second
].constValue
= other
.edges
[it
.first
].constValue
;
86 edges
[it
.second
].isExtern
= other
.edges
[it
.first
].isExtern
;
89 nodes
.resize(other2this
.size());
90 for (auto &it
: other2this
) {
91 nodes
[it
.second
] = other
.nodes
[it
.first
];
92 for (auto &i2
: nodes
[it
.second
].ports
)
93 for (auto &i3
: i2
.bits
)
94 i3
.edgeIdx
= edges2this
.at(i3
.edgeIdx
);
98 bool SubCircuit::Graph::BitRef::operator < (const BitRef
&other
) const
100 if (nodeIdx
!= other
.nodeIdx
)
101 return nodeIdx
< other
.nodeIdx
;
102 if (portIdx
!= other
.portIdx
)
103 return portIdx
< other
.portIdx
;
104 return bitIdx
< other
.bitIdx
;
107 void SubCircuit::Graph::createNode(std::string nodeId
, std::string typeId
, void *userData
, bool shared
)
109 assert(nodeMap
.count(nodeId
) == 0);
110 nodeMap
[nodeId
] = nodes
.size();
111 nodes
.push_back(Node());
113 Node
&newNode
= nodes
.back();
114 newNode
.nodeId
= nodeId
;
115 newNode
.typeId
= typeId
;
116 newNode
.userData
= userData
;
117 newNode
.shared
= shared
;
120 void SubCircuit::Graph::createPort(std::string nodeId
, std::string portId
, int width
, int minWidth
)
122 assert(nodeMap
.count(nodeId
) != 0);
123 int nodeIdx
= nodeMap
[nodeId
];
124 Node
&node
= nodes
[nodeIdx
];
126 assert(node
.portMap
.count(portId
) == 0);
128 int portIdx
= node
.ports
.size();
129 node
.portMap
[portId
] = portIdx
;
130 node
.ports
.push_back(Port());
131 Port
&port
= node
.ports
.back();
133 port
.portId
= portId
;
134 port
.minWidth
= minWidth
< 0 ? width
: minWidth
;
135 port
.bits
.insert(port
.bits
.end(), width
, PortBit());
137 for (int i
= 0; i
< width
; i
++) {
138 port
.bits
[i
].edgeIdx
= edges
.size();
139 edges
.push_back(Edge());
140 edges
.back().portBits
.insert(BitRef(nodeIdx
, portIdx
, i
));
144 void SubCircuit::Graph::createConnection(std::string fromNodeId
, std::string fromPortId
, int fromBit
, std::string toNodeId
, std::string toPortId
, int toBit
, int width
)
146 assert(nodeMap
.count(fromNodeId
) != 0);
147 assert(nodeMap
.count(toNodeId
) != 0);
149 int fromNodeIdx
= nodeMap
[fromNodeId
];
150 Node
&fromNode
= nodes
[fromNodeIdx
];
152 int toNodeIdx
= nodeMap
[toNodeId
];
153 Node
&toNode
= nodes
[toNodeIdx
];
155 assert(fromNode
.portMap
.count(fromPortId
) != 0);
156 assert(toNode
.portMap
.count(toPortId
) != 0);
158 int fromPortIdx
= fromNode
.portMap
[fromPortId
];
159 Port
&fromPort
= fromNode
.ports
[fromPortIdx
];
161 int toPortIdx
= toNode
.portMap
[toPortId
];
162 Port
&toPort
= toNode
.ports
[toPortIdx
];
165 assert(fromBit
== 0 && toBit
== 0);
166 assert(fromPort
.bits
.size() == toPort
.bits
.size());
167 width
= fromPort
.bits
.size();
170 assert(fromBit
>= 0 && toBit
>= 0);
171 for (int i
= 0; i
< width
; i
++)
173 assert(fromBit
+ i
< int(fromPort
.bits
.size()));
174 assert(toBit
+ i
< int(toPort
.bits
.size()));
176 int fromEdgeIdx
= fromPort
.bits
[fromBit
+ i
].edgeIdx
;
177 int toEdgeIdx
= toPort
.bits
[toBit
+ i
].edgeIdx
;
179 if (fromEdgeIdx
== toEdgeIdx
)
182 // merge toEdge into fromEdge
183 if (edges
[toEdgeIdx
].isExtern
)
184 edges
[fromEdgeIdx
].isExtern
= true;
185 if (edges
[toEdgeIdx
].constValue
) {
186 assert(edges
[fromEdgeIdx
].constValue
== 0);
187 edges
[fromEdgeIdx
].constValue
= edges
[toEdgeIdx
].constValue
;
189 for (const auto &ref
: edges
[toEdgeIdx
].portBits
) {
190 edges
[fromEdgeIdx
].portBits
.insert(ref
);
191 nodes
[ref
.nodeIdx
].ports
[ref
.portIdx
].bits
[ref
.bitIdx
].edgeIdx
= fromEdgeIdx
;
194 // remove toEdge (move last edge over toEdge if needed)
195 if (toEdgeIdx
+1 != int(edges
.size())) {
196 edges
[toEdgeIdx
] = edges
.back();
197 for (const auto &ref
: edges
[toEdgeIdx
].portBits
)
198 nodes
[ref
.nodeIdx
].ports
[ref
.portIdx
].bits
[ref
.bitIdx
].edgeIdx
= toEdgeIdx
;
204 void SubCircuit::Graph::createConnection(std::string fromNodeId
, std::string fromPortId
, std::string toNodeId
, std::string toPortId
)
206 createConnection(fromNodeId
, fromPortId
, 0, toNodeId
, toPortId
, 0, -1);
209 void SubCircuit::Graph::createConstant(std::string toNodeId
, std::string toPortId
, int toBit
, int constValue
)
211 assert(nodeMap
.count(toNodeId
) != 0);
212 int toNodeIdx
= nodeMap
[toNodeId
];
213 Node
&toNode
= nodes
[toNodeIdx
];
215 assert(toNode
.portMap
.count(toPortId
) != 0);
216 int toPortIdx
= toNode
.portMap
[toPortId
];
217 Port
&toPort
= toNode
.ports
[toPortIdx
];
219 assert(toBit
>= 0 && toBit
< int(toPort
.bits
.size()));
220 int toEdgeIdx
= toPort
.bits
[toBit
].edgeIdx
;
222 assert(edges
[toEdgeIdx
].constValue
== 0);
223 edges
[toEdgeIdx
].constValue
= constValue
;
226 void SubCircuit::Graph::createConstant(std::string toNodeId
, std::string toPortId
, int constValue
)
228 assert(nodeMap
.count(toNodeId
) != 0);
229 int toNodeIdx
= nodeMap
[toNodeId
];
230 Node
&toNode
= nodes
[toNodeIdx
];
232 assert(toNode
.portMap
.count(toPortId
) != 0);
233 int toPortIdx
= toNode
.portMap
[toPortId
];
234 Port
&toPort
= toNode
.ports
[toPortIdx
];
236 for (int i
= 0; i
< int(toPort
.bits
.size()); i
++) {
237 int toEdgeIdx
= toPort
.bits
[i
].edgeIdx
;
238 assert(edges
[toEdgeIdx
].constValue
== 0);
239 edges
[toEdgeIdx
].constValue
= constValue
% 2 ? '1' : '0';
240 constValue
= constValue
>> 1;
244 void SubCircuit::Graph::markExtern(std::string nodeId
, std::string portId
, int bit
)
246 assert(nodeMap
.count(nodeId
) != 0);
247 Node
&node
= nodes
[nodeMap
[nodeId
]];
249 assert(node
.portMap
.count(portId
) != 0);
250 Port
&port
= node
.ports
[node
.portMap
[portId
]];
253 for (const auto portBit
: port
.bits
)
254 edges
[portBit
.edgeIdx
].isExtern
= true;
256 assert(bit
< int(port
.bits
.size()));
257 edges
[port
.bits
[bit
].edgeIdx
].isExtern
= true;
261 void SubCircuit::Graph::markAllExtern()
266 void SubCircuit::Graph::print()
268 for (int i
= 0; i
< int(nodes
.size()); i
++) {
269 const Node
&node
= nodes
[i
];
270 my_printf("NODE %d: %s (%s)\n", i
, node
.nodeId
.c_str(), node
.typeId
.c_str());
271 for (int j
= 0; j
< int(node
.ports
.size()); j
++) {
272 const Port
&port
= node
.ports
[j
];
273 my_printf(" PORT %d: %s (%d/%d)\n", j
, port
.portId
.c_str(), port
.minWidth
, int(port
.bits
.size()));
274 for (int k
= 0; k
< int(port
.bits
.size()); k
++) {
275 int edgeIdx
= port
.bits
[k
].edgeIdx
;
276 my_printf(" BIT %d (%d):", k
, edgeIdx
);
277 for (const auto &ref
: edges
[edgeIdx
].portBits
)
278 my_printf(" %d.%d.%d", ref
.nodeIdx
, ref
.portIdx
, ref
.bitIdx
);
279 if (edges
[edgeIdx
].isExtern
)
280 my_printf(" [extern]");
287 class SubCircuit::SolverWorker
289 // basic internal data structures
291 typedef std::vector
<std::map
<int, int>> adjMatrix_t
;
296 adjMatrix_t adjMatrix
;
297 std::vector
<bool> usedNodes
;
300 static void printAdjMatrix(const adjMatrix_t
&matrix
)
302 my_printf("%7s", "");
303 for (int i
= 0; i
< int(matrix
.size()); i
++)
304 my_printf("%4d:", i
);
306 for (int i
= 0; i
< int(matrix
.size()); i
++) {
307 my_printf("%5d:", i
);
308 for (int j
= 0; j
< int(matrix
.size()); j
++)
309 if (matrix
.at(i
).count(j
) == 0)
310 my_printf("%5s", "-");
312 my_printf("%5d", matrix
.at(i
).at(j
));
317 // helper functions for handling permutations
319 static constexpr int maxPermutationsLimit
= 1000000;
321 static int numberOfPermutations(const std::vector
<std::string
> &list
)
323 constexpr size_t mappedPermutationsSize
= 10;
324 constexpr int mappedPermutations
[mappedPermutationsSize
] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
325 assert(list
.size() < mappedPermutationsSize
);
326 return mappedPermutations
[list
.size()];
329 static void permutateVectorToMap(std::map
<std::string
, std::string
> &map
, const std::vector
<std::string
> &list
, int idx
)
331 // convert idx to a list.size() digits factoradic number
333 std::vector
<int> factoradicDigits
;
334 for (int i
= 0; i
< int(list
.size()); i
++) {
335 factoradicDigits
.push_back(idx
% (i
+1));
339 // construct permutation
341 std::vector
<std::string
> pool
= list
;
342 std::vector
<std::string
> permutation
;
344 while (!factoradicDigits
.empty()) {
345 int i
= factoradicDigits
.back();
346 factoradicDigits
.pop_back();
347 permutation
.push_back(pool
[i
]);
348 pool
.erase(pool
.begin() + i
);
353 for (int i
= 0; i
< int(list
.size()); i
++)
354 map
[list
[i
]] = permutation
[i
];
357 static int numberOfPermutationsArray(const std::vector
<std::vector
<std::string
>> &list
)
359 int numPermutations
= 1;
360 for (const auto &it
: list
) {
361 int thisPermutations
= numberOfPermutations(it
);
362 assert(float(numPermutations
) * float(thisPermutations
) < maxPermutationsLimit
);
363 numPermutations
*= thisPermutations
;
365 return numPermutations
;
368 static void permutateVectorToMapArray(std::map
<std::string
, std::string
> &map
, const std::vector
<std::vector
<std::string
>> &list
, int idx
)
370 for (const auto &it
: list
) {
371 int thisPermutations
= numberOfPermutations(it
);
372 int thisIdx
= idx
% thisPermutations
;
373 permutateVectorToMap(map
, it
, thisIdx
);
374 idx
/= thisPermutations
;
378 static void applyPermutation(std::map
<std::string
, std::string
> &map
, const std::map
<std::string
, std::string
> &permutation
)
380 std::vector
<std::pair
<std::string
, std::string
>> changeLog
;
381 for (const auto &it
: permutation
)
382 if (map
.count(it
.second
))
383 changeLog
.push_back(std::pair
<std::string
, std::string
>(it
.first
, map
.at(it
.second
)));
385 changeLog
.push_back(std::pair
<std::string
, std::string
>(it
.first
, it
.second
));
386 for (const auto &it
: changeLog
)
387 map
[it
.first
] = it
.second
;
390 // classes for internal digraph representation
394 std::string fromPort
, toPort
;
397 DiBit() : fromPort(), toPort(), fromBit(-1), toBit(-1) { }
398 DiBit(std::string fromPort
, int fromBit
, std::string toPort
, int toBit
) : fromPort(fromPort
), toPort(toPort
), fromBit(fromBit
), toBit(toBit
) { }
400 bool operator < (const DiBit
&other
) const
402 if (fromPort
!= other
.fromPort
)
403 return fromPort
< other
.fromPort
;
404 if (toPort
!= other
.toPort
)
405 return toPort
< other
.toPort
;
406 if (fromBit
!= other
.fromBit
)
407 return fromBit
< other
.fromBit
;
408 return toBit
< other
.toBit
;
411 std::string
toString() const
413 return my_stringf("%s[%d]:%s[%d]", fromPort
.c_str(), fromBit
, toPort
.c_str(), toBit
);
420 std::map
<std::string
, int> portSizes
;
426 DiNode(const Graph
&graph
, int nodeIdx
)
428 const Graph::Node
&node
= graph
.nodes
.at(nodeIdx
);
429 typeId
= node
.typeId
;
430 for (const auto &port
: node
.ports
)
431 portSizes
[port
.portId
] = port
.bits
.size();
434 bool operator < (const DiNode
&other
) const
436 if (typeId
!= other
.typeId
)
437 return typeId
< other
.typeId
;
438 return portSizes
< other
.portSizes
;
441 std::string
toString() const
444 bool firstPort
= true;
445 for (const auto &it
: portSizes
) {
446 str
+= my_stringf("%s%s[%d]", firstPort
? "" : ",", it
.first
.c_str(), it
.second
);
449 return typeId
+ "(" + str
+ ")";
455 DiNode fromNode
, toNode
;
456 std::set
<DiBit
> bits
;
457 std::string userAnnotation
;
459 bool operator < (const DiEdge
&other
) const
461 if (fromNode
< other
.fromNode
|| other
.fromNode
< fromNode
)
462 return fromNode
< other
.fromNode
;
463 if (toNode
< other
.toNode
|| other
.toNode
< toNode
)
464 return toNode
< other
.toNode
;
465 if (bits
< other
.bits
|| other
.bits
< bits
)
466 return bits
< other
.bits
;
467 return userAnnotation
< other
.userAnnotation
;
470 bool compare(const DiEdge
&other
, const std::map
<std::string
, std::string
> &mapFromPorts
, const std::map
<std::string
, std::string
> &mapToPorts
) const
472 // Rules for matching edges:
474 // For all bits in the needle edge:
475 // - ignore if needle ports don't exist in haystack edge
476 // - otherwise: matching bit in haystack edge must exist
478 // There is no need to check in the other direction, as checking
479 // of the isExtern properties is already performed in node matching.
481 // Note: "this" is needle, "other" is haystack
483 for (auto bit
: bits
)
485 if (mapFromPorts
.count(bit
.fromPort
) > 0)
486 bit
.fromPort
= mapFromPorts
.at(bit
.fromPort
);
487 if (mapToPorts
.count(bit
.toPort
) > 0)
488 bit
.toPort
= mapToPorts
.at(bit
.toPort
);
490 if (other
.fromNode
.portSizes
.count(bit
.fromPort
) == 0)
492 if (other
.toNode
.portSizes
.count(bit
.toPort
) == 0)
495 if (bit
.fromBit
>= other
.fromNode
.portSizes
.at(bit
.fromPort
))
497 if (bit
.toBit
>= other
.toNode
.portSizes
.at(bit
.toPort
))
500 if (other
.bits
.count(bit
) == 0)
507 bool compareWithFromAndToPermutations(const DiEdge
&other
, const std::map
<std::string
, std::string
> &mapFromPorts
, const std::map
<std::string
, std::string
> &mapToPorts
,
508 const std::map
<std::string
, std::set
<std::map
<std::string
, std::string
>>> &swapPermutations
) const
510 if (swapPermutations
.count(fromNode
.typeId
) > 0)
511 for (const auto &permutation
: swapPermutations
.at(fromNode
.typeId
)) {
512 std::map
<std::string
, std::string
> thisMapFromPorts
= mapFromPorts
;
513 applyPermutation(thisMapFromPorts
, permutation
);
514 if (compareWithToPermutations(other
, thisMapFromPorts
, mapToPorts
, swapPermutations
))
517 return compareWithToPermutations(other
, mapFromPorts
, mapToPorts
, swapPermutations
);
520 bool compareWithToPermutations(const DiEdge
&other
, const std::map
<std::string
, std::string
> &mapFromPorts
, const std::map
<std::string
, std::string
> &mapToPorts
,
521 const std::map
<std::string
, std::set
<std::map
<std::string
, std::string
>>> &swapPermutations
) const
523 if (swapPermutations
.count(toNode
.typeId
) > 0)
524 for (const auto &permutation
: swapPermutations
.at(toNode
.typeId
)) {
525 std::map
<std::string
, std::string
> thisMapToPorts
= mapToPorts
;
526 applyPermutation(thisMapToPorts
, permutation
);
527 if (compare(other
, mapFromPorts
, thisMapToPorts
))
530 return compare(other
, mapFromPorts
, mapToPorts
);
533 bool compare(const DiEdge
&other
, const std::map
<std::string
, std::set
<std::set
<std::string
>>> &swapPorts
,
534 const std::map
<std::string
, std::set
<std::map
<std::string
, std::string
>>> &swapPermutations
) const
536 // brute force method for port swapping: try all variations
538 std::vector
<std::vector
<std::string
>> swapFromPorts
;
539 std::vector
<std::vector
<std::string
>> swapToPorts
;
541 // only use groups that are relevant for this edge
543 if (swapPorts
.count(fromNode
.typeId
) > 0)
544 for (const auto &ports
: swapPorts
.at(fromNode
.typeId
)) {
545 for (const auto &bit
: bits
)
546 if (ports
.count(bit
.fromPort
))
547 goto foundFromPortMatch
;
550 std::vector
<std::string
> portsVector
;
551 for (const auto &port
: ports
)
552 portsVector
.push_back(port
);
553 swapFromPorts
.push_back(portsVector
);
557 if (swapPorts
.count(toNode
.typeId
) > 0)
558 for (const auto &ports
: swapPorts
.at(toNode
.typeId
)) {
559 for (const auto &bit
: bits
)
560 if (ports
.count(bit
.toPort
))
561 goto foundToPortMatch
;
564 std::vector
<std::string
> portsVector
;
565 for (const auto &port
: ports
)
566 portsVector
.push_back(port
);
567 swapToPorts
.push_back(portsVector
);
571 // try all permutations
573 std::map
<std::string
, std::string
> mapFromPorts
, mapToPorts
;
574 int fromPortsPermutations
= numberOfPermutationsArray(swapFromPorts
);
575 int toPortsPermutations
= numberOfPermutationsArray(swapToPorts
);
577 for (int i
= 0; i
< fromPortsPermutations
; i
++)
579 permutateVectorToMapArray(mapFromPorts
, swapFromPorts
, i
);
581 for (int j
= 0; j
< toPortsPermutations
; j
++) {
582 permutateVectorToMapArray(mapToPorts
, swapToPorts
, j
);
583 if (compareWithFromAndToPermutations(other
, mapFromPorts
, mapToPorts
, swapPermutations
))
591 bool compare(const DiEdge
&other
, const std::map
<std::string
, std::string
> &mapFromPorts
, const std::map
<std::string
, std::set
<std::set
<std::string
>>> &swapPorts
,
592 const std::map
<std::string
, std::set
<std::map
<std::string
, std::string
>>> &swapPermutations
) const
594 // strip-down version of the last function: only try permutations for mapToPorts, mapFromPorts is already provided by the caller
596 std::vector
<std::vector
<std::string
>> swapToPorts
;
598 if (swapPorts
.count(toNode
.typeId
) > 0)
599 for (const auto &ports
: swapPorts
.at(toNode
.typeId
)) {
600 for (const auto &bit
: bits
)
601 if (ports
.count(bit
.toPort
))
602 goto foundToPortMatch
;
605 std::vector
<std::string
> portsVector
;
606 for (const auto &port
: ports
)
607 portsVector
.push_back(port
);
608 swapToPorts
.push_back(portsVector
);
612 std::map
<std::string
, std::string
> mapToPorts
;
613 int toPortsPermutations
= numberOfPermutationsArray(swapToPorts
);
615 for (int j
= 0; j
< toPortsPermutations
; j
++) {
616 permutateVectorToMapArray(mapToPorts
, swapToPorts
, j
);
617 if (compareWithToPermutations(other
, mapFromPorts
, mapToPorts
, swapPermutations
))
624 std::string
toString() const
626 std::string buffer
= fromNode
.toString() + " " + toNode
.toString();
627 for (const auto &bit
: bits
)
628 buffer
+= " " + bit
.toString();
629 if (!userAnnotation
.empty())
630 buffer
+= " " + userAnnotation
;
634 static void findEdgesInGraph(const Graph
&graph
, std::map
<std::pair
<int, int>, DiEdge
> &edges
)
637 for (const auto &edge
: graph
.edges
) {
638 if (edge
.constValue
!= 0)
640 for (const auto &fromBit
: edge
.portBits
)
641 for (const auto &toBit
: edge
.portBits
)
642 if (&fromBit
!= &toBit
) {
643 DiEdge
&de
= edges
[std::pair
<int, int>(fromBit
.nodeIdx
, toBit
.nodeIdx
)];
644 de
.fromNode
= DiNode(graph
, fromBit
.nodeIdx
);
645 de
.toNode
= DiNode(graph
, toBit
.nodeIdx
);
646 std::string fromPortId
= graph
.nodes
[fromBit
.nodeIdx
].ports
[fromBit
.portIdx
].portId
;
647 std::string toPortId
= graph
.nodes
[toBit
.nodeIdx
].ports
[toBit
.portIdx
].portId
;
648 de
.bits
.insert(DiBit(fromPortId
, fromBit
.bitIdx
, toPortId
, toBit
.bitIdx
));
656 std::map
<DiEdge
, int> edgeTypesMap
;
657 std::vector
<DiEdge
> edgeTypes
;
658 std::map
<std::pair
<int, int>, bool> compareCache
;
660 void add(const Graph
&graph
, adjMatrix_t
&adjMatrix
, const std::string
&graphId
, Solver
*userSolver
)
662 std::map
<std::pair
<int, int>, DiEdge
> edges
;
663 DiEdge::findEdgesInGraph(graph
, edges
);
666 adjMatrix
.resize(graph
.nodes
.size());
668 for (auto &it
: edges
) {
669 const Graph::Node
&fromNode
= graph
.nodes
[it
.first
.first
];
670 const Graph::Node
&toNode
= graph
.nodes
[it
.first
.second
];
671 it
.second
.userAnnotation
= userSolver
->userAnnotateEdge(graphId
, fromNode
.nodeId
, fromNode
.userData
, toNode
.nodeId
, toNode
.userData
);
674 for (const auto &it
: edges
) {
675 if (edgeTypesMap
.count(it
.second
) == 0) {
676 edgeTypesMap
[it
.second
] = edgeTypes
.size();
677 edgeTypes
.push_back(it
.second
);
679 adjMatrix
[it
.first
.first
][it
.first
.second
] = edgeTypesMap
[it
.second
];
683 bool compare(int needleEdge
, int haystackEdge
, const std::map
<std::string
, std::set
<std::set
<std::string
>>> &swapPorts
,
684 const std::map
<std::string
, std::set
<std::map
<std::string
, std::string
>>> &swapPermutations
)
686 std::pair
<int, int> key(needleEdge
, haystackEdge
);
687 if (!compareCache
.count(key
))
688 compareCache
[key
] = edgeTypes
.at(needleEdge
).compare(edgeTypes
.at(haystackEdge
), swapPorts
, swapPermutations
);
689 return compareCache
[key
];
692 bool compare(int needleEdge
, int haystackEdge
, const std::map
<std::string
, std::string
> &mapFromPorts
, const std::map
<std::string
, std::set
<std::set
<std::string
>>> &swapPorts
,
693 const std::map
<std::string
, std::set
<std::map
<std::string
, std::string
>>> &swapPermutations
) const
695 return edgeTypes
.at(needleEdge
).compare(edgeTypes
.at(haystackEdge
), mapFromPorts
, swapPorts
, swapPermutations
);
698 bool compare(int needleEdge
, int haystackEdge
, const std::map
<std::string
, std::string
> &mapFromPorts
, const std::map
<std::string
, std::string
> &mapToPorts
) const
700 return edgeTypes
.at(needleEdge
).compare(edgeTypes
.at(haystackEdge
), mapFromPorts
, mapToPorts
);
703 void printEdgeTypes() const
705 for (int i
= 0; i
< int(edgeTypes
.size()); i
++)
706 my_printf("%5d: %s\n", i
, edgeTypes
[i
].toString().c_str());
710 // solver state variables
713 std::map
<std::string
, GraphData
> graphData
;
714 std::map
<std::string
, std::set
<std::string
>> compatibleTypes
;
715 std::map
<int, std::set
<int>> compatibleConstants
;
716 std::map
<std::string
, std::set
<std::set
<std::string
>>> swapPorts
;
717 std::map
<std::string
, std::set
<std::map
<std::string
, std::string
>>> swapPermutations
;
721 // main solver functions
723 bool matchNodePorts(const Graph
&needle
, int needleNodeIdx
, const Graph
&haystack
, int haystackNodeIdx
, const std::map
<std::string
, std::string
> &swaps
) const
725 const Graph::Node
&nn
= needle
.nodes
[needleNodeIdx
];
726 const Graph::Node
&hn
= haystack
.nodes
[haystackNodeIdx
];
727 assert(nn
.ports
.size() == hn
.ports
.size());
729 for (int i
= 0; i
< int(nn
.ports
.size()); i
++)
731 std::string hnPortId
= nn
.ports
[i
].portId
;
732 if (swaps
.count(hnPortId
) > 0)
733 hnPortId
= swaps
.at(hnPortId
);
735 if (hn
.portMap
.count(hnPortId
) == 0)
738 const Graph::Port
&np
= nn
.ports
[i
];
739 const Graph::Port
&hp
= hn
.ports
[hn
.portMap
.at(hnPortId
)];
741 if (int(hp
.bits
.size()) < np
.minWidth
|| hp
.bits
.size() > np
.bits
.size())
744 for (int j
= 0; j
< int(hp
.bits
.size()); j
++)
746 const Graph::Edge
&ne
= needle
.edges
[np
.bits
[j
].edgeIdx
];
747 const Graph::Edge
&he
= haystack
.edges
[hp
.bits
[j
].edgeIdx
];
749 if (ne
.constValue
|| he
.constValue
) {
750 if (ne
.constValue
!= he
.constValue
)
751 if (compatibleConstants
.count(ne
.constValue
) == 0 || compatibleConstants
.at(ne
.constValue
).count(he
.constValue
) == 0)
756 if (ne
.isExtern
|| needle
.allExtern
) {
757 if (he
.portBits
.size() < ne
.portBits
.size())
760 if (he
.portBits
.size() != ne
.portBits
.size())
762 if (he
.isExtern
|| haystack
.allExtern
)
771 bool matchNodes(const GraphData
&needle
, int needleNodeIdx
, const GraphData
&haystack
, int haystackNodeIdx
) const
773 // Rules for matching nodes:
775 // 1. their typeId must be identical or compatible
776 // (this is checked before calling this function)
778 // 2. they must have the same ports and the haystack port
779 // widths must match the needle port width range
781 // 3. All edges from the needle must match the haystack:
782 // a) if the needle edge is extern:
783 // - the haystack edge must have at least as many components as the needle edge
784 // b) if the needle edge is not extern:
785 // - the haystack edge must have the same number of components as the needle edge
786 // - the haystack edge must not be extern
788 const Graph::Node
&nn
= needle
.graph
.nodes
[needleNodeIdx
];
789 const Graph::Node
&hn
= haystack
.graph
.nodes
[haystackNodeIdx
];
791 assert(nn
.typeId
== hn
.typeId
|| (compatibleTypes
.count(nn
.typeId
) > 0 && compatibleTypes
.at(nn
.typeId
).count(hn
.typeId
) > 0));
793 if (nn
.ports
.size() != hn
.ports
.size())
796 std::map
<std::string
, std::string
> currentCandidate
;
798 for (const auto &port
: needle
.graph
.nodes
[needleNodeIdx
].ports
)
799 currentCandidate
[port
.portId
] = port
.portId
;
801 if (swapPorts
.count(needle
.graph
.nodes
[needleNodeIdx
].typeId
) == 0)
803 if (matchNodePorts(needle
.graph
, needleNodeIdx
, haystack
.graph
, haystackNodeIdx
, currentCandidate
) &&
804 userSolver
->userCompareNodes(needle
.graphId
, nn
.nodeId
, nn
.userData
, haystack
.graphId
, hn
.nodeId
, hn
.userData
, currentCandidate
))
807 if (swapPermutations
.count(needle
.graph
.nodes
[needleNodeIdx
].typeId
) > 0)
808 for (const auto &permutation
: swapPermutations
.at(needle
.graph
.nodes
[needleNodeIdx
].typeId
)) {
809 std::map
<std::string
, std::string
> currentSubCandidate
= currentCandidate
;
810 applyPermutation(currentSubCandidate
, permutation
);
811 if (matchNodePorts(needle
.graph
, needleNodeIdx
, haystack
.graph
, haystackNodeIdx
, currentCandidate
) &&
812 userSolver
->userCompareNodes(needle
.graphId
, nn
.nodeId
, nn
.userData
, haystack
.graphId
, hn
.nodeId
, hn
.userData
, currentCandidate
))
818 std::vector
<std::vector
<std::string
>> thisSwapPorts
;
819 for (const auto &ports
: swapPorts
.at(needle
.graph
.nodes
[needleNodeIdx
].typeId
)) {
820 std::vector
<std::string
> portsVector
;
821 for (const auto &port
: ports
)
822 portsVector
.push_back(port
);
823 thisSwapPorts
.push_back(portsVector
);
826 int thisPermutations
= numberOfPermutationsArray(thisSwapPorts
);
827 for (int i
= 0; i
< thisPermutations
; i
++)
829 permutateVectorToMapArray(currentCandidate
, thisSwapPorts
, i
);
831 if (matchNodePorts(needle
.graph
, needleNodeIdx
, haystack
.graph
, haystackNodeIdx
, currentCandidate
) &&
832 userSolver
->userCompareNodes(needle
.graphId
, nn
.nodeId
, nn
.userData
, haystack
.graphId
, hn
.nodeId
, hn
.userData
, currentCandidate
))
835 if (swapPermutations
.count(needle
.graph
.nodes
[needleNodeIdx
].typeId
) > 0)
836 for (const auto &permutation
: swapPermutations
.at(needle
.graph
.nodes
[needleNodeIdx
].typeId
)) {
837 std::map
<std::string
, std::string
> currentSubCandidate
= currentCandidate
;
838 applyPermutation(currentSubCandidate
, permutation
);
839 if (matchNodePorts(needle
.graph
, needleNodeIdx
, haystack
.graph
, haystackNodeIdx
, currentCandidate
) &&
840 userSolver
->userCompareNodes(needle
.graphId
, nn
.nodeId
, nn
.userData
, haystack
.graphId
, hn
.nodeId
, hn
.userData
, currentCandidate
))
849 void generateEnumerationMatrix(std::vector
<std::set
<int>> &enumerationMatrix
, const GraphData
&needle
, const GraphData
&haystack
, const std::map
<std::string
, std::set
<std::string
>> &initialMappings
) const
851 std::map
<std::string
, std::set
<int>> haystackNodesByTypeId
;
852 for (int i
= 0; i
< int(haystack
.graph
.nodes
.size()); i
++)
853 haystackNodesByTypeId
[haystack
.graph
.nodes
[i
].typeId
].insert(i
);
855 enumerationMatrix
.clear();
856 enumerationMatrix
.resize(needle
.graph
.nodes
.size());
857 for (int i
= 0; i
< int(needle
.graph
.nodes
.size()); i
++)
859 const Graph::Node
&nn
= needle
.graph
.nodes
[i
];
861 for (int j
: haystackNodesByTypeId
[nn
.typeId
]) {
862 const Graph::Node
&hn
= haystack
.graph
.nodes
[j
];
863 if (initialMappings
.count(nn
.nodeId
) > 0 && initialMappings
.at(nn
.nodeId
).count(hn
.nodeId
) == 0)
865 if (!matchNodes(needle
, i
, haystack
, j
))
867 enumerationMatrix
[i
].insert(j
);
870 if (compatibleTypes
.count(nn
.typeId
) > 0)
871 for (const std::string
&compatibleTypeId
: compatibleTypes
.at(nn
.typeId
))
872 for (int j
: haystackNodesByTypeId
[compatibleTypeId
]) {
873 const Graph::Node
&hn
= haystack
.graph
.nodes
[j
];
874 if (initialMappings
.count(nn
.nodeId
) > 0 && initialMappings
.at(nn
.nodeId
).count(hn
.nodeId
) == 0)
876 if (!matchNodes(needle
, i
, haystack
, j
))
878 enumerationMatrix
[i
].insert(j
);
883 bool checkEnumerationMatrix(std::vector
<std::set
<int>> &enumerationMatrix
, int i
, int j
, const GraphData
&needle
, const GraphData
&haystack
)
885 for (const auto &it_needle
: needle
.adjMatrix
.at(i
))
887 int needleNeighbour
= it_needle
.first
;
888 int needleEdgeType
= it_needle
.second
;
890 for (int haystackNeighbour
: enumerationMatrix
[needleNeighbour
])
891 if (haystack
.adjMatrix
.at(j
).count(haystackNeighbour
) > 0) {
892 int haystackEdgeType
= haystack
.adjMatrix
.at(j
).at(haystackNeighbour
);
893 if (diCache
.compare(needleEdgeType
, haystackEdgeType
, swapPorts
, swapPermutations
)) {
894 const Graph::Node
&needleFromNode
= needle
.graph
.nodes
[i
];
895 const Graph::Node
&needleToNode
= needle
.graph
.nodes
[needleNeighbour
];
896 const Graph::Node
&haystackFromNode
= haystack
.graph
.nodes
[j
];
897 const Graph::Node
&haystackToNode
= haystack
.graph
.nodes
[haystackNeighbour
];
898 if (userSolver
->userCompareEdge(needle
.graphId
, needleFromNode
.nodeId
, needleFromNode
.userData
, needleToNode
.nodeId
, needleToNode
.userData
,
899 haystack
.graphId
, haystackFromNode
.nodeId
, haystackFromNode
.userData
, haystackToNode
.nodeId
, haystackToNode
.userData
))
911 bool pruneEnumerationMatrix(std::vector
<std::set
<int>> &enumerationMatrix
, const GraphData
&needle
, const GraphData
&haystack
, int &nextRow
, bool allowOverlap
)
913 bool didSomething
= true;
917 didSomething
= false;
918 for (int i
= 0; i
< int(enumerationMatrix
.size()); i
++) {
919 std::set
<int> newRow
;
920 for (int j
: enumerationMatrix
[i
]) {
921 if (!checkEnumerationMatrix(enumerationMatrix
, i
, j
, needle
, haystack
))
923 else if (!allowOverlap
&& haystack
.usedNodes
[j
])
928 if (newRow
.size() == 0)
930 if (newRow
.size() >= 2 && (nextRow
< 0 || needle
.adjMatrix
.at(nextRow
).size() < needle
.adjMatrix
.at(i
).size()))
932 enumerationMatrix
[i
].swap(newRow
);
938 void printEnumerationMatrix(const std::vector
<std::set
<int>> &enumerationMatrix
, int maxHaystackNodeIdx
= -1) const
940 if (maxHaystackNodeIdx
< 0) {
941 for (const auto &it
: enumerationMatrix
)
943 maxHaystackNodeIdx
= std::max(maxHaystackNodeIdx
, idx
);
947 for (int j
= 0; j
< maxHaystackNodeIdx
; j
+= 5)
948 my_printf("%-6d", j
);
951 for (int i
= 0; i
< int(enumerationMatrix
.size()); i
++)
953 my_printf("%5d:", i
);
954 for (int j
= 0; j
< maxHaystackNodeIdx
; j
++) {
957 my_printf("%c", enumerationMatrix
[i
].count(j
) > 0 ? '*' : '.');
963 bool checkPortmapCandidate(const std::vector
<std::set
<int>> &enumerationMatrix
, const GraphData
&needle
, const GraphData
&haystack
, int idx
, const std::map
<std::string
, std::string
> ¤tCandidate
)
965 assert(enumerationMatrix
[idx
].size() == 1);
966 int idxHaystack
= *enumerationMatrix
[idx
].begin();
968 const Graph::Node
&nn
= needle
.graph
.nodes
[idx
];
969 const Graph::Node
&hn
= haystack
.graph
.nodes
[idxHaystack
];
971 if (!matchNodePorts(needle
.graph
, idx
, haystack
.graph
, idxHaystack
, currentCandidate
) ||
972 !userSolver
->userCompareNodes(needle
.graphId
, nn
.nodeId
, nn
.userData
, haystack
.graphId
, hn
.nodeId
, hn
.userData
, currentCandidate
))
975 for (const auto &it_needle
: needle
.adjMatrix
.at(idx
))
977 int needleNeighbour
= it_needle
.first
;
978 int needleEdgeType
= it_needle
.second
;
980 assert(enumerationMatrix
[needleNeighbour
].size() == 1);
981 int haystackNeighbour
= *enumerationMatrix
[needleNeighbour
].begin();
983 assert(haystack
.adjMatrix
.at(idxHaystack
).count(haystackNeighbour
) > 0);
984 int haystackEdgeType
= haystack
.adjMatrix
.at(idxHaystack
).at(haystackNeighbour
);
985 if (!diCache
.compare(needleEdgeType
, haystackEdgeType
, currentCandidate
, swapPorts
, swapPermutations
))
992 void generatePortmapCandidates(std::set
<std::map
<std::string
, std::string
>> &portmapCandidates
, const std::vector
<std::set
<int>> &enumerationMatrix
,
993 const GraphData
&needle
, const GraphData
&haystack
, int idx
)
995 std::map
<std::string
, std::string
> currentCandidate
;
997 for (const auto &port
: needle
.graph
.nodes
[idx
].ports
)
998 currentCandidate
[port
.portId
] = port
.portId
;
1000 if (swapPorts
.count(needle
.graph
.nodes
[idx
].typeId
) == 0)
1002 if (checkPortmapCandidate(enumerationMatrix
, needle
, haystack
, idx
, currentCandidate
))
1003 portmapCandidates
.insert(currentCandidate
);
1005 if (swapPermutations
.count(needle
.graph
.nodes
[idx
].typeId
) > 0)
1006 for (const auto &permutation
: swapPermutations
.at(needle
.graph
.nodes
[idx
].typeId
)) {
1007 std::map
<std::string
, std::string
> currentSubCandidate
= currentCandidate
;
1008 applyPermutation(currentSubCandidate
, permutation
);
1009 if (checkPortmapCandidate(enumerationMatrix
, needle
, haystack
, idx
, currentSubCandidate
))
1010 portmapCandidates
.insert(currentSubCandidate
);
1015 std::vector
<std::vector
<std::string
>> thisSwapPorts
;
1016 for (const auto &ports
: swapPorts
.at(needle
.graph
.nodes
[idx
].typeId
)) {
1017 std::vector
<std::string
> portsVector
;
1018 for (const auto &port
: ports
)
1019 portsVector
.push_back(port
);
1020 thisSwapPorts
.push_back(portsVector
);
1023 int thisPermutations
= numberOfPermutationsArray(thisSwapPorts
);
1024 for (int i
= 0; i
< thisPermutations
; i
++)
1026 permutateVectorToMapArray(currentCandidate
, thisSwapPorts
, i
);
1028 if (checkPortmapCandidate(enumerationMatrix
, needle
, haystack
, idx
, currentCandidate
))
1029 portmapCandidates
.insert(currentCandidate
);
1031 if (swapPermutations
.count(needle
.graph
.nodes
[idx
].typeId
) > 0)
1032 for (const auto &permutation
: swapPermutations
.at(needle
.graph
.nodes
[idx
].typeId
)) {
1033 std::map
<std::string
, std::string
> currentSubCandidate
= currentCandidate
;
1034 applyPermutation(currentSubCandidate
, permutation
);
1035 if (checkPortmapCandidate(enumerationMatrix
, needle
, haystack
, idx
, currentSubCandidate
))
1036 portmapCandidates
.insert(currentSubCandidate
);
1042 bool prunePortmapCandidates(std::vector
<std::set
<std::map
<std::string
, std::string
>>> &portmapCandidates
, std::vector
<std::set
<int>> enumerationMatrix
, const GraphData
&needle
, const GraphData
&haystack
)
1044 bool didSomething
= false;
1046 // strategy #1: prune impossible port mappings
1048 for (int i
= 0; i
< int(needle
.graph
.nodes
.size()); i
++)
1050 assert(enumerationMatrix
[i
].size() == 1);
1051 int j
= *enumerationMatrix
[i
].begin();
1053 std::set
<std::map
<std::string
, std::string
>> thisCandidates
;
1054 portmapCandidates
[i
].swap(thisCandidates
);
1056 for (const auto &testCandidate
: thisCandidates
)
1058 for (const auto &it_needle
: needle
.adjMatrix
.at(i
))
1060 int needleNeighbour
= it_needle
.first
;
1061 int needleEdgeType
= it_needle
.second
;
1063 assert(enumerationMatrix
[needleNeighbour
].size() == 1);
1064 int haystackNeighbour
= *enumerationMatrix
[needleNeighbour
].begin();
1066 assert(haystack
.adjMatrix
.at(j
).count(haystackNeighbour
) > 0);
1067 int haystackEdgeType
= haystack
.adjMatrix
.at(j
).at(haystackNeighbour
);
1069 std::set
<std::map
<std::string
, std::string
>> &candidates
=
1070 i
== needleNeighbour
? thisCandidates
: portmapCandidates
[needleNeighbour
];
1072 for (const auto &otherCandidate
: candidates
) {
1073 if (diCache
.compare(needleEdgeType
, haystackEdgeType
, testCandidate
, otherCandidate
))
1077 didSomething
= true;
1078 goto purgeCandidate
;
1082 portmapCandidates
[i
].insert(testCandidate
);
1086 if (portmapCandidates
[i
].size() == 0)
1093 // strategy #2: prune a single random port mapping
1095 for (int i
= 0; i
< int(needle
.graph
.nodes
.size()); i
++)
1096 if (portmapCandidates
[i
].size() > 1) {
1097 // remove last mapping. this keeps ports unswapped in don't-care situations
1098 portmapCandidates
[i
].erase(--portmapCandidates
[i
].end());
1105 void ullmannRecursion(std::vector
<Solver::Result
> &results
, std::vector
<std::set
<int>> &enumerationMatrix
, int iter
, const GraphData
&needle
, GraphData
&haystack
, bool allowOverlap
, int limitResults
)
1108 if (!pruneEnumerationMatrix(enumerationMatrix
, needle
, haystack
, i
, allowOverlap
))
1113 Solver::Result result
;
1114 result
.needleGraphId
= needle
.graphId
;
1115 result
.haystackGraphId
= haystack
.graphId
;
1117 std::vector
<std::set
<std::map
<std::string
, std::string
>>> portmapCandidates
;
1118 portmapCandidates
.resize(enumerationMatrix
.size());
1120 for (int j
= 0; j
< int(enumerationMatrix
.size()); j
++) {
1121 Solver::ResultNodeMapping mapping
;
1122 mapping
.needleNodeId
= needle
.graph
.nodes
[j
].nodeId
;
1123 mapping
.needleUserData
= needle
.graph
.nodes
[j
].userData
;
1124 mapping
.haystackNodeId
= haystack
.graph
.nodes
[*enumerationMatrix
[j
].begin()].nodeId
;
1125 mapping
.haystackUserData
= haystack
.graph
.nodes
[*enumerationMatrix
[j
].begin()].userData
;
1126 generatePortmapCandidates(portmapCandidates
[j
], enumerationMatrix
, needle
, haystack
, j
);
1127 result
.mappings
[needle
.graph
.nodes
[j
].nodeId
] = mapping
;
1130 while (prunePortmapCandidates(portmapCandidates
, enumerationMatrix
, needle
, haystack
)) { }
1133 my_printf("\nPortmapper results:\n");
1134 for (int j
= 0; j
< int(enumerationMatrix
.size()); j
++) {
1135 my_printf("%5d: %s\n", j
, needle
.graph
.nodes
[j
].nodeId
.c_str());
1136 int variantCounter
= 0;
1137 for (auto &i2
: portmapCandidates
.at(j
)) {
1138 my_printf("%*s variant %2d:", 6, "", variantCounter
++);
1141 my_printf("%s %s -> %s", mapCounter
++ ? "," : "", i3
.first
.c_str(), i3
.second
.c_str());
1147 for (int j
= 0; j
< int(enumerationMatrix
.size()); j
++) {
1148 if (portmapCandidates
[j
].size() == 0) {
1150 my_printf("\nSolution (rejected by portmapper):\n");
1151 printEnumerationMatrix(enumerationMatrix
, haystack
.graph
.nodes
.size());
1155 result
.mappings
[needle
.graph
.nodes
[j
].nodeId
].portMapping
= *portmapCandidates
[j
].begin();
1158 if (!userSolver
->userCheckSolution(result
)) {
1160 my_printf("\nSolution (rejected by userCheckSolution):\n");
1161 printEnumerationMatrix(enumerationMatrix
, haystack
.graph
.nodes
.size());
1166 for (int j
= 0; j
< int(enumerationMatrix
.size()); j
++)
1167 if (!haystack
.graph
.nodes
[*enumerationMatrix
[j
].begin()].shared
)
1168 haystack
.usedNodes
[*enumerationMatrix
[j
].begin()] = true;
1171 my_printf("\nSolution:\n");
1172 printEnumerationMatrix(enumerationMatrix
, haystack
.graph
.nodes
.size());
1175 results
.push_back(result
);
1181 my_printf("Enumeration Matrix at recursion level %d (%d):\n", iter
, i
);
1182 printEnumerationMatrix(enumerationMatrix
, haystack
.graph
.nodes
.size());
1185 std::set
<int> activeRow
;
1186 enumerationMatrix
[i
].swap(activeRow
);
1188 for (int j
: activeRow
)
1191 if (limitResults
>= 0 && int(results
.size()) >= limitResults
)
1194 // already used by other solution -> try next
1195 if (!allowOverlap
&& haystack
.usedNodes
[j
])
1198 // create enumeration matrix for child in recursion tree
1199 std::vector
<std::set
<int>> nextEnumerationMatrix
= enumerationMatrix
;
1200 for (int k
= 0; k
< int(nextEnumerationMatrix
.size()); k
++)
1201 nextEnumerationMatrix
[k
].erase(j
);
1202 nextEnumerationMatrix
[i
].insert(j
);
1205 ullmannRecursion(results
, nextEnumerationMatrix
, iter
+1, needle
, haystack
, allowOverlap
, limitResults
);
1207 // we just have found something -> unroll to top recursion level
1208 if (!allowOverlap
&& haystack
.usedNodes
[j
] && iter
> 0)
1213 // additional data structes and functions for mining
1216 std::string graphId
;
1217 std::set
<int> nodes
;
1218 NodeSet(std::string graphId
, int node1
, int node2
) {
1219 this->graphId
= graphId
;
1220 nodes
.insert(node1
);
1221 nodes
.insert(node2
);
1223 NodeSet(std::string graphId
, const std::vector
<int> &nodes
) {
1224 this->graphId
= graphId
;
1225 for (int node
: nodes
)
1226 this->nodes
.insert(node
);
1228 void extend(const NodeSet
&other
) {
1229 assert(this->graphId
== other
.graphId
);
1230 for (int node
: other
.nodes
)
1233 int extendCandidate(const NodeSet
&other
) const {
1234 if (graphId
!= other
.graphId
)
1237 bool intersect
= false;
1238 for (int node
: other
.nodes
)
1239 if (nodes
.count(node
) > 0)
1243 return intersect
? newNodes
: 0;
1245 bool operator <(const NodeSet
&other
) const {
1246 if (graphId
!= other
.graphId
)
1247 return graphId
< other
.graphId
;
1248 return nodes
< other
.nodes
;
1250 std::string
to_string() const {
1251 std::string str
= graphId
+ "(";
1253 for (int node
: nodes
) {
1254 str
+= my_stringf("%s%d", first
? "" : " ", node
);
1261 void solveForMining(std::vector
<Solver::Result
> &results
, const GraphData
&needle
)
1263 bool backupVerbose
= verbose
;
1266 for (auto &it
: graphData
)
1268 GraphData
&haystack
= it
.second
;
1270 std::vector
<std::set
<int>> enumerationMatrix
;
1271 std::map
<std::string
, std::set
<std::string
>> initialMappings
;
1272 generateEnumerationMatrix(enumerationMatrix
, needle
, haystack
, initialMappings
);
1274 haystack
.usedNodes
.resize(haystack
.graph
.nodes
.size());
1275 ullmannRecursion(results
, enumerationMatrix
, 0, needle
, haystack
, true, -1);
1278 verbose
= backupVerbose
;
1281 int testForMining(std::vector
<Solver::MineResult
> &results
, std::set
<NodeSet
> &usedSets
, std::set
<NodeSet
> &nextPool
, NodeSet
&testSet
,
1282 const std::string
&graphId
, const Graph
&graph
, int minNodes
, int minMatches
, int limitMatchesPerGraph
)
1284 // my_printf("test: %s\n", testSet.to_string().c_str());
1287 std::vector
<std::string
> needle_nodes
;
1288 for (int nodeIdx
: testSet
.nodes
)
1289 needle_nodes
.push_back(graph
.nodes
[nodeIdx
].nodeId
);
1290 needle
.graph
= Graph(graph
, needle_nodes
);
1291 needle
.graph
.markAllExtern();
1292 diCache
.add(needle
.graph
, needle
.adjMatrix
, graphId
, userSolver
);
1294 std::vector
<Solver::Result
> ullmannResults
;
1295 solveForMining(ullmannResults
, needle
);
1298 std::map
<std::string
, int> matchesPerGraph
;
1299 std::set
<NodeSet
> thisNodeSetSet
;
1301 for (auto &it
: ullmannResults
)
1303 std::vector
<int> resultNodes
;
1304 for (auto &i2
: it
.mappings
)
1305 resultNodes
.push_back(graphData
[it
.haystackGraphId
].graph
.nodeMap
[i2
.second
.haystackNodeId
]);
1306 NodeSet
resultSet(it
.haystackGraphId
, resultNodes
);
1308 // my_printf("match: %s%s\n", resultSet.to_string().c_str(), usedSets.count(resultSet) > 0 ? " (dup)" : "");
1311 if (usedSets
.count(resultSet
) > 0) {
1312 // Because of shorted pins isomorphisim is not always bidirectional!
1314 // This means that the following assert is not true in all cases and subgraph A might
1315 // show up in the matches for subgraph B but not vice versa... This also means that the
1316 // order in which subgraphs are processed has an impact on the results set.
1318 assert(thisNodeSetSet
.count(resultSet
) > 0);
1322 if (thisNodeSetSet
.count(resultSet
) > 0)
1326 usedSets
.insert(resultSet
);
1327 thisNodeSetSet
.insert(resultSet
);
1329 matchesPerGraph
[it
.haystackGraphId
]++;
1330 if (limitMatchesPerGraph
< 0 || matchesPerGraph
[it
.haystackGraphId
] < limitMatchesPerGraph
)
1334 if (matches
< minMatches
)
1337 if (minNodes
<= int(testSet
.nodes
.size()))
1339 Solver::MineResult result
;
1340 result
.graphId
= graphId
;
1341 result
.totalMatchesAfterLimits
= matches
;
1342 result
.matchesPerGraph
= matchesPerGraph
;
1343 for (int nodeIdx
: testSet
.nodes
) {
1344 Solver::MineResultNode resultNode
;
1345 resultNode
.nodeId
= graph
.nodes
[nodeIdx
].nodeId
;
1346 resultNode
.userData
= graph
.nodes
[nodeIdx
].userData
;
1347 result
.nodes
.push_back(resultNode
);
1349 results
.push_back(result
);
1352 nextPool
.insert(thisNodeSetSet
.begin(), thisNodeSetSet
.end());
1356 void findNodePairs(std::vector
<Solver::MineResult
> &results
, std::set
<NodeSet
> &nodePairs
, int minNodes
, int minMatches
, int limitMatchesPerGraph
)
1358 int groupCounter
= 0;
1359 std::set
<NodeSet
> usedPairs
;
1363 my_printf("\nMining for frequent node pairs:\n");
1365 for (auto &graph_it
: graphData
)
1366 for (int node1
= 0; node1
< int(graph_it
.second
.graph
.nodes
.size()); node1
++)
1367 for (auto &adj_it
: graph_it
.second
.adjMatrix
.at(node1
))
1369 const std::string
&graphId
= graph_it
.first
;
1370 const auto &graph
= graph_it
.second
.graph
;
1371 int node2
= adj_it
.first
;
1376 NodeSet
pair(graphId
, node1
, node2
);
1378 if (usedPairs
.count(pair
) > 0)
1381 int matches
= testForMining(results
, usedPairs
, nodePairs
, pair
, graphId
, graph
, minNodes
, minMatches
, limitMatchesPerGraph
);
1384 my_printf("Pair %s[%s,%s] -> %d%s\n", graphId
.c_str(), graph
.nodes
[node1
].nodeId
.c_str(),
1385 graph
.nodes
[node2
].nodeId
.c_str(), matches
, matches
< minMatches
? " *purge*" : "");
1387 if (minMatches
<= matches
)
1392 my_printf("Found a total of %d subgraphs in %d groups.\n", int(nodePairs
.size()), groupCounter
);
1395 void findNextPool(std::vector
<Solver::MineResult
> &results
, std::set
<NodeSet
> &pool
,
1396 int oldSetSize
, int increment
, int minNodes
, int minMatches
, int limitMatchesPerGraph
)
1398 int groupCounter
= 0;
1399 std::map
<std::string
, std::vector
<const NodeSet
*>> poolPerGraph
;
1400 std::set
<NodeSet
> nextPool
;
1402 for (auto &it
: pool
)
1403 poolPerGraph
[it
.graphId
].push_back(&it
);
1406 my_printf("\nMining for frequent subcircuits of size %d using increment %d:\n", oldSetSize
+increment
, increment
);
1409 for (auto &it
: poolPerGraph
)
1411 std::map
<int, std::set
<int>> node2sets
;
1412 std::set
<NodeSet
> usedSets
;
1414 for (int idx
= 0; idx
< int(it
.second
.size()); idx
++) {
1415 for (int node
: it
.second
[idx
]->nodes
)
1416 node2sets
[node
].insert(idx
);
1419 for (int idx1
= 0; idx1
< int(it
.second
.size()); idx1
++, count
++)
1421 std::set
<int> idx2set
;
1423 for (int node
: it
.second
[idx1
]->nodes
)
1424 for (int idx2
: node2sets
[node
])
1426 idx2set
.insert(idx2
);
1428 for (int idx2
: idx2set
)
1430 if (it
.second
[idx1
]->extendCandidate(*it
.second
[idx2
]) != increment
)
1433 NodeSet mergedSet
= *it
.second
[idx1
];
1434 mergedSet
.extend(*it
.second
[idx2
]);
1436 if (usedSets
.count(mergedSet
) > 0)
1439 const std::string
&graphId
= it
.first
;
1440 const auto &graph
= graphData
[it
.first
].graph
;
1443 my_printf("<%d%%/%d> Found %s[", int(100*count
/pool
.size()), oldSetSize
+increment
, graphId
.c_str());
1445 for (int nodeIdx
: mergedSet
.nodes
) {
1446 my_printf("%s%s", first
? "" : ",", graph
.nodes
[nodeIdx
].nodeId
.c_str());
1452 int matches
= testForMining(results
, usedSets
, nextPool
, mergedSet
, graphId
, graph
, minNodes
, minMatches
, limitMatchesPerGraph
);
1455 my_printf(" %d%s\n", matches
, matches
< minMatches
? " *purge*" : "");
1457 if (minMatches
<= matches
)
1463 pool
.swap(nextPool
);
1466 my_printf("Found a total of %d subgraphs in %d groups.\n", int(pool
.size()), groupCounter
);
1469 // interface to the public solver class
1472 SolverWorker(Solver
*userSolver
) : userSolver(userSolver
), verbose(false)
1481 void addGraph(std::string graphId
, const Graph
&graph
)
1483 assert(graphData
.count(graphId
) == 0);
1485 GraphData
&gd
= graphData
[graphId
];
1486 gd
.graphId
= graphId
;
1488 diCache
.add(gd
.graph
, gd
.adjMatrix
, graphId
, userSolver
);
1491 void addCompatibleTypes(std::string needleTypeId
, std::string haystackTypeId
)
1493 compatibleTypes
[needleTypeId
].insert(haystackTypeId
);
1496 void addCompatibleConstants(int needleConstant
, int haystackConstant
)
1498 compatibleConstants
[needleConstant
].insert(haystackConstant
);
1501 void addSwappablePorts(std::string needleTypeId
, const std::set
<std::string
> &ports
)
1503 swapPorts
[needleTypeId
].insert(ports
);
1504 diCache
.compareCache
.clear();
1507 void addSwappablePortsPermutation(std::string needleTypeId
, const std::map
<std::string
, std::string
> &portMapping
)
1509 swapPermutations
[needleTypeId
].insert(portMapping
);
1510 diCache
.compareCache
.clear();
1513 void solve(std::vector
<Solver::Result
> &results
, std::string needleGraphId
, std::string haystackGraphId
,
1514 const std::map
<std::string
, std::set
<std::string
>> &initialMappings
, bool allowOverlap
, int maxSolutions
)
1516 assert(graphData
.count(needleGraphId
) > 0);
1517 assert(graphData
.count(haystackGraphId
) > 0);
1519 const GraphData
&needle
= graphData
[needleGraphId
];
1520 GraphData
&haystack
= graphData
[haystackGraphId
];
1522 std::vector
<std::set
<int>> enumerationMatrix
;
1523 generateEnumerationMatrix(enumerationMatrix
, needle
, haystack
, initialMappings
);
1528 my_printf("Needle nodes:\n");
1529 for (int i
= 0; i
< int(needle
.graph
.nodes
.size()); i
++)
1530 my_printf("%5d: %s (%s)\n", i
, needle
.graph
.nodes
[i
].nodeId
.c_str(), needle
.graph
.nodes
[i
].typeId
.c_str());
1533 my_printf("Haystack nodes:\n");
1534 for (int i
= 0; i
< int(haystack
.graph
.nodes
.size()); i
++)
1535 my_printf("%5d: %s (%s)\n", i
, haystack
.graph
.nodes
[i
].nodeId
.c_str(), haystack
.graph
.nodes
[i
].typeId
.c_str());
1538 my_printf("Needle Adjecency Matrix:\n");
1539 printAdjMatrix(needle
.adjMatrix
);
1542 my_printf("Haystack Adjecency Matrix:\n");
1543 printAdjMatrix(haystack
.adjMatrix
);
1546 my_printf("Edge Types:\n");
1547 diCache
.printEdgeTypes();
1550 my_printf("Enumeration Matrix (haystack nodes at column indices):\n");
1551 printEnumerationMatrix(enumerationMatrix
, haystack
.graph
.nodes
.size());
1554 haystack
.usedNodes
.resize(haystack
.graph
.nodes
.size());
1555 ullmannRecursion(results
, enumerationMatrix
, 0, needle
, haystack
, allowOverlap
, maxSolutions
> 0 ? results
.size() + maxSolutions
: -1);
1558 void mine(std::vector
<Solver::MineResult
> &results
, int minNodes
, int maxNodes
, int minMatches
, int limitMatchesPerGraph
)
1560 int nodeSetSize
= 2;
1561 std::set
<NodeSet
> pool
;
1562 findNodePairs(results
, pool
, minNodes
, minMatches
, limitMatchesPerGraph
);
1564 while ((maxNodes
< 0 || nodeSetSize
< maxNodes
) && pool
.size() > 0)
1566 int increment
= nodeSetSize
- 1;
1567 if (nodeSetSize
+ increment
>= minNodes
)
1568 increment
= minNodes
- nodeSetSize
;
1569 if (nodeSetSize
>= minNodes
)
1572 findNextPool(results
, pool
, nodeSetSize
, increment
, minNodes
, minMatches
, limitMatchesPerGraph
);
1573 nodeSetSize
+= increment
;
1577 void clearOverlapHistory()
1579 for (auto &it
: graphData
)
1580 it
.second
.usedNodes
.clear();
1585 compatibleTypes
.clear();
1586 compatibleConstants
.clear();
1588 swapPermutations
.clear();
1589 diCache
.compareCache
.clear();
1592 friend class Solver
;
1595 bool Solver::userCompareNodes(const std::string
&, const std::string
&, void*, const std::string
&, const std::string
&, void*, const std::map
<std::string
, std::string
>&)
1600 std::string
Solver::userAnnotateEdge(const std::string
&, const std::string
&, void*, const std::string
&, void*)
1602 return std::string();
1605 bool Solver::userCompareEdge(const std::string
&, const std::string
&, void*, const std::string
&, void*, const std::string
&, const std::string
&, void*, const std::string
&, void*)
1610 bool Solver::userCheckSolution(const Result
&)
1615 SubCircuit::Solver::Solver()
1617 worker
= new SolverWorker(this);
1620 SubCircuit::Solver::~Solver()
1625 void SubCircuit::Solver::setVerbose()
1627 worker
->setVerbose();
1630 void SubCircuit::Solver::addGraph(std::string graphId
, const Graph
&graph
)
1632 worker
->addGraph(graphId
, graph
);
1635 void SubCircuit::Solver::addCompatibleTypes(std::string needleTypeId
, std::string haystackTypeId
)
1637 worker
->addCompatibleTypes(needleTypeId
, haystackTypeId
);
1640 void SubCircuit::Solver::addCompatibleConstants(int needleConstant
, int haystackConstant
)
1642 worker
->addCompatibleConstants(needleConstant
, haystackConstant
);
1645 void SubCircuit::Solver::addSwappablePorts(std::string needleTypeId
, std::string portId1
, std::string portId2
, std::string portId3
, std::string portId4
)
1647 std::set
<std::string
> ports
;
1648 ports
.insert(portId1
);
1649 ports
.insert(portId2
);
1650 ports
.insert(portId3
);
1651 ports
.insert(portId4
);
1652 ports
.erase(std::string());
1653 addSwappablePorts(needleTypeId
, ports
);
1656 void SubCircuit::Solver::addSwappablePorts(std::string needleTypeId
, std::set
<std::string
> ports
)
1658 worker
->addSwappablePorts(needleTypeId
, ports
);
1661 void SubCircuit::Solver::addSwappablePortsPermutation(std::string needleTypeId
, std::map
<std::string
, std::string
> portMapping
)
1663 worker
->addSwappablePortsPermutation(needleTypeId
, portMapping
);
1666 void SubCircuit::Solver::solve(std::vector
<Result
> &results
, std::string needleGraphId
, std::string haystackGraphId
, bool allowOverlap
, int maxSolutions
)
1668 std::map
<std::string
, std::set
<std::string
>> emptyInitialMapping
;
1669 worker
->solve(results
, needleGraphId
, haystackGraphId
, emptyInitialMapping
, allowOverlap
, maxSolutions
);
1672 void SubCircuit::Solver::solve(std::vector
<Result
> &results
, std::string needleGraphId
, std::string haystackGraphId
,
1673 const std::map
<std::string
, std::set
<std::string
>> &initialMappings
, bool allowOverlap
, int maxSolutions
)
1675 worker
->solve(results
, needleGraphId
, haystackGraphId
, initialMappings
, allowOverlap
, maxSolutions
);
1678 void SubCircuit::Solver::mine(std::vector
<MineResult
> &results
, int minNodes
, int maxNodes
, int minMatches
, int limitMatchesPerGraph
)
1680 worker
->mine(results
, minNodes
, maxNodes
, minMatches
, limitMatchesPerGraph
);
1683 void SubCircuit::Solver::clearOverlapHistory()
1685 worker
->clearOverlapHistory();
1688 void SubCircuit::Solver::clearConfig()
1690 worker
->clearConfig();