Bump version
[yosys.git] / libs / subcircuit / subcircuit.cc
1 /*
2 * SubCircuit -- An implementation of the Ullmann Subgraph Isomorphism
3 * algorithm for coarse grain logic networks
4 *
5 * Copyright (C) 2013 Claire Xenia Wolf <claire@yosyshq.com>
6 *
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.
10 *
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.
18 *
19 */
20
21 #include "subcircuit.h"
22
23 #include <algorithm>
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27
28 #ifdef _YOSYS_
29 # include "kernel/yosys.h"
30 # define my_printf YOSYS_NAMESPACE_PREFIX log
31 #else
32 # define my_printf printf
33 #endif
34
35 using namespace SubCircuit;
36
37 #ifndef _YOSYS_
38 static std::string my_stringf(const char *fmt, ...)
39 {
40 std::string string;
41 char *str = NULL;
42 va_list ap;
43
44 va_start(ap, fmt);
45 if (vasprintf(&str, fmt, ap) < 0)
46 str = NULL;
47 va_end(ap);
48
49 if (str != NULL) {
50 string = str;
51 free(str);
52 }
53
54 return string;
55 }
56 #else
57 # define my_stringf YOSYS_NAMESPACE_PREFIX stringf
58 #endif
59
60 SubCircuit::Graph::Graph(const Graph &other, const std::vector<std::string> &otherNodes)
61 {
62 allExtern = other.allExtern;
63
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;
69 }
70
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;
78 }
79
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;
87 }
88
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);
95 }
96 }
97
98 bool SubCircuit::Graph::BitRef::operator < (const BitRef &other) const
99 {
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;
105 }
106
107 void SubCircuit::Graph::createNode(std::string nodeId, std::string typeId, void *userData, bool shared)
108 {
109 assert(nodeMap.count(nodeId) == 0);
110 nodeMap[nodeId] = nodes.size();
111 nodes.push_back(Node());
112
113 Node &newNode = nodes.back();
114 newNode.nodeId = nodeId;
115 newNode.typeId = typeId;
116 newNode.userData = userData;
117 newNode.shared = shared;
118 }
119
120 void SubCircuit::Graph::createPort(std::string nodeId, std::string portId, int width, int minWidth)
121 {
122 assert(nodeMap.count(nodeId) != 0);
123 int nodeIdx = nodeMap[nodeId];
124 Node &node = nodes[nodeIdx];
125
126 assert(node.portMap.count(portId) == 0);
127
128 int portIdx = node.ports.size();
129 node.portMap[portId] = portIdx;
130 node.ports.push_back(Port());
131 Port &port = node.ports.back();
132
133 port.portId = portId;
134 port.minWidth = minWidth < 0 ? width : minWidth;
135 port.bits.insert(port.bits.end(), width, PortBit());
136
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));
141 }
142 }
143
144 void SubCircuit::Graph::createConnection(std::string fromNodeId, std::string fromPortId, int fromBit, std::string toNodeId, std::string toPortId, int toBit, int width)
145 {
146 assert(nodeMap.count(fromNodeId) != 0);
147 assert(nodeMap.count(toNodeId) != 0);
148
149 int fromNodeIdx = nodeMap[fromNodeId];
150 Node &fromNode = nodes[fromNodeIdx];
151
152 int toNodeIdx = nodeMap[toNodeId];
153 Node &toNode = nodes[toNodeIdx];
154
155 assert(fromNode.portMap.count(fromPortId) != 0);
156 assert(toNode.portMap.count(toPortId) != 0);
157
158 int fromPortIdx = fromNode.portMap[fromPortId];
159 Port &fromPort = fromNode.ports[fromPortIdx];
160
161 int toPortIdx = toNode.portMap[toPortId];
162 Port &toPort = toNode.ports[toPortIdx];
163
164 if (width < 0) {
165 assert(fromBit == 0 && toBit == 0);
166 assert(fromPort.bits.size() == toPort.bits.size());
167 width = fromPort.bits.size();
168 }
169
170 assert(fromBit >= 0 && toBit >= 0);
171 for (int i = 0; i < width; i++)
172 {
173 assert(fromBit + i < int(fromPort.bits.size()));
174 assert(toBit + i < int(toPort.bits.size()));
175
176 int fromEdgeIdx = fromPort.bits[fromBit + i].edgeIdx;
177 int toEdgeIdx = toPort.bits[toBit + i].edgeIdx;
178
179 if (fromEdgeIdx == toEdgeIdx)
180 continue;
181
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;
188 }
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;
192 }
193
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;
199 }
200 edges.pop_back();
201 }
202 }
203
204 void SubCircuit::Graph::createConnection(std::string fromNodeId, std::string fromPortId, std::string toNodeId, std::string toPortId)
205 {
206 createConnection(fromNodeId, fromPortId, 0, toNodeId, toPortId, 0, -1);
207 }
208
209 void SubCircuit::Graph::createConstant(std::string toNodeId, std::string toPortId, int toBit, int constValue)
210 {
211 assert(nodeMap.count(toNodeId) != 0);
212 int toNodeIdx = nodeMap[toNodeId];
213 Node &toNode = nodes[toNodeIdx];
214
215 assert(toNode.portMap.count(toPortId) != 0);
216 int toPortIdx = toNode.portMap[toPortId];
217 Port &toPort = toNode.ports[toPortIdx];
218
219 assert(toBit >= 0 && toBit < int(toPort.bits.size()));
220 int toEdgeIdx = toPort.bits[toBit].edgeIdx;
221
222 assert(edges[toEdgeIdx].constValue == 0);
223 edges[toEdgeIdx].constValue = constValue;
224 }
225
226 void SubCircuit::Graph::createConstant(std::string toNodeId, std::string toPortId, int constValue)
227 {
228 assert(nodeMap.count(toNodeId) != 0);
229 int toNodeIdx = nodeMap[toNodeId];
230 Node &toNode = nodes[toNodeIdx];
231
232 assert(toNode.portMap.count(toPortId) != 0);
233 int toPortIdx = toNode.portMap[toPortId];
234 Port &toPort = toNode.ports[toPortIdx];
235
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;
241 }
242 }
243
244 void SubCircuit::Graph::markExtern(std::string nodeId, std::string portId, int bit)
245 {
246 assert(nodeMap.count(nodeId) != 0);
247 Node &node = nodes[nodeMap[nodeId]];
248
249 assert(node.portMap.count(portId) != 0);
250 Port &port = node.ports[node.portMap[portId]];
251
252 if (bit < 0) {
253 for (const auto portBit : port.bits)
254 edges[portBit.edgeIdx].isExtern = true;
255 } else {
256 assert(bit < int(port.bits.size()));
257 edges[port.bits[bit].edgeIdx].isExtern = true;
258 }
259 }
260
261 void SubCircuit::Graph::markAllExtern()
262 {
263 allExtern = true;
264 }
265
266 void SubCircuit::Graph::print()
267 {
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]");
281 my_printf("\n");
282 }
283 }
284 }
285 }
286
287 class SubCircuit::SolverWorker
288 {
289 // basic internal data structures
290
291 typedef std::vector<std::map<int, int>> adjMatrix_t;
292
293 struct GraphData {
294 std::string graphId;
295 Graph graph;
296 adjMatrix_t adjMatrix;
297 std::vector<bool> usedNodes;
298 };
299
300 static void printAdjMatrix(const adjMatrix_t &matrix)
301 {
302 my_printf("%7s", "");
303 for (int i = 0; i < int(matrix.size()); i++)
304 my_printf("%4d:", i);
305 my_printf("\n");
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", "-");
311 else
312 my_printf("%5d", matrix.at(i).at(j));
313 my_printf("\n");
314 }
315 }
316
317 // helper functions for handling permutations
318
319 static constexpr int maxPermutationsLimit = 1000000;
320
321 static int numberOfPermutations(const std::vector<std::string> &list)
322 {
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()];
327 }
328
329 static void permutateVectorToMap(std::map<std::string, std::string> &map, const std::vector<std::string> &list, int idx)
330 {
331 // convert idx to a list.size() digits factoradic number
332
333 std::vector<int> factoradicDigits;
334 for (int i = 0; i < int(list.size()); i++) {
335 factoradicDigits.push_back(idx % (i+1));
336 idx = idx / (i+1);
337 }
338
339 // construct permutation
340
341 std::vector<std::string> pool = list;
342 std::vector<std::string> permutation;
343
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);
349 }
350
351 // update map
352
353 for (int i = 0; i < int(list.size()); i++)
354 map[list[i]] = permutation[i];
355 }
356
357 static int numberOfPermutationsArray(const std::vector<std::vector<std::string>> &list)
358 {
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;
364 }
365 return numPermutations;
366 }
367
368 static void permutateVectorToMapArray(std::map<std::string, std::string> &map, const std::vector<std::vector<std::string>> &list, int idx)
369 {
370 for (const auto &it : list) {
371 int thisPermutations = numberOfPermutations(it);
372 int thisIdx = idx % thisPermutations;
373 permutateVectorToMap(map, it, thisIdx);
374 idx /= thisPermutations;
375 }
376 }
377
378 static void applyPermutation(std::map<std::string, std::string> &map, const std::map<std::string, std::string> &permutation)
379 {
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)));
384 else
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;
388 }
389
390 // classes for internal digraph representation
391
392 struct DiBit
393 {
394 std::string fromPort, toPort;
395 int fromBit, toBit;
396
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) { }
399
400 bool operator < (const DiBit &other) const
401 {
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;
409 }
410
411 std::string toString() const
412 {
413 return my_stringf("%s[%d]:%s[%d]", fromPort.c_str(), fromBit, toPort.c_str(), toBit);
414 }
415 };
416
417 struct DiNode
418 {
419 std::string typeId;
420 std::map<std::string, int> portSizes;
421
422 DiNode()
423 {
424 }
425
426 DiNode(const Graph &graph, int nodeIdx)
427 {
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();
432 }
433
434 bool operator < (const DiNode &other) const
435 {
436 if (typeId != other.typeId)
437 return typeId < other.typeId;
438 return portSizes < other.portSizes;
439 }
440
441 std::string toString() const
442 {
443 std::string str;
444 bool firstPort = true;
445 for (const auto &it : portSizes) {
446 str += my_stringf("%s%s[%d]", firstPort ? "" : ",", it.first.c_str(), it.second);
447 firstPort = false;
448 }
449 return typeId + "(" + str + ")";
450 }
451 };
452
453 struct DiEdge
454 {
455 DiNode fromNode, toNode;
456 std::set<DiBit> bits;
457 std::string userAnnotation;
458
459 bool operator < (const DiEdge &other) const
460 {
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;
468 }
469
470 bool compare(const DiEdge &other, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::string> &mapToPorts) const
471 {
472 // Rules for matching edges:
473 //
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
477 //
478 // There is no need to check in the other direction, as checking
479 // of the isExtern properties is already performed in node matching.
480 //
481 // Note: "this" is needle, "other" is haystack
482
483 for (auto bit : bits)
484 {
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);
489
490 if (other.fromNode.portSizes.count(bit.fromPort) == 0)
491 continue;
492 if (other.toNode.portSizes.count(bit.toPort) == 0)
493 continue;
494
495 if (bit.fromBit >= other.fromNode.portSizes.at(bit.fromPort))
496 continue;
497 if (bit.toBit >= other.toNode.portSizes.at(bit.toPort))
498 continue;
499
500 if (other.bits.count(bit) == 0)
501 return false;
502 }
503
504 return true;
505 }
506
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
509 {
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))
515 return true;
516 }
517 return compareWithToPermutations(other, mapFromPorts, mapToPorts, swapPermutations);
518 }
519
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
522 {
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))
528 return true;
529 }
530 return compare(other, mapFromPorts, mapToPorts);
531 }
532
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
535 {
536 // brute force method for port swapping: try all variations
537
538 std::vector<std::vector<std::string>> swapFromPorts;
539 std::vector<std::vector<std::string>> swapToPorts;
540
541 // only use groups that are relevant for this edge
542
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;
548 if (0) {
549 foundFromPortMatch:
550 std::vector<std::string> portsVector;
551 for (const auto &port : ports)
552 portsVector.push_back(port);
553 swapFromPorts.push_back(portsVector);
554 }
555 }
556
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;
562 if (0) {
563 foundToPortMatch:
564 std::vector<std::string> portsVector;
565 for (const auto &port : ports)
566 portsVector.push_back(port);
567 swapToPorts.push_back(portsVector);
568 }
569 }
570
571 // try all permutations
572
573 std::map<std::string, std::string> mapFromPorts, mapToPorts;
574 int fromPortsPermutations = numberOfPermutationsArray(swapFromPorts);
575 int toPortsPermutations = numberOfPermutationsArray(swapToPorts);
576
577 for (int i = 0; i < fromPortsPermutations; i++)
578 {
579 permutateVectorToMapArray(mapFromPorts, swapFromPorts, i);
580
581 for (int j = 0; j < toPortsPermutations; j++) {
582 permutateVectorToMapArray(mapToPorts, swapToPorts, j);
583 if (compareWithFromAndToPermutations(other, mapFromPorts, mapToPorts, swapPermutations))
584 return true;
585 }
586 }
587
588 return false;
589 }
590
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
593 {
594 // strip-down version of the last function: only try permutations for mapToPorts, mapFromPorts is already provided by the caller
595
596 std::vector<std::vector<std::string>> swapToPorts;
597
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;
603 if (0) {
604 foundToPortMatch:
605 std::vector<std::string> portsVector;
606 for (const auto &port : ports)
607 portsVector.push_back(port);
608 swapToPorts.push_back(portsVector);
609 }
610 }
611
612 std::map<std::string, std::string> mapToPorts;
613 int toPortsPermutations = numberOfPermutationsArray(swapToPorts);
614
615 for (int j = 0; j < toPortsPermutations; j++) {
616 permutateVectorToMapArray(mapToPorts, swapToPorts, j);
617 if (compareWithToPermutations(other, mapFromPorts, mapToPorts, swapPermutations))
618 return true;
619 }
620
621 return false;
622 }
623
624 std::string toString() const
625 {
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;
631 return buffer;
632 }
633
634 static void findEdgesInGraph(const Graph &graph, std::map<std::pair<int, int>, DiEdge> &edges)
635 {
636 edges.clear();
637 for (const auto &edge : graph.edges) {
638 if (edge.constValue != 0)
639 continue;
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));
649 }
650 }
651 }
652 };
653
654 struct DiCache
655 {
656 std::map<DiEdge, int> edgeTypesMap;
657 std::vector<DiEdge> edgeTypes;
658 std::map<std::pair<int, int>, bool> compareCache;
659
660 void add(const Graph &graph, adjMatrix_t &adjMatrix, const std::string &graphId, Solver *userSolver)
661 {
662 std::map<std::pair<int, int>, DiEdge> edges;
663 DiEdge::findEdgesInGraph(graph, edges);
664
665 adjMatrix.clear();
666 adjMatrix.resize(graph.nodes.size());
667
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);
672 }
673
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);
678 }
679 adjMatrix[it.first.first][it.first.second] = edgeTypesMap[it.second];
680 }
681 }
682
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)
685 {
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];
690 }
691
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
694 {
695 return edgeTypes.at(needleEdge).compare(edgeTypes.at(haystackEdge), mapFromPorts, swapPorts, swapPermutations);
696 }
697
698 bool compare(int needleEdge, int haystackEdge, const std::map<std::string, std::string> &mapFromPorts, const std::map<std::string, std::string> &mapToPorts) const
699 {
700 return edgeTypes.at(needleEdge).compare(edgeTypes.at(haystackEdge), mapFromPorts, mapToPorts);
701 }
702
703 void printEdgeTypes() const
704 {
705 for (int i = 0; i < int(edgeTypes.size()); i++)
706 my_printf("%5d: %s\n", i, edgeTypes[i].toString().c_str());
707 }
708 };
709
710 // solver state variables
711
712 Solver *userSolver;
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;
718 DiCache diCache;
719 bool verbose;
720
721 // main solver functions
722
723 bool matchNodePorts(const Graph &needle, int needleNodeIdx, const Graph &haystack, int haystackNodeIdx, const std::map<std::string, std::string> &swaps) const
724 {
725 const Graph::Node &nn = needle.nodes[needleNodeIdx];
726 const Graph::Node &hn = haystack.nodes[haystackNodeIdx];
727 assert(nn.ports.size() == hn.ports.size());
728
729 for (int i = 0; i < int(nn.ports.size()); i++)
730 {
731 std::string hnPortId = nn.ports[i].portId;
732 if (swaps.count(hnPortId) > 0)
733 hnPortId = swaps.at(hnPortId);
734
735 if (hn.portMap.count(hnPortId) == 0)
736 return false;
737
738 const Graph::Port &np = nn.ports[i];
739 const Graph::Port &hp = hn.ports[hn.portMap.at(hnPortId)];
740
741 if (int(hp.bits.size()) < np.minWidth || hp.bits.size() > np.bits.size())
742 return false;
743
744 for (int j = 0; j < int(hp.bits.size()); j++)
745 {
746 const Graph::Edge &ne = needle.edges[np.bits[j].edgeIdx];
747 const Graph::Edge &he = haystack.edges[hp.bits[j].edgeIdx];
748
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)
752 return false;
753 continue;
754 }
755
756 if (ne.isExtern || needle.allExtern) {
757 if (he.portBits.size() < ne.portBits.size())
758 return false;
759 } else {
760 if (he.portBits.size() != ne.portBits.size())
761 return false;
762 if (he.isExtern || haystack.allExtern)
763 return false;
764 }
765 }
766 }
767
768 return true;
769 }
770
771 bool matchNodes(const GraphData &needle, int needleNodeIdx, const GraphData &haystack, int haystackNodeIdx) const
772 {
773 // Rules for matching nodes:
774 //
775 // 1. their typeId must be identical or compatible
776 // (this is checked before calling this function)
777 //
778 // 2. they must have the same ports and the haystack port
779 // widths must match the needle port width range
780 //
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
787
788 const Graph::Node &nn = needle.graph.nodes[needleNodeIdx];
789 const Graph::Node &hn = haystack.graph.nodes[haystackNodeIdx];
790
791 assert(nn.typeId == hn.typeId || (compatibleTypes.count(nn.typeId) > 0 && compatibleTypes.at(nn.typeId).count(hn.typeId) > 0));
792
793 if (nn.ports.size() != hn.ports.size())
794 return false;
795
796 std::map<std::string, std::string> currentCandidate;
797
798 for (const auto &port : needle.graph.nodes[needleNodeIdx].ports)
799 currentCandidate[port.portId] = port.portId;
800
801 if (swapPorts.count(needle.graph.nodes[needleNodeIdx].typeId) == 0)
802 {
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))
805 return true;
806
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))
813 return true;
814 }
815 }
816 else
817 {
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);
824 }
825
826 int thisPermutations = numberOfPermutationsArray(thisSwapPorts);
827 for (int i = 0; i < thisPermutations; i++)
828 {
829 permutateVectorToMapArray(currentCandidate, thisSwapPorts, i);
830
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))
833 return true;
834
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))
841 return true;
842 }
843 }
844 }
845
846 return false;
847 }
848
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
850 {
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);
854
855 enumerationMatrix.clear();
856 enumerationMatrix.resize(needle.graph.nodes.size());
857 for (int i = 0; i < int(needle.graph.nodes.size()); i++)
858 {
859 const Graph::Node &nn = needle.graph.nodes[i];
860
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)
864 continue;
865 if (!matchNodes(needle, i, haystack, j))
866 continue;
867 enumerationMatrix[i].insert(j);
868 }
869
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)
875 continue;
876 if (!matchNodes(needle, i, haystack, j))
877 continue;
878 enumerationMatrix[i].insert(j);
879 }
880 }
881 }
882
883 bool checkEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, int i, int j, const GraphData &needle, const GraphData &haystack)
884 {
885 for (const auto &it_needle : needle.adjMatrix.at(i))
886 {
887 int needleNeighbour = it_needle.first;
888 int needleEdgeType = it_needle.second;
889
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))
900 goto found_match;
901 }
902 }
903
904 return false;
905 found_match:;
906 }
907
908 return true;
909 }
910
911 bool pruneEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, const GraphData &needle, const GraphData &haystack, int &nextRow, bool allowOverlap)
912 {
913 bool didSomething = true;
914 while (didSomething)
915 {
916 nextRow = -1;
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))
922 didSomething = true;
923 else if (!allowOverlap && haystack.usedNodes[j])
924 didSomething = true;
925 else
926 newRow.insert(j);
927 }
928 if (newRow.size() == 0)
929 return false;
930 if (newRow.size() >= 2 && (nextRow < 0 || needle.adjMatrix.at(nextRow).size() < needle.adjMatrix.at(i).size()))
931 nextRow = i;
932 enumerationMatrix[i].swap(newRow);
933 }
934 }
935 return true;
936 }
937
938 void printEnumerationMatrix(const std::vector<std::set<int>> &enumerationMatrix, int maxHaystackNodeIdx = -1) const
939 {
940 if (maxHaystackNodeIdx < 0) {
941 for (const auto &it : enumerationMatrix)
942 for (int idx : it)
943 maxHaystackNodeIdx = std::max(maxHaystackNodeIdx, idx);
944 }
945
946 my_printf(" ");
947 for (int j = 0; j < maxHaystackNodeIdx; j += 5)
948 my_printf("%-6d", j);
949 my_printf("\n");
950
951 for (int i = 0; i < int(enumerationMatrix.size()); i++)
952 {
953 my_printf("%5d:", i);
954 for (int j = 0; j < maxHaystackNodeIdx; j++) {
955 if (j % 5 == 0)
956 my_printf(" ");
957 my_printf("%c", enumerationMatrix[i].count(j) > 0 ? '*' : '.');
958 }
959 my_printf("\n");
960 }
961 }
962
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> &currentCandidate)
964 {
965 assert(enumerationMatrix[idx].size() == 1);
966 int idxHaystack = *enumerationMatrix[idx].begin();
967
968 const Graph::Node &nn = needle.graph.nodes[idx];
969 const Graph::Node &hn = haystack.graph.nodes[idxHaystack];
970
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))
973 return false;
974
975 for (const auto &it_needle : needle.adjMatrix.at(idx))
976 {
977 int needleNeighbour = it_needle.first;
978 int needleEdgeType = it_needle.second;
979
980 assert(enumerationMatrix[needleNeighbour].size() == 1);
981 int haystackNeighbour = *enumerationMatrix[needleNeighbour].begin();
982
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))
986 return false;
987 }
988
989 return true;
990 }
991
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)
994 {
995 std::map<std::string, std::string> currentCandidate;
996
997 for (const auto &port : needle.graph.nodes[idx].ports)
998 currentCandidate[port.portId] = port.portId;
999
1000 if (swapPorts.count(needle.graph.nodes[idx].typeId) == 0)
1001 {
1002 if (checkPortmapCandidate(enumerationMatrix, needle, haystack, idx, currentCandidate))
1003 portmapCandidates.insert(currentCandidate);
1004
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);
1011 }
1012 }
1013 else
1014 {
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);
1021 }
1022
1023 int thisPermutations = numberOfPermutationsArray(thisSwapPorts);
1024 for (int i = 0; i < thisPermutations; i++)
1025 {
1026 permutateVectorToMapArray(currentCandidate, thisSwapPorts, i);
1027
1028 if (checkPortmapCandidate(enumerationMatrix, needle, haystack, idx, currentCandidate))
1029 portmapCandidates.insert(currentCandidate);
1030
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);
1037 }
1038 }
1039 }
1040 }
1041
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)
1043 {
1044 bool didSomething = false;
1045
1046 // strategy #1: prune impossible port mappings
1047
1048 for (int i = 0; i < int(needle.graph.nodes.size()); i++)
1049 {
1050 assert(enumerationMatrix[i].size() == 1);
1051 int j = *enumerationMatrix[i].begin();
1052
1053 std::set<std::map<std::string, std::string>> thisCandidates;
1054 portmapCandidates[i].swap(thisCandidates);
1055
1056 for (const auto &testCandidate : thisCandidates)
1057 {
1058 for (const auto &it_needle : needle.adjMatrix.at(i))
1059 {
1060 int needleNeighbour = it_needle.first;
1061 int needleEdgeType = it_needle.second;
1062
1063 assert(enumerationMatrix[needleNeighbour].size() == 1);
1064 int haystackNeighbour = *enumerationMatrix[needleNeighbour].begin();
1065
1066 assert(haystack.adjMatrix.at(j).count(haystackNeighbour) > 0);
1067 int haystackEdgeType = haystack.adjMatrix.at(j).at(haystackNeighbour);
1068
1069 std::set<std::map<std::string, std::string>> &candidates =
1070 i == needleNeighbour ? thisCandidates : portmapCandidates[needleNeighbour];
1071
1072 for (const auto &otherCandidate : candidates) {
1073 if (diCache.compare(needleEdgeType, haystackEdgeType, testCandidate, otherCandidate))
1074 goto found_match;
1075 }
1076
1077 didSomething = true;
1078 goto purgeCandidate;
1079 found_match:;
1080 }
1081
1082 portmapCandidates[i].insert(testCandidate);
1083 purgeCandidate:;
1084 }
1085
1086 if (portmapCandidates[i].size() == 0)
1087 return false;
1088 }
1089
1090 if (didSomething)
1091 return true;
1092
1093 // strategy #2: prune a single random port mapping
1094
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());
1099 return true;
1100 }
1101
1102 return false;
1103 }
1104
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)
1106 {
1107 int i = -1;
1108 if (!pruneEnumerationMatrix(enumerationMatrix, needle, haystack, i, allowOverlap))
1109 return;
1110
1111 if (i < 0)
1112 {
1113 Solver::Result result;
1114 result.needleGraphId = needle.graphId;
1115 result.haystackGraphId = haystack.graphId;
1116
1117 std::vector<std::set<std::map<std::string, std::string>>> portmapCandidates;
1118 portmapCandidates.resize(enumerationMatrix.size());
1119
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;
1128 }
1129
1130 while (prunePortmapCandidates(portmapCandidates, enumerationMatrix, needle, haystack)) { }
1131
1132 if (verbose) {
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++);
1139 int mapCounter = 0;
1140 for (auto &i3 : i2)
1141 my_printf("%s %s -> %s", mapCounter++ ? "," : "", i3.first.c_str(), i3.second.c_str());
1142 my_printf("\n");
1143 }
1144 }
1145 }
1146
1147 for (int j = 0; j < int(enumerationMatrix.size()); j++) {
1148 if (portmapCandidates[j].size() == 0) {
1149 if (verbose) {
1150 my_printf("\nSolution (rejected by portmapper):\n");
1151 printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
1152 }
1153 return;
1154 }
1155 result.mappings[needle.graph.nodes[j].nodeId].portMapping = *portmapCandidates[j].begin();
1156 }
1157
1158 if (!userSolver->userCheckSolution(result)) {
1159 if (verbose) {
1160 my_printf("\nSolution (rejected by userCheckSolution):\n");
1161 printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
1162 }
1163 return;
1164 }
1165
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;
1169
1170 if (verbose) {
1171 my_printf("\nSolution:\n");
1172 printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
1173 }
1174
1175 results.push_back(result);
1176 return;
1177 }
1178
1179 if (verbose) {
1180 my_printf("\n");
1181 my_printf("Enumeration Matrix at recursion level %d (%d):\n", iter, i);
1182 printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
1183 }
1184
1185 std::set<int> activeRow;
1186 enumerationMatrix[i].swap(activeRow);
1187
1188 for (int j : activeRow)
1189 {
1190 // found enough?
1191 if (limitResults >= 0 && int(results.size()) >= limitResults)
1192 return;
1193
1194 // already used by other solution -> try next
1195 if (!allowOverlap && haystack.usedNodes[j])
1196 continue;
1197
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);
1203
1204 // recursion
1205 ullmannRecursion(results, nextEnumerationMatrix, iter+1, needle, haystack, allowOverlap, limitResults);
1206
1207 // we just have found something -> unroll to top recursion level
1208 if (!allowOverlap && haystack.usedNodes[j] && iter > 0)
1209 return;
1210 }
1211 }
1212
1213 // additional data structes and functions for mining
1214
1215 struct NodeSet {
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);
1222 }
1223 NodeSet(std::string graphId, const std::vector<int> &nodes) {
1224 this->graphId = graphId;
1225 for (int node : nodes)
1226 this->nodes.insert(node);
1227 }
1228 void extend(const NodeSet &other) {
1229 assert(this->graphId == other.graphId);
1230 for (int node : other.nodes)
1231 nodes.insert(node);
1232 }
1233 int extendCandidate(const NodeSet &other) const {
1234 if (graphId != other.graphId)
1235 return 0;
1236 int newNodes = 0;
1237 bool intersect = false;
1238 for (int node : other.nodes)
1239 if (nodes.count(node) > 0)
1240 intersect = true;
1241 else
1242 newNodes++;
1243 return intersect ? newNodes : 0;
1244 }
1245 bool operator <(const NodeSet &other) const {
1246 if (graphId != other.graphId)
1247 return graphId < other.graphId;
1248 return nodes < other.nodes;
1249 }
1250 std::string to_string() const {
1251 std::string str = graphId + "(";
1252 bool first = true;
1253 for (int node : nodes) {
1254 str += my_stringf("%s%d", first ? "" : " ", node);
1255 first = false;
1256 }
1257 return str + ")";
1258 }
1259 };
1260
1261 void solveForMining(std::vector<Solver::Result> &results, const GraphData &needle)
1262 {
1263 bool backupVerbose = verbose;
1264 verbose = false;
1265
1266 for (auto &it : graphData)
1267 {
1268 GraphData &haystack = it.second;
1269
1270 std::vector<std::set<int>> enumerationMatrix;
1271 std::map<std::string, std::set<std::string>> initialMappings;
1272 generateEnumerationMatrix(enumerationMatrix, needle, haystack, initialMappings);
1273
1274 haystack.usedNodes.resize(haystack.graph.nodes.size());
1275 ullmannRecursion(results, enumerationMatrix, 0, needle, haystack, true, -1);
1276 }
1277
1278 verbose = backupVerbose;
1279 }
1280
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)
1283 {
1284 // my_printf("test: %s\n", testSet.to_string().c_str());
1285
1286 GraphData needle;
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);
1293
1294 std::vector<Solver::Result> ullmannResults;
1295 solveForMining(ullmannResults, needle);
1296
1297 int matches = 0;
1298 std::map<std::string, int> matchesPerGraph;
1299 std::set<NodeSet> thisNodeSetSet;
1300
1301 for (auto &it : ullmannResults)
1302 {
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);
1307
1308 // my_printf("match: %s%s\n", resultSet.to_string().c_str(), usedSets.count(resultSet) > 0 ? " (dup)" : "");
1309
1310 #if 0
1311 if (usedSets.count(resultSet) > 0) {
1312 // Because of shorted pins isomorphisim is not always bidirectional!
1313 //
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.
1317 //
1318 assert(thisNodeSetSet.count(resultSet) > 0);
1319 continue;
1320 }
1321 #else
1322 if (thisNodeSetSet.count(resultSet) > 0)
1323 continue;
1324 #endif
1325
1326 usedSets.insert(resultSet);
1327 thisNodeSetSet.insert(resultSet);
1328
1329 matchesPerGraph[it.haystackGraphId]++;
1330 if (limitMatchesPerGraph < 0 || matchesPerGraph[it.haystackGraphId] < limitMatchesPerGraph)
1331 matches++;
1332 }
1333
1334 if (matches < minMatches)
1335 return matches;
1336
1337 if (minNodes <= int(testSet.nodes.size()))
1338 {
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);
1348 }
1349 results.push_back(result);
1350 }
1351
1352 nextPool.insert(thisNodeSetSet.begin(), thisNodeSetSet.end());
1353 return matches;
1354 }
1355
1356 void findNodePairs(std::vector<Solver::MineResult> &results, std::set<NodeSet> &nodePairs, int minNodes, int minMatches, int limitMatchesPerGraph)
1357 {
1358 int groupCounter = 0;
1359 std::set<NodeSet> usedPairs;
1360 nodePairs.clear();
1361
1362 if (verbose)
1363 my_printf("\nMining for frequent node pairs:\n");
1364
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))
1368 {
1369 const std::string &graphId = graph_it.first;
1370 const auto &graph = graph_it.second.graph;
1371 int node2 = adj_it.first;
1372
1373 if (node1 == node2)
1374 continue;
1375
1376 NodeSet pair(graphId, node1, node2);
1377
1378 if (usedPairs.count(pair) > 0)
1379 continue;
1380
1381 int matches = testForMining(results, usedPairs, nodePairs, pair, graphId, graph, minNodes, minMatches, limitMatchesPerGraph);
1382
1383 if (verbose)
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*" : "");
1386
1387 if (minMatches <= matches)
1388 groupCounter++;
1389 }
1390
1391 if (verbose)
1392 my_printf("Found a total of %d subgraphs in %d groups.\n", int(nodePairs.size()), groupCounter);
1393 }
1394
1395 void findNextPool(std::vector<Solver::MineResult> &results, std::set<NodeSet> &pool,
1396 int oldSetSize, int increment, int minNodes, int minMatches, int limitMatchesPerGraph)
1397 {
1398 int groupCounter = 0;
1399 std::map<std::string, std::vector<const NodeSet*>> poolPerGraph;
1400 std::set<NodeSet> nextPool;
1401
1402 for (auto &it : pool)
1403 poolPerGraph[it.graphId].push_back(&it);
1404
1405 if (verbose)
1406 my_printf("\nMining for frequent subcircuits of size %d using increment %d:\n", oldSetSize+increment, increment);
1407
1408 int count = 0;
1409 for (auto &it : poolPerGraph)
1410 {
1411 std::map<int, std::set<int>> node2sets;
1412 std::set<NodeSet> usedSets;
1413
1414 for (int idx = 0; idx < int(it.second.size()); idx++) {
1415 for (int node : it.second[idx]->nodes)
1416 node2sets[node].insert(idx);
1417 }
1418
1419 for (int idx1 = 0; idx1 < int(it.second.size()); idx1++, count++)
1420 {
1421 std::set<int> idx2set;
1422
1423 for (int node : it.second[idx1]->nodes)
1424 for (int idx2 : node2sets[node])
1425 if (idx2 > idx1)
1426 idx2set.insert(idx2);
1427
1428 for (int idx2 : idx2set)
1429 {
1430 if (it.second[idx1]->extendCandidate(*it.second[idx2]) != increment)
1431 continue;
1432
1433 NodeSet mergedSet = *it.second[idx1];
1434 mergedSet.extend(*it.second[idx2]);
1435
1436 if (usedSets.count(mergedSet) > 0)
1437 continue;
1438
1439 const std::string &graphId = it.first;
1440 const auto &graph = graphData[it.first].graph;
1441
1442 if (verbose) {
1443 my_printf("<%d%%/%d> Found %s[", int(100*count/pool.size()), oldSetSize+increment, graphId.c_str());
1444 bool first = true;
1445 for (int nodeIdx : mergedSet.nodes) {
1446 my_printf("%s%s", first ? "" : ",", graph.nodes[nodeIdx].nodeId.c_str());
1447 first = false;
1448 }
1449 my_printf("] ->");
1450 }
1451
1452 int matches = testForMining(results, usedSets, nextPool, mergedSet, graphId, graph, minNodes, minMatches, limitMatchesPerGraph);
1453
1454 if (verbose)
1455 my_printf(" %d%s\n", matches, matches < minMatches ? " *purge*" : "");
1456
1457 if (minMatches <= matches)
1458 groupCounter++;
1459 }
1460 }
1461 }
1462
1463 pool.swap(nextPool);
1464
1465 if (verbose)
1466 my_printf("Found a total of %d subgraphs in %d groups.\n", int(pool.size()), groupCounter);
1467 }
1468
1469 // interface to the public solver class
1470
1471 protected:
1472 SolverWorker(Solver *userSolver) : userSolver(userSolver), verbose(false)
1473 {
1474 }
1475
1476 void setVerbose()
1477 {
1478 verbose = true;
1479 }
1480
1481 void addGraph(std::string graphId, const Graph &graph)
1482 {
1483 assert(graphData.count(graphId) == 0);
1484
1485 GraphData &gd = graphData[graphId];
1486 gd.graphId = graphId;
1487 gd.graph = graph;
1488 diCache.add(gd.graph, gd.adjMatrix, graphId, userSolver);
1489 }
1490
1491 void addCompatibleTypes(std::string needleTypeId, std::string haystackTypeId)
1492 {
1493 compatibleTypes[needleTypeId].insert(haystackTypeId);
1494 }
1495
1496 void addCompatibleConstants(int needleConstant, int haystackConstant)
1497 {
1498 compatibleConstants[needleConstant].insert(haystackConstant);
1499 }
1500
1501 void addSwappablePorts(std::string needleTypeId, const std::set<std::string> &ports)
1502 {
1503 swapPorts[needleTypeId].insert(ports);
1504 diCache.compareCache.clear();
1505 }
1506
1507 void addSwappablePortsPermutation(std::string needleTypeId, const std::map<std::string, std::string> &portMapping)
1508 {
1509 swapPermutations[needleTypeId].insert(portMapping);
1510 diCache.compareCache.clear();
1511 }
1512
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)
1515 {
1516 assert(graphData.count(needleGraphId) > 0);
1517 assert(graphData.count(haystackGraphId) > 0);
1518
1519 const GraphData &needle = graphData[needleGraphId];
1520 GraphData &haystack = graphData[haystackGraphId];
1521
1522 std::vector<std::set<int>> enumerationMatrix;
1523 generateEnumerationMatrix(enumerationMatrix, needle, haystack, initialMappings);
1524
1525 if (verbose)
1526 {
1527 my_printf("\n");
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());
1531
1532 my_printf("\n");
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());
1536
1537 my_printf("\n");
1538 my_printf("Needle Adjecency Matrix:\n");
1539 printAdjMatrix(needle.adjMatrix);
1540
1541 my_printf("\n");
1542 my_printf("Haystack Adjecency Matrix:\n");
1543 printAdjMatrix(haystack.adjMatrix);
1544
1545 my_printf("\n");
1546 my_printf("Edge Types:\n");
1547 diCache.printEdgeTypes();
1548
1549 my_printf("\n");
1550 my_printf("Enumeration Matrix (haystack nodes at column indices):\n");
1551 printEnumerationMatrix(enumerationMatrix, haystack.graph.nodes.size());
1552 }
1553
1554 haystack.usedNodes.resize(haystack.graph.nodes.size());
1555 ullmannRecursion(results, enumerationMatrix, 0, needle, haystack, allowOverlap, maxSolutions > 0 ? results.size() + maxSolutions : -1);
1556 }
1557
1558 void mine(std::vector<Solver::MineResult> &results, int minNodes, int maxNodes, int minMatches, int limitMatchesPerGraph)
1559 {
1560 int nodeSetSize = 2;
1561 std::set<NodeSet> pool;
1562 findNodePairs(results, pool, minNodes, minMatches, limitMatchesPerGraph);
1563
1564 while ((maxNodes < 0 || nodeSetSize < maxNodes) && pool.size() > 0)
1565 {
1566 int increment = nodeSetSize - 1;
1567 if (nodeSetSize + increment >= minNodes)
1568 increment = minNodes - nodeSetSize;
1569 if (nodeSetSize >= minNodes)
1570 increment = 1;
1571
1572 findNextPool(results, pool, nodeSetSize, increment, minNodes, minMatches, limitMatchesPerGraph);
1573 nodeSetSize += increment;
1574 }
1575 }
1576
1577 void clearOverlapHistory()
1578 {
1579 for (auto &it : graphData)
1580 it.second.usedNodes.clear();
1581 }
1582
1583 void clearConfig()
1584 {
1585 compatibleTypes.clear();
1586 compatibleConstants.clear();
1587 swapPorts.clear();
1588 swapPermutations.clear();
1589 diCache.compareCache.clear();
1590 }
1591
1592 friend class Solver;
1593 };
1594
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>&)
1596 {
1597 return true;
1598 }
1599
1600 std::string Solver::userAnnotateEdge(const std::string&, const std::string&, void*, const std::string&, void*)
1601 {
1602 return std::string();
1603 }
1604
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*)
1606 {
1607 return true;
1608 }
1609
1610 bool Solver::userCheckSolution(const Result&)
1611 {
1612 return true;
1613 }
1614
1615 SubCircuit::Solver::Solver()
1616 {
1617 worker = new SolverWorker(this);
1618 }
1619
1620 SubCircuit::Solver::~Solver()
1621 {
1622 delete worker;
1623 }
1624
1625 void SubCircuit::Solver::setVerbose()
1626 {
1627 worker->setVerbose();
1628 }
1629
1630 void SubCircuit::Solver::addGraph(std::string graphId, const Graph &graph)
1631 {
1632 worker->addGraph(graphId, graph);
1633 }
1634
1635 void SubCircuit::Solver::addCompatibleTypes(std::string needleTypeId, std::string haystackTypeId)
1636 {
1637 worker->addCompatibleTypes(needleTypeId, haystackTypeId);
1638 }
1639
1640 void SubCircuit::Solver::addCompatibleConstants(int needleConstant, int haystackConstant)
1641 {
1642 worker->addCompatibleConstants(needleConstant, haystackConstant);
1643 }
1644
1645 void SubCircuit::Solver::addSwappablePorts(std::string needleTypeId, std::string portId1, std::string portId2, std::string portId3, std::string portId4)
1646 {
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);
1654 }
1655
1656 void SubCircuit::Solver::addSwappablePorts(std::string needleTypeId, std::set<std::string> ports)
1657 {
1658 worker->addSwappablePorts(needleTypeId, ports);
1659 }
1660
1661 void SubCircuit::Solver::addSwappablePortsPermutation(std::string needleTypeId, std::map<std::string, std::string> portMapping)
1662 {
1663 worker->addSwappablePortsPermutation(needleTypeId, portMapping);
1664 }
1665
1666 void SubCircuit::Solver::solve(std::vector<Result> &results, std::string needleGraphId, std::string haystackGraphId, bool allowOverlap, int maxSolutions)
1667 {
1668 std::map<std::string, std::set<std::string>> emptyInitialMapping;
1669 worker->solve(results, needleGraphId, haystackGraphId, emptyInitialMapping, allowOverlap, maxSolutions);
1670 }
1671
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)
1674 {
1675 worker->solve(results, needleGraphId, haystackGraphId, initialMappings, allowOverlap, maxSolutions);
1676 }
1677
1678 void SubCircuit::Solver::mine(std::vector<MineResult> &results, int minNodes, int maxNodes, int minMatches, int limitMatchesPerGraph)
1679 {
1680 worker->mine(results, minNodes, maxNodes, minMatches, limitMatchesPerGraph);
1681 }
1682
1683 void SubCircuit::Solver::clearOverlapHistory()
1684 {
1685 worker->clearOverlapHistory();
1686 }
1687
1688 void SubCircuit::Solver::clearConfig()
1689 {
1690 worker->clearConfig();
1691 }