ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
rrrLevelBasePartitioner.h
Go to the documentation of this file.
1#pragma once
2
3#include <utility>
4#include <map>
5#include <tuple>
6#include <random>
7
8#include "rrrParameter.h"
9#include "rrrUtils.h"
10
12
13namespace rrr {
14
15 template <typename Ntk>
17 private:
18 // pointer to network
19 Ntk *pNtk;
20
21 // parameters
22 int nVerbose;
23 int nPartitionSize;
24 int nPartitionSizeMin;
25 std::string strVerbosePrefix;
26
27 // data
28 int nMaxLevel;
29 std::vector<int> vLevels;
30 std::map<Ntk *, std::tuple<std::set<int>, std::vector<int>, std::vector<bool>, std::vector<int>>> mSubNtk2Io;
31 std::set<int> sBlocked;
32 std::vector<bool> vFailed;
33
34 // print
35 template<typename... Args>
36 void Print(int nVerboseLevel, Args... args);
37
38 // subroutines
39 void ComputeLevel();
40 std::vector<int> GetIOI(int id, int nLevels);
41 Ntk *ExtractIOI(int id);
42
43 public:
44 // constructors
46 void AssignNetwork(Ntk *pNtk);
47
48 // APIs
49 Ntk *Extract(int iSeed);
50 void Insert(Ntk *pSubNtk);
51 };
52
53 /* {{{ Print */
54
55 template <typename Ntk>
56 template <typename... Args>
57 inline void LevelBasePartitioner<Ntk>::Print(int nVerboseLevel, Args... args) {
58 if(nVerbose > nVerboseLevel) {
59 std::cout << strVerbosePrefix;
60 for(int i = 0; i < nVerboseLevel; i++) {
61 std::cout << "\t";
62 }
63 PrintNext(std::cout, args...);
64 std::cout << std::endl;
65 }
66 }
67
68 /* }}} */
69
70 /* {{{ Subroutines */
71
72 template <typename Ntk>
73 void LevelBasePartitioner<Ntk>::ComputeLevel() {
74 nMaxLevel = 0;
75 vLevels.clear();
76 vLevels.resize(pNtk->GetNumNodes());
77 pNtk->ForEachInt([&](int id) {
78 pNtk->ForEachFanin(id, [&](int fi) {
79 if(vLevels[id] < vLevels[fi]) {
80 vLevels[id] = vLevels[fi];
81 }
82 });
83 vLevels[id] += 1;
84 if(nMaxLevel < vLevels[id]) {
85 nMaxLevel = vLevels[id];
86 }
87 });
88 }
89
90 template <typename Ntk>
91 std::vector<int> LevelBasePartitioner<Ntk>::GetIOI(int id, int nLevels) {
92 std::vector<int> vNodes, vNodes2;
93 vNodes.push_back(id);
94 pNtk->ForEachTfiUpdate(id, false, [&](int fi) {
95 if(vLevels[fi] < vLevels[id] - nLevels) {
96 return false;
97 }
98 vNodes.push_back(fi);
99 return true;
100 });
101 pNtk->ForEachTfosUpdate(vNodes, false, [&](int fo) {
102 if(vLevels[fo] > vLevels[id] + nLevels) {
103 return false;
104 }
105 vNodes2.push_back(fo);
106 return true;
107 });
108 vNodes.clear();
109 pNtk->ForEachTfisUpdate(vNodes2, false, [&](int fi) {
110 if(vLevels[fi] < vLevels[id] - nLevels) {
111 return false;
112 }
113 vNodes.push_back(fi);
114 return true;
115 });
116 return vNodes;
117 }
118
119 template <typename Ntk>
120 Ntk *LevelBasePartitioner<Ntk>::ExtractIOI(int id) {
121 // collect IOI nodes
122 assert(!sBlocked.count(id));
123 int nLevels = 1;
124 std::vector<int> vNodes = GetIOI(id, nLevels);
125 Print(1, "level", NS(), nLevels, ":", "size =", int_size(vNodes));
126 std::vector<int> vNodesNew = GetIOI(id, nLevels + 1);
127 Print(1, "level", NS(), nLevels + 1, ":", "size =", int_size(vNodesNew));
128 // gradually increase level until it hits partition size limit
129 while(int_size(vNodesNew) < nPartitionSize) {
130 if(vLevels[id] - nLevels < 1 && vLevels[id] + nLevels >= nMaxLevel) { // already maximum
131 break;
132 }
133 vNodes = vNodesNew;
134 nLevels++;
135 vNodesNew = GetIOI(id, nLevels + 1);
136 Print(1, "level", NS(), nLevels + 1, ":", "size =", int_size(vNodesNew));
137 }
138 std::set<int> sNodes(vNodes.begin(), vNodes.end());
139 // remove nodes that are already blocked
140 for(std::set<int>::iterator it = sNodes.begin(); it != sNodes.end();) {
141 if(sBlocked.count(*it)) {
142 it = sNodes.erase(it);
143 } else {
144 it++;
145 }
146 }
147 Print(1, "checking:", "size =", int_size(sNodes));
148 // get partition IO
149 std::set<int> sInputs, sOutputs;
150 for(int id: sNodes) {
151 pNtk->ForEachFanin(id, [&](int fi) {
152 if(!sNodes.count(fi)) {
153 sInputs.insert(fi);
154 }
155 });
156 bool fOutput = false;
157 pNtk->ForEachFanout(id, true, [&](int fo) {
158 if(!sNodes.count(fo)) {
159 fOutput = true;
160 }
161 });
162 if(fOutput) {
163 sOutputs.insert(id);
164 }
165 }
166 Print(2, "nodes:", sNodes);
167 Print(2, "inputs:", sInputs);
168 Print(2, "outputs:", sOutputs);
169 if(int_size(sNodes) < nPartitionSizeMin) {
170 return NULL;
171 }
172 // check loops and just give up if any
173 std::set<int> sFanouts;
174 for(int id: sOutputs) {
175 pNtk->ForEachFanout(id, false, [&](int fo) {
176 if(!sNodes.count(fo)) {
177 sFanouts.insert(fo);
178 }
179 });
180 }
181 if(pNtk->IsReachable(sFanouts, sInputs)) {
182 return NULL;
183 }
184 for(auto const &entry: mSubNtk2Io) {
185 if(!pNtk->IsReachable(sOutputs, std::get<1>(entry.second))) {
186 continue;
187 }
188 if(!pNtk->IsReachable(std::get<3>(entry.second), sInputs)) {
189 continue;
190 }
191 return NULL;
192 }
193 // extract by inputs, internals, and outputs (no checks needed in ntk side)
194 std::vector<int> vInputs(sInputs.begin(), sInputs.end());
195 std::vector<int> vOutputs(sOutputs.begin(), sOutputs.end());
196 Ntk *pSubNtk = pNtk->Extract(sNodes, vInputs, vOutputs);
197 // return subntk to be modified, while remember IOs
198 for(int id: sNodes) {
199 sBlocked.insert(id);
200 }
201 mSubNtk2Io.emplace(pSubNtk, std::make_tuple(std::move(sNodes), std::move(vInputs), std::vector<bool>(vInputs.size()), std::move(vOutputs)));
202 return pSubNtk;
203 }
204
205 /* }}} */
206
207 /* {{{ Constructors */
208
209 template <typename Ntk>
211 nVerbose(pPar->nPartitionerVerbose),
212 nPartitionSize(pPar->nPartitionSize),
213 nPartitionSizeMin(pPar->nPartitionSizeMin) {
214 }
215
216 template <typename Ntk>
218 pNtk = pNtk_;
219 assert(mSubNtk2Io.empty());
220 assert(sBlocked.empty());
221 vFailed.clear();
222 vLevels.clear();
223 }
224
225 /* }}} */
226
227 /* {{{ APIs */
228
229 template <typename Ntk>
231 // pick a center node from candidates that do not belong to any other ongoing partitions
232 vFailed.resize(pNtk->GetNumNodes());
233 if(vLevels.empty()) {
234 ComputeLevel();
235 }
236 std::mt19937 rng(iSeed);
237 std::vector<int> vInts = pNtk->GetInts();
238 std::shuffle(vInts.begin(), vInts.end(), rng);
239 for(int i = 0; i < int_size(vInts); i++) {
240 int id = vInts[i];
241 if(vFailed[id]) {
242 continue;
243 }
244 if(!sBlocked.count(id)) {
245 Print(0, "try partitioning with node", id, "(", i, "/", int_size(vInts), ")");
246 Ntk *pSubNtk = ExtractIOI(id);
247 if(pSubNtk) {
248 return pSubNtk;
249 }
250 }
251 vFailed[id] = true;
252 }
253 return NULL;
254 }
255
256 template <typename Ntk>
258 for(int i: std::get<0>(mSubNtk2Io[pSubNtk])) {
259 sBlocked.erase(i);
260 }
261 std::pair<std::vector<int>, std::vector<bool>> vNewSignals = pNtk->Insert(pSubNtk, std::get<1>(mSubNtk2Io[pSubNtk]), std::get<2>(mSubNtk2Io[pSubNtk]), std::get<3>(mSubNtk2Io[pSubNtk]));
262 std::vector<int> &vOldOutputs = std::get<3>(mSubNtk2Io[pSubNtk]);
263 std::vector<int> &vNewOutputs = vNewSignals.first;
264 std::vector<bool> &vNewCompls = vNewSignals.second;
265 // need to remap updated outputs that are used as inputs in other partitions
266 std::map<int, int> mOutput2Idx;
267 for(int idx = 0; idx < int_size(vOldOutputs); idx++) {
268 mOutput2Idx[vOldOutputs[idx]] = idx;
269 }
270 for(auto &entry: mSubNtk2Io) {
271 if(entry.first != pSubNtk) {
272 std::vector<int> &vInputs = std::get<1>(entry.second);
273 std::vector<bool> &vCompls = std::get<2>(entry.second);
274 for(int i = 0; i < int_size(vInputs); i++) {
275 if(mOutput2Idx.count(vInputs[i])) {
276 int idx = mOutput2Idx[vInputs[i]];
277 vInputs[i] = vNewOutputs[idx];
278 vCompls[i] = vCompls[i] ^ vNewCompls[idx];
279 }
280 }
281 }
282 }
283 delete pSubNtk;
284 mSubNtk2Io.erase(pSubNtk);
285 vFailed.clear(); // clear, there isn't really a way to track
286 vLevels.clear();
287 }
288
289 /* }}} */
290
291}
292
#define ABC_NAMESPACE_CXX_HEADER_START
#define ABC_NAMESPACE_CXX_HEADER_END
LevelBasePartitioner(Parameter const *pPar)
Definition rrr.h:16
void PrintNext(std::ostream &os, T t)
Definition rrrUtils.h:218
#define assert(ex)
Definition util_old.h:213