mirror of https://github.com/YosysHQ/abc.git
345 lines
10 KiB
C++
345 lines
10 KiB
C++
#pragma once
|
|
|
|
#include <utility>
|
|
#include <map>
|
|
#include <tuple>
|
|
#include <random>
|
|
|
|
#include "rrrParameter.h"
|
|
#include "rrrUtils.h"
|
|
|
|
ABC_NAMESPACE_CXX_HEADER_START
|
|
|
|
namespace rrr {
|
|
|
|
template <typename Ntk>
|
|
class Partitioner {
|
|
private:
|
|
// pointer to network
|
|
Ntk *pNtk;
|
|
|
|
// parameters
|
|
int nVerbose;
|
|
int nPartitionSize;
|
|
int nPartitionSizeMin;
|
|
std::string strVerbosePrefix;
|
|
|
|
// data
|
|
std::map<Ntk *, std::tuple<std::set<int>, std::vector<int>, std::vector<bool>, std::vector<int>>> mSubNtk2Io;
|
|
std::set<int> sBlocked;
|
|
std::vector<bool> vFailed;
|
|
|
|
// print
|
|
template<typename... Args>
|
|
void Print(int nVerboseLevel, Args... args);
|
|
|
|
// subroutines
|
|
void GetIo(std::set<int> const &sNodes, std::set<int> &sInputs, std::set<int> &sOutputs);
|
|
std::set<int> GetFanouts(std::set<int> const &sNodes, std::set<int> const &sOutputs);
|
|
std::set<int> GetUnblockedNeighborsAndInners(int id, int nRadius);
|
|
Ntk *ExtractDisjoint(int id);
|
|
|
|
public:
|
|
// constructors
|
|
Partitioner(Parameter const *pPar);
|
|
void AssignNetwork(Ntk *pNtk);
|
|
|
|
// APIs
|
|
Ntk *Extract(int iSeed);
|
|
void Insert(Ntk *pSubNtk);
|
|
};
|
|
|
|
/* {{{ Print */
|
|
|
|
template <typename Ntk>
|
|
template <typename... Args>
|
|
inline void Partitioner<Ntk>::Print(int nVerboseLevel, Args... args) {
|
|
if(nVerbose > nVerboseLevel) {
|
|
std::cout << strVerbosePrefix;
|
|
for(int i = 0; i < nVerboseLevel; i++) {
|
|
std::cout << "\t";
|
|
}
|
|
PrintNext(std::cout, args...);
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ Subroutines */
|
|
|
|
template <typename Ntk>
|
|
void Partitioner<Ntk>::GetIo(std::set<int> const &sNodes, std::set<int> &sInputs, std::set<int> &sOutputs) {
|
|
sInputs.clear();
|
|
sOutputs.clear();
|
|
for(int id: sNodes) {
|
|
pNtk->ForEachFanin(id, [&](int fi) {
|
|
if(!sNodes.count(fi)) {
|
|
sInputs.insert(fi);
|
|
}
|
|
});
|
|
bool fOutput = false;
|
|
pNtk->ForEachFanout(id, true, [&](int fo) {
|
|
if(!sNodes.count(fo)) {
|
|
fOutput = true;
|
|
}
|
|
});
|
|
if(fOutput) {
|
|
sOutputs.insert(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename Ntk>
|
|
std::set<int> Partitioner<Ntk>::GetFanouts(std::set<int> const &sNodes, std::set<int> const &sOutputs) {
|
|
std::set<int> sFanouts;
|
|
for(int id: sOutputs) {
|
|
pNtk->ForEachFanout(id, false, [&](int fo) {
|
|
if(!sNodes.count(fo)) {
|
|
sFanouts.insert(fo);
|
|
}
|
|
});
|
|
}
|
|
return sFanouts;
|
|
}
|
|
|
|
template <typename Ntk>
|
|
std::set<int> Partitioner<Ntk>::GetUnblockedNeighborsAndInners(int id, int nRadius) {
|
|
// return empty set on failure
|
|
// get neighbors
|
|
std::vector<int> vNodes = pNtk->GetNeighbors(id, false, nRadius);
|
|
vNodes.push_back(id);
|
|
std::set<int> sNodes(vNodes.begin(), vNodes.end());
|
|
Print(2, "radius", NS(), nRadius, ":", "size =", int_size(sNodes));
|
|
// remove nodes that are already blocked
|
|
for(std::set<int>::iterator it = sNodes.begin(); it != sNodes.end();) {
|
|
if(sBlocked.count(*it)) {
|
|
it = sNodes.erase(it);
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
Print(2, "unblocked:", "size =", int_size(sNodes));
|
|
if(int_size(sNodes) > nPartitionSize) {
|
|
// too large
|
|
return std::set<int>();
|
|
}
|
|
// get tentative partition IO
|
|
std::set<int> sInputs, sOutputs;
|
|
GetIo(sNodes, sInputs, sOutputs);
|
|
Print(3, "nodes:", sNodes);
|
|
Print(3, "inputs:", sInputs);
|
|
Print(3, "outputs:", sOutputs);
|
|
// include inner nodes
|
|
std::set<int> sFanouts = GetFanouts(sNodes, sOutputs);
|
|
std::vector<int> vInners = pNtk->GetInners(sFanouts, sInputs);
|
|
while(!vInners.empty()) {
|
|
Print(2, "inner size =", int_size(vInners));
|
|
// check overlap
|
|
for(int id: vInners) {
|
|
if(sBlocked.count(id)) {
|
|
// overlapped
|
|
return std::set<int>();
|
|
}
|
|
}
|
|
// expand
|
|
sNodes.insert(vInners.begin(), vInners.end());
|
|
Print(2, "expanded:", "size =", int_size(sNodes));
|
|
if(int_size(sNodes) > nPartitionSize) {
|
|
// too large
|
|
return std::set<int>();
|
|
}
|
|
GetIo(sNodes, sInputs, sOutputs);
|
|
Print(3, "nodes:", sNodes);
|
|
Print(3, "inputs:", sInputs);
|
|
Print(3, "outputs:", sOutputs);
|
|
// recompute fanouts
|
|
sFanouts = GetFanouts(sNodes, sOutputs);
|
|
vInners = pNtk->GetInners(sFanouts, sInputs);
|
|
}
|
|
return sNodes;
|
|
}
|
|
|
|
template <typename Ntk>
|
|
Ntk *Partitioner<Ntk>::ExtractDisjoint(int id) {
|
|
// collect neighbor
|
|
assert(!sBlocked.count(id));
|
|
int nRadius = 1;
|
|
std::set<int> sNodes = GetUnblockedNeighborsAndInners(id, nRadius);
|
|
Print(1, "radius", NS(), nRadius, ":", "size =", int_size(sNodes));
|
|
if(sNodes.empty()) {
|
|
return NULL;
|
|
}
|
|
std::set<int> sNodesNew = GetUnblockedNeighborsAndInners(id, nRadius + 1);
|
|
Print(1, "radius", NS(), nRadius + 1, ":", "size =", int_size(sNodesNew));
|
|
// gradually increase radius until it hits partition size limit
|
|
while(!sNodesNew.empty()) {
|
|
if(int_size(sNodes) == int_size(sNodesNew)) { // likely already maximum
|
|
break;
|
|
}
|
|
sNodes = sNodesNew;
|
|
nRadius++;
|
|
sNodesNew = GetUnblockedNeighborsAndInners(id, nRadius + 1);
|
|
Print(1, "radius", NS(), nRadius + 1, ":", "size =", int_size(sNodesNew));
|
|
}
|
|
if(int_size(sNodes) < nPartitionSizeMin) {
|
|
// too small
|
|
return NULL;
|
|
}
|
|
std::set<int> sInputs, sOutputs;
|
|
GetIo(sNodes, sInputs, sOutputs);
|
|
/* fall back method by removing TFI of outputs reachable to inputs
|
|
if(!vInners.empty()) {
|
|
for(int id: sOutputs) {
|
|
if(!sNodes.count(id)) { // already removed
|
|
continue;
|
|
}
|
|
std::set<int> sFanouts = GetFanouts(sNodes, sOutputs);
|
|
if(pNtk->IsReachable(sFanouts, sInputs)) {
|
|
Print(2, "node", id, "is reachable");
|
|
sNodes.erase(id);
|
|
pNtk->ForEachTfiEnd(id, sInputs, [&](int fi) {
|
|
int r = sNodes.erase(fi);
|
|
assert(r);
|
|
});
|
|
Print(1, "shrinking:", "size =", int_size(sNodes));
|
|
if(int_size(sNodes) < nPartitionSizeMin) {
|
|
// too small
|
|
return NULL;
|
|
}
|
|
// recompute inputs
|
|
sInputs.clear();
|
|
for(int id: sNodes) {
|
|
pNtk->ForEachFanin(id, [&](int fi) {
|
|
if(!sNodes.count(fi)) {
|
|
sInputs.insert(fi);
|
|
}
|
|
});
|
|
}
|
|
Print(3, "nodes:", sNodes);
|
|
Print(3, "inputs:", sInputs);
|
|
}
|
|
}
|
|
// recompute outputs
|
|
for(std::set<int>::iterator it = sOutputs.begin(); it != sOutputs.end();) {
|
|
if(!sNodes.count(*it)) {
|
|
it = sOutputs.erase(it);
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
Print(3, "new outputs:", sOutputs);
|
|
}
|
|
*/
|
|
// ensure outputs of both partitions do not reach each other's inputs at the same time
|
|
for(auto const &entry: mSubNtk2Io) {
|
|
if(!pNtk->IsReachable(sOutputs, std::get<1>(entry.second))) {
|
|
continue;
|
|
}
|
|
if(!pNtk->IsReachable(std::get<3>(entry.second), sInputs)) {
|
|
continue;
|
|
}
|
|
// if(nVerbose) {
|
|
// std::cout << "POTENTIAL LOOPS" << std::endl;
|
|
// }
|
|
return NULL;
|
|
}
|
|
assert(int_size(sNodes) <= nPartitionSize);
|
|
assert(int_size(sNodes) >= nPartitionSizeMin);
|
|
// extract by inputs, internals, and outputs (no checks needed in ntk side)
|
|
std::vector<int> vInputs(sInputs.begin(), sInputs.end());
|
|
std::vector<int> vOutputs(sOutputs.begin(), sOutputs.end());
|
|
Ntk *pSubNtk = pNtk->Extract(sNodes, vInputs, vOutputs);
|
|
// return subntk to be modified, while remember IOs
|
|
for(int id: sNodes) {
|
|
sBlocked.insert(id);
|
|
}
|
|
mSubNtk2Io.emplace(pSubNtk, std::make_tuple(std::move(sNodes), std::move(vInputs), std::vector<bool>(vInputs.size()), std::move(vOutputs)));
|
|
return pSubNtk;
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ Constructors */
|
|
|
|
template <typename Ntk>
|
|
Partitioner<Ntk>::Partitioner(Parameter const *pPar) :
|
|
nVerbose(pPar->nPartitionerVerbose),
|
|
nPartitionSize(pPar->nPartitionSize),
|
|
nPartitionSizeMin(pPar->nPartitionSizeMin) {
|
|
}
|
|
|
|
template <typename Ntk>
|
|
void Partitioner<Ntk>::AssignNetwork(Ntk *pNtk_) {
|
|
pNtk = pNtk_;
|
|
assert(mSubNtk2Io.empty());
|
|
assert(sBlocked.empty());
|
|
vFailed.clear();
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ APIs */
|
|
|
|
template <typename Ntk>
|
|
Ntk *Partitioner<Ntk>::Extract(int iSeed) {
|
|
// pick a center node from candidates that do not belong to any other ongoing partitions
|
|
vFailed.resize(pNtk->GetNumNodes());
|
|
std::mt19937 rng(iSeed);
|
|
std::vector<int> vInts = pNtk->GetInts();
|
|
std::shuffle(vInts.begin(), vInts.end(), rng);
|
|
for(int i = 0; i < int_size(vInts); i++) {
|
|
int id = vInts[i];
|
|
if(vFailed[id]) {
|
|
continue;
|
|
}
|
|
if(!sBlocked.count(id)) {
|
|
Print(0, "try partitioning with node", id, "(", i, "/", int_size(vInts), ")");
|
|
Ntk *pSubNtk = ExtractDisjoint(id);
|
|
if(pSubNtk) {
|
|
return pSubNtk;
|
|
}
|
|
}
|
|
vFailed[id] = true;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
template <typename Ntk>
|
|
void Partitioner<Ntk>::Insert(Ntk *pSubNtk) {
|
|
for(int i: std::get<0>(mSubNtk2Io[pSubNtk])) {
|
|
sBlocked.erase(i);
|
|
}
|
|
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]));
|
|
std::vector<int> &vOldOutputs = std::get<3>(mSubNtk2Io[pSubNtk]);
|
|
std::vector<int> &vNewOutputs = vNewSignals.first;
|
|
std::vector<bool> &vNewCompls = vNewSignals.second;
|
|
// need to remap updated outputs that are used as inputs in other partitions
|
|
std::map<int, int> mOutput2Idx;
|
|
for(int idx = 0; idx < int_size(vOldOutputs); idx++) {
|
|
mOutput2Idx[vOldOutputs[idx]] = idx;
|
|
}
|
|
for(auto &entry: mSubNtk2Io) {
|
|
if(entry.first != pSubNtk) {
|
|
std::vector<int> &vInputs = std::get<1>(entry.second);
|
|
std::vector<bool> &vCompls = std::get<2>(entry.second);
|
|
for(int i = 0; i < int_size(vInputs); i++) {
|
|
if(mOutput2Idx.count(vInputs[i])) {
|
|
int idx = mOutput2Idx[vInputs[i]];
|
|
vInputs[i] = vNewOutputs[idx];
|
|
vCompls[i] = vCompls[i] ^ vNewCompls[idx];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete pSubNtk;
|
|
mSubNtk2Io.erase(pSubNtk);
|
|
vFailed.clear(); // clear, there isn't really a way to track
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
}
|
|
|
|
ABC_NAMESPACE_CXX_HEADER_END
|