abc/src/aig/gia/giaDecGraph.cpp

2214 lines
69 KiB
C++

/**CFile****************************************************************
FileName [giaDecGraph.c]
SystemName [ABC: Logic synthesis and verification system.]
PackageName [Scalable AIG package.]
Synopsis [Implementation of circuit learning.]
Author [Jiun-Hao Chen]
Affiliation [UC Berkeley]
Date [Ver. 1.0. Started - 2025.]
Revision [$Id: giaDecGraph.c,v 1.00 2025/01/01 00:00:00 Exp $]
***********************************************************************/
#ifdef _WIN32
#ifndef __MINGW32__
#pragma warning(disable : 4786) // warning C4786: identifier was truncated to '255' characters in the browser information
#endif
#endif
#include <cmath>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <set>
#include <numeric>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <functional>
#include "gia.h"
#include "misc/vec/vecHash.h"
#include "misc/extra/extra.h"
ABC_NAMESPACE_IMPL_START
namespace DecGraph {
template<typename I,
typename std::enable_if<std::is_integral<I>::value, int>::type = 0>
struct integer_iterator {
using iterator_category = std::input_iterator_tag;
using value_type = I;
using difference_type = I;
using pointer = I*;
using reference = I&;
explicit integer_iterator(I value)
: value(value) {
}
I operator*() const {
return value;
}
I const* operator->() const {
return &value;
}
integer_iterator& operator++() {
++value;
return *this;
}
integer_iterator operator++(int) {
const auto copy = *this;
++*this;
return copy;
}
bool operator==(const integer_iterator& other) const {
return value == other.value;
}
bool operator!=(const integer_iterator& other) const {
return value != other.value;
}
protected:
I value;
};
template<typename I, typename std::enable_if<std::is_integral<I>::value,
bool>::type
= true>
struct integer_range {
public:
integer_range(I begin, I end)
: begin_(begin), end_(end) {
}
integer_iterator<I> begin() const {
return begin_;
}
integer_iterator<I> end() const {
return end_;
}
private:
integer_iterator<I> begin_;
integer_iterator<I> end_;
};
template<typename Integer1, typename Integer2,
typename std::enable_if<std::is_integral<Integer1>::value,
bool>::type
= true,
typename std::enable_if<std::is_integral<Integer2>::value,
bool>::type
= true>
integer_range<Integer2> irange(Integer1 begin, Integer2 end) {
return {static_cast<Integer2>(begin),
std::max(static_cast<Integer2>(begin), end)};
}
template<typename Integer,
typename std::enable_if<std::is_integral<Integer>::value,
bool>::type
= true>
integer_range<Integer> irange(Integer end) {
return {Integer(), std::max(Integer(), end)};
}
// End Iterger Iterator
static int IdToLit(int id, bool fCompl) {
return (id << 1) | (fCompl ? 1 : 0);
}
static int LitToId(int lit) {
return lit >> 1;
}
static int LitNot(int lit) {
return lit ^ 1;
}
static int LitNotCond(int lit, bool cond) {
return lit ^ (int)cond;
}
static int LitIsComplement(int lit) {
return (lit & 1);
}
#ifdef _MSC_VER
#include <intrin.h>
#define __builtin_popcount __popcnt
#endif
#ifdef DECTHREAD
#else
static int decGraphXadd(int* addr, int delta) {
int temp = *addr;
*addr += delta;
return temp;
}
#endif
static int neededWords(int num_variables) {
return num_variables <= 6 ? 1 : 1 << (num_variables - 6);
}
static inline const uint64_t permutation_mask[5][3] = {
{0x9999999999999999, 0x2222222222222222, 0x4444444444444444},
{0xC3C3C3C3C3C3C3C3, 0x0C0C0C0C0C0C0C0C, 0x3030303030303030},
{0xF00FF00FF00FF00F, 0x00F000F000F000F0, 0x0F000F000F000F00},
{0xFF0000FFFF0000FF, 0x0000FF000000FF00, 0x00FF000000FF0000},
{0xFFFF00000000FFFF, 0x00000000FFFF0000, 0x0000FFFF00000000}};
static inline const uint64_t swap_mask[6][6] = {
{0x2222222222222222, 0x0A0A0A0A0A0A0A0A, 0x00AA00AA00AA00AA, 0x0000AAAA0000AAAA, 0x00000000AAAAAAAA, 0xAAAAAAAAAAAAAAAA},
{0x0000000000000000, 0x0C0C0C0C0C0C0C0C, 0x00CC00CC00CC00CC, 0x0000CCCC0000CCCC, 0x00000000CCCCCCCC, 0xCCCCCCCCCCCCCCCC},
{0x0000000000000000, 0x0000000000000000, 0x00F000F000F000F0, 0x0000F0F00000F0F0, 0x00000000F0F0F0F0, 0xF0F0F0F0F0F0F0F0},
{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000FF000000FF00, 0x00000000FF00FF00, 0xFF00FF00FF00FF00},
{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x00000000FFFF0000, 0xFFFF0000FFFF0000},
{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0xFFFFFFFF00000000}};
static inline const uint64_t elementary_mask[6] = {
0xAAAAAAAAAAAAAAAA,
0xCCCCCCCCCCCCCCCC,
0xF0F0F0F0F0F0F0F0,
0xFF00FF00FF00FF00,
0xFFFF0000FFFF0000,
0xFFFFFFFF00000000};
static inline const uint64_t extend_mask[5] = {
0xC000000000000000,
0xF000000000000000,
0xFF00000000000000,
0xFFFF000000000000,
0xFFFFFFFF00000000};
static inline const uint64_t ones_mask[7]{
0x0000000000000001,
0x0000000000000003,
0x000000000000000f,
0x00000000000000ff,
0x000000000000ffff,
0x00000000ffffffff,
0xffffffffffffffff};
struct TruthTableData {
int refcount;
std::vector<uint64_t> data;
std::vector<uint64_t> place_to_variable;
std::vector<uint64_t> variable_to_place;
};
class TruthTable {
public:
// empty
TruthTable();
// release
~TruthTable();
// copy
TruthTable(const TruthTable& m);
// assign
TruthTable& operator=(const TruthTable& m);
// deep copy
TruthTable clone(void) const;
// deep copy from other truth table, inplace
void clone_from(const TruthTable& tt);
public:
// build from bits
TruthTable(uint64_t bits);
public:
// allocate truth table
void create(uint64_t bits);
public:
// refcount++
void addref();
// refcount--
void release();
public:
bool operator[](int index);
bool operator==(const TruthTable& rhs) const;
bool operator!=(const TruthTable& rhs) const;
TruthTable operator~(void);
public:
uint8_t nVars() const;
uint64_t nBits() const;
uint32_t nWords() const;
bool empty() const;
public:
uint64_t* data() const;
void set(int index, bool value);
void rand();
void readBinary(std::string bitstream);
void readBinaryReverse(std::string bitstream);
void readHex(std::string bitstream);
void readHexReverse(std::string bitstream);
public:
void resetPosition(void);
void moveTo(uint32_t variable, uint8_t place, bool adjecent = false);
void swapVariable(int variable1, int variable2);
void extend(int nVariable, std::vector<int> variableOrder = {});
int countCofactor(int nBoundSet, std::vector<TruthTable>& cofactors, std::vector<TruthTable>& boundSetFunctions, bool derive = false);
TruthTable cofactor(uint32_t variable, bool negative);
uint64_t countOne(void);
bool hasVariable(uint32_t variable);
int supportSize(void);
int isAndorOr(void);
void variablePartition(int nMSB, std::vector<int>& MSB, std::vector<int>& LSB);
uint32_t getVariable(int place);
void swapAdjacent(uint64_t* rdata, uint64_t* data, int index);
float cost(void);
public:
std::string hash_str(void);
private:
void assign(int offset, uint64_t value);
void assign(uint64_t value);
public:
enum class Format {
BIN,
HEX
};
void print(Format format = Format::BIN);
void printReverse(Format format = Format::BIN);
void printPosition(void);
std::string str(Format format = Format::BIN);
std::string strReverse(Format format = Format::BIN);
private:
std::vector<int64_t> fourierTransform_rec(uint64_t begin, uint64_t end);
public:
std::vector<float> fourierTransform(void);
std::vector<float> fourierWeight(void);
float fourierCost(void);
private:
// pointer to the read data
TruthTableData* _data;
// reference counter
int* _refcount;
// number of variables
uint8_t _vars;
// number of bits
uint64_t _bits;
};
inline TruthTable::TruthTable()
: _data(nullptr), _refcount(nullptr), _vars(0), _bits(0) {
}
inline TruthTable::~TruthTable() {
release();
}
inline TruthTable::TruthTable(const TruthTable& m)
: _data(m._data), _refcount(m._refcount), _vars(m._vars), _bits(m._bits) {
addref();
}
inline TruthTable& TruthTable::operator=(const TruthTable& m) {
if (this == &m)
return *this;
if (m._refcount)
decGraphXadd(m._refcount, 1);
release();
_data = m._data;
_refcount = m._refcount;
_vars = m._vars;
_bits = m._bits;
return *this;
}
inline TruthTable::TruthTable(uint64_t bits)
: _data(nullptr), _refcount(nullptr), _vars(0), _bits(0) {
create(bits);
}
inline void TruthTable::addref(void) {
if (_refcount)
decGraphXadd(_refcount, 1);
}
inline void TruthTable::release(void) {
if (_refcount && decGraphXadd(_refcount, -1) == 1) {
delete _data;
}
_data = nullptr;
_refcount = nullptr;
_vars = 0;
_bits = 0;
}
inline uint8_t TruthTable::nVars() const {
return _vars;
}
inline uint64_t TruthTable::nBits() const {
return _bits;
}
inline uint32_t TruthTable::nWords() const {
return _data ? _data->data.size() : 0;
}
inline bool TruthTable::empty() const {
return (_data == nullptr) || (_bits == 0);
}
inline uint64_t* TruthTable::data() const {
if (_data)
return _data->data.data();
return nullptr;
}
inline bool TruthTable::operator[](int index) {
if (empty())
return false;
return (_data->data[index / (sizeof(uint64_t) * 8)] << (index % (sizeof(uint64_t) * 8))) & 0x8000000000000000;
}
TruthTable TruthTable::clone(void) const {
if (empty())
return TruthTable();
TruthTable tt;
tt.create(_bits);
if (tt.empty()) {
return tt;
}
*tt._data = *this->_data;
*tt._refcount = 1;
return tt;
}
void TruthTable::clone_from(const TruthTable &tt) {
*this = tt.clone();
return;
}
void TruthTable::create(uint64_t bits) {
if (_bits == bits) {
resetPosition();
return;
}
release();
_bits = bits;
_vars = std::log2(bits);
_data = new TruthTableData;
if (_data) {
_data->data.resize(std::max((uint64_t)(bits / (sizeof(uint64_t) * 8)), (uint64_t)1), 0);
resetPosition();
_refcount = &_data->refcount;
*_refcount = 1;
}
return;
}
bool TruthTable::operator==(const TruthTable &rhs) const {
if (this->_data == rhs._data)
return true;
if (nBits() != rhs.nBits())
return false;
uint64_t *ldata = this->data();
uint64_t *rdata = rhs.data();
for (const auto &i : irange(nWords())) {
if (ldata[i] != rdata[i])
return false;
}
return true;
}
bool TruthTable::operator!=(const TruthTable &rhs) const {
return !(*this == rhs);
}
TruthTable TruthTable::operator~(void) {
TruthTable rtt(nBits());
uint64_t *ldata = this->data();
uint64_t *rdata = rtt.data();
for (const auto &i : irange(nWords())) {
rdata[i] = ~ldata[i];
}
if (nBits() < 64) {
for (const auto &i : irange(nBits(), 64)) {
rtt.set(i, false);
}
}
return rtt;
}
void TruthTable::set(int index, bool value) {
if (empty())
return;
uint64_t *data = &_data->data[0];
if (value) {
data[index / (sizeof(uint64_t) * 8)] |= (uint64_t)0x8000000000000000 >> (index % (sizeof(uint64_t) * 8));
} else {
data[index / (sizeof(uint64_t) * 8)] &= ~((uint64_t)0x8000000000000000 >> (index % (sizeof(uint64_t) * 8)));
}
return;
}
void TruthTable::rand(void) {
if (empty())
return;
uint64_t *data = this->data();
for (const auto &i : irange(nWords())) {
data[i] = ((uint64_t)std::rand() << 32) | ((uint64_t)std::rand());
}
for (const auto &i : irange(nBits(), ((uint64_t)64 * nWords()))) {
set(i, false);
}
return;
}
void TruthTable::readBinary(std::string bitstream) {
create(bitstream.size());
for (const auto &i : irange(nBits())) {
set(i, bitstream[i] == '1');
}
return;
}
void TruthTable::readBinaryReverse(std::string bitstream_reverse) {
std::reverse(bitstream_reverse.begin(), bitstream_reverse.end());
readBinary(bitstream_reverse);
return;
}
static char hexToBin(char ch) {
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
return 0;
}
void TruthTable::readHex(std::string bitstream) {
create(bitstream.size() * 4);
for (const auto &i : irange(nBits() / 4)) {
char value = hexToBin(bitstream[i]);
for (const auto &j : irange(4)) {
set(i * 4 + j, (value >> (3 - j)) & 1);
}
}
return;
}
void TruthTable::readHexReverse(std::string bitstream) {
create(bitstream.size() * 4);
for (const auto &i : irange(nBits() / 4)) {
char value = hexToBin(bitstream[i]);
for (const auto &j : irange(4)) {
set(nBits() - (i * 4 + j) - 1, (value << j) & 0x8);
}
}
return;
}
void TruthTable::resetPosition(void) {
if (empty())
return;
auto &place_to_variable = _data->place_to_variable;
auto &variable_to_place = _data->variable_to_place;
place_to_variable.resize(nVars());
variable_to_place.resize(nVars());
for (const auto &i : irange(nVars())) {
place_to_variable[i] = variable_to_place[i] = i;
}
return;
}
void TruthTable::moveTo(uint32_t variable, uint8_t place, bool adjecent) {
if (empty())
return;
auto &place_to_variable = _data->place_to_variable;
auto &variable_to_place = _data->variable_to_place;
if (!adjecent) {
swapVariable(variable_to_place[variable], place);
}
TruthTable temp(nBits());
uint64_t *pData = this->data();
uint64_t *pOut = temp.data();
uint64_t *pTemp = nullptr;
uint32_t iPlace0, iPlace1, Count = 0;
while (variable_to_place[variable] < place) {
iPlace0 = variable_to_place[variable];
iPlace1 = variable_to_place[variable] + 1;
this->swapAdjacent(pOut, pData, iPlace0);
pTemp = pData;
pData = pOut, pOut = pTemp;
variable_to_place[place_to_variable[iPlace0]]++;
variable_to_place[place_to_variable[iPlace1]]--;
std::swap(place_to_variable[iPlace0], place_to_variable[iPlace1]);
Count++;
}
while (variable_to_place[variable] > place) {
iPlace0 = variable_to_place[variable] - 1;
iPlace1 = variable_to_place[variable];
this->swapAdjacent(pOut, pData, iPlace0);
pTemp = pData;
pData = pOut, pOut = pTemp;
variable_to_place[place_to_variable[iPlace0]]++;
variable_to_place[place_to_variable[iPlace1]]--;
std::swap(place_to_variable[iPlace0], place_to_variable[iPlace1]);
Count++;
}
uint64_t *data = this->data();
if (Count & 1) {
for (const auto &i : irange(nWords())) {
data[i] = pData[i];
}
}
return;
}
void TruthTable::swapVariable(int variable1, int variable2) {
if (empty())
return;
int nWord = nWords();
if (variable1 == variable2) {
return;
}
if (variable2 < variable1) {
std::swap(variable1, variable2);
}
auto &place_to_variable = _data->place_to_variable;
auto &variable_to_place = _data->variable_to_place;
uint64_t *data = this->data();
if (variable1 < 6 && variable2 < 6) {
int nShift = (1 << variable2) - (1 << variable1);
for (const auto &w : irange(nWord)) {
uint64_t low2High = (data[w] & swap_mask[variable1][variable2 - 1]) << nShift;
data[w] &= ~swap_mask[variable1][variable2 - 1];
uint64_t high2Low = (data[w] & (swap_mask[variable1][variable2 - 1] << nShift)) >> nShift;
data[w] &= ~(swap_mask[variable1][variable2 - 1] << nShift);
data[w] = data[w] | low2High | high2Low;
}
} else if (variable1 < 6 && variable2 >= 6) {
int nStep = neededWords(variable2 + 1) / 2;
int nShift = 1 << variable1;
for (int w = 0; w < nWord; w += 2 * nStep) {
for (int j = 0; j < nStep; j++) {
uint64_t low2High = (data[w + j] & (swap_mask[variable1][5] >> nShift)) << nShift;
data[w + j] &= ~(swap_mask[variable1][5] >> nShift);
uint64_t high2Low = (data[w + nStep + j] & (swap_mask[variable1][5])) >> nShift;
data[w + nStep + j] &= ~(swap_mask[variable1][5]);
data[w + j] |= high2Low;
data[w + nStep + j] |= low2High;
}
}
} else {
int nStep1 = neededWords(variable1 + 1) / 2;
int nStep2 = neededWords(variable2 + 1) / 2;
for (int w = 0; w < nWord; w += 2 * nStep2) {
for (int i = 0; i < nStep2; i += 2 * nStep1) {
for (int j = 0; j < nStep1; j++) {
std::swap(data[w + nStep1 + i + j], data[w + nStep2 + i + j]);
}
}
}
}
variable_to_place[place_to_variable[variable1]] = variable2;
variable_to_place[place_to_variable[variable2]] = variable1;
std::swap(place_to_variable[variable1], place_to_variable[variable2]);
return;
}
void TruthTable::extend(int nExtend, std::vector<int> variable_order) {
if (empty())
return;
TruthTable temp(this->nBits() * (((uint64_t)1) << nExtend));
int vars = this->nVars();
int bits = this->nBits();
uint64_t *data = this->data();
uint64_t *temp_data = temp.data();
if (vars < 6) {
for (const auto &i : irange(std::min((((uint64_t)1) << nExtend), (uint64_t)64))) {
temp_data[0] |= (data[0] & extend_mask[vars - 1]) >> (i * bits);
}
if (temp.nWords() > 1) {
for (const auto &i : irange(1, temp.nWords())) {
temp_data[i] = temp_data[0];
}
}
} else {
int nSize = nWords();
for (const auto &i : irange((((uint64_t)1) << nExtend))) {
for (const auto &j : irange(nSize)) {
temp_data[i * nSize + j] = data[j];
}
}
}
for (const auto &i : irange(variable_order.size())) {
temp.moveTo(i, variable_order[i]);
}
temp.resetPosition();
this->clone_from(temp);
return;
}
int TruthTable::countCofactor(int nBoundSet, std::vector<TruthTable> &cofactors, std::vector<TruthTable> &boundSetFunctions, bool derive) {
if (empty())
return 0;
uint64_t nFreeSet = nVars() - nBoundSet;
uint64_t nMinterm = ((uint64_t)1 << nBoundSet);
if (derive) {
cofactors.clear();
boundSetFunctions.clear();
}
if (nFreeSet == 0) {
if (derive) {
cofactors.emplace_back(*this);
}
return 1;
}
std::vector<uint64_t> iCofactor(128, 0);
uint64_t iShift = 0;
uint64_t i, c, w;
uint64_t nCofactor;
uint64_t *data = this->data();
if (nFreeSet < 6) {
uint64_t sCofactor = ((uint64_t)1 << nFreeSet);
uint64_t mCofactor = (((uint64_t)1) << sCofactor) - 1;
for (nCofactor = i = 0; i < nMinterm; ++i) {
uint64_t cofactor = (data[(iShift + i * sCofactor) / 64] >> ((64 - (iShift + (i + 1) * sCofactor)) % 64)) & mCofactor;
for (c = 0; c < nCofactor; ++c) {
if (cofactor == iCofactor[c]) {
break;
}
}
// record the unique cofactor
if (c == nCofactor) {
iCofactor[nCofactor++] = cofactor;
if (derive) {
boundSetFunctions.emplace_back(TruthTable(nMinterm));
}
}
if (derive) {
// record the bound set function
for (c = 0; c < nCofactor; ++c) {
if (cofactor == iCofactor[c]) {
boundSetFunctions[c].set(i, true);
}
}
}
}
if (derive) {
for (c = 0; c < nCofactor; ++c) {
TruthTable cofactor(((uint64_t)1 << nFreeSet));
cofactor.assign(iCofactor[c]);
cofactors.emplace_back(cofactor);
}
}
} else {
uint32_t nWords = neededWords(nFreeSet);
for (nCofactor = i = 0; i < nMinterm; ++i) {
uint64_t *cofactor_begin = data + (iShift + i * nWords);
for (c = 0; c < nCofactor; ++c) {
uint64_t *cofactor_compare = data + (iShift + iCofactor[c] * nWords);
for (w = 0; w < nWords; ++w) {
if (cofactor_begin[w] != cofactor_compare[w]) {
break;
}
}
if (w == nWords) {
break;
}
}
// record the index of the unique cofactor
if (c == nCofactor) {
iCofactor[nCofactor++] = i;
if (derive) {
boundSetFunctions.emplace_back(TruthTable(nMinterm));
}
}
// record the bound set function
if (derive) {
for (c = 0; c < nCofactor; ++c) {
uint64_t *cofactor_compare = data + (iShift + iCofactor[c] * nWords);
for (w = 0; w < nWords; ++w) {
if (cofactor_begin[w] != cofactor_compare[w]) {
break;
}
}
if (w == nWords) {
boundSetFunctions[c].set(i, true);
}
}
}
}
if (derive) {
for (c = 0; c < nCofactor; ++c) {
TruthTable cofactor(((uint64_t)1 << nFreeSet));
for (w = 0; w < nWords; ++w) {
cofactor.assign(w, data[iShift + iCofactor[c] * nWords + w]);
}
cofactors.emplace_back(cofactor);
}
}
}
return nCofactor;
}
TruthTable TruthTable::cofactor(uint32_t variable, bool negative) {
if (empty())
return TruthTable();
TruthTable rtt(nBits());
if (variable >= nVars()) {
fprintf(stderr, "[Error] TruthTable::cofactor: variable (%d) must be less than %d\n", variable, nVars());
exit(-1);
}
uint64_t *pdata = this->data();
uint64_t *rdata = rtt.data();
if (variable < 6) {
for (const auto &i : irange(nWords())) {
uint64_t mask = 0;
uint64_t data = pdata[i];
if (negative) {
mask = (data & elementary_mask[variable]) | ((data & elementary_mask[variable]) >> (1 << variable));
} else {
mask = (data & ~elementary_mask[variable]) | ((data & ~elementary_mask[variable]) << (1 << variable));
}
rdata[i] = mask;
}
} else {
int nSize = nWords();
int nMove = 1 << (variable - 6);
int nLength = 1 << (variable - 5);
int nSegment = nSize / nLength;
if (negative) {
for (const auto &s : irange(nSegment)) {
int offset = s * nLength;
for (const auto &i : irange(nMove)) {
rdata[offset + i] = rdata[offset + i + nMove] = pdata[offset + i];
}
}
} else {
for (const auto &s : irange(nSegment)) {
int offset = s * nLength;
for (const auto &i : irange(nMove)) {
rdata[offset + i] = rdata[offset + i + nMove] = pdata[offset + i + nMove];
}
}
}
}
return rtt;
}
uint64_t TruthTable::countOne(void) {
if (empty())
return 0;
uint64_t *data = this->data();
uint64_t count = 0;
for (const auto &i : irange(nWords())) {
count += __builtin_popcount(data[i]);
}
return count;
}
bool TruthTable::hasVariable(uint32_t variable) {
if (empty())
return false;
int nSize = nWords();
uint64_t *data = this->data();
if (variable < 6) {
int nShift = 1 << variable;
for (const auto &i : irange(nSize)) {
if ((data[i] & ~elementary_mask[variable]) != ((data[i] & elementary_mask[variable]) >> nShift))
return true;
}
} else {
int nStep = (1 << (variable - 6));
for (int k = 0; k < nSize; k += 2 * nStep) {
for (const auto &i : irange(nStep)) {
if (data[i] != data[i + nStep])
return true;
}
data += 2 * nStep;
}
}
return false;
}
int TruthTable::supportSize(void) {
if (empty())
return 0;
int nSupport = 0;
for (const auto &i : irange(nVars())) {
nSupport += hasVariable(i);
}
return nSupport;
}
int TruthTable::isAndorOr(void) {
if (empty())
return 0;
int supportSize = this->supportSize();
int onSet = this->countOne();
int offSet = this->nBits() - onSet;
int condition = ((uint64_t)1 << (nVars() - supportSize));
if (onSet == condition || offSet == condition) {
return supportSize - 1;
}
return 0;
}
void TruthTable::variablePartition(int nMSB, std::vector<int> &MSB, std::vector<int> &LSB) {
if (empty())
return;
MSB.clear();
LSB.clear();
for (const auto &i : irange(nVars())) {
if (i < nVars() - nMSB) {
LSB.push_back(getVariable(i));
} else {
MSB.push_back(getVariable(i));
}
}
return;
}
uint32_t TruthTable::getVariable(int place) {
if (empty())
return 0;
return _data->place_to_variable[place];
}
void TruthTable::swapAdjacent(uint64_t *rdata, uint64_t *data, int variable) {
int nSize = nWords();
if (variable < 5) {
int shift = 1 << variable;
for (const auto &i : irange(nSize)) {
rdata[i] = (data[i] & permutation_mask[variable][0]) | ((data[i] & permutation_mask[variable][1]) << shift) | ((data[i] & permutation_mask[variable][2]) >> shift);
}
} else if (variable == 5) {
for (const auto &i : irange(nSize / 2)) {
rdata[i * 2 + 0] = (data[i * 2 + 0] & 0x00000000FFFFFFFF) | ((data[i * 2 + 1] & 0x00000000FFFFFFFF) << 32);
rdata[i * 2 + 1] = (data[i * 2 + 1] & 0xFFFFFFFF00000000) | ((data[i * 2 + 0] & 0xFFFFFFFF00000000) >> 32);
}
} else {
int nMove = (1 << (variable - 6));
for (int i = 0; i < nSize; i += 4 * nMove) {
for (const auto &j : irange(nMove)) {
rdata[j + nMove * 0] = data[j + nMove * 0];
rdata[j + nMove * 1] = data[j + nMove * 2];
rdata[j + nMove * 2] = data[j + nMove * 1];
rdata[j + nMove * 3] = data[j + nMove * 3];
}
data += 4 * nMove;
rdata += 4 * nMove;
}
}
}
template< typename T >
std::string int_to_hex( T i )
{
std::stringstream stream;
stream << "0x"
<< std::setfill ('0') << std::setw(sizeof(T)*2)
<< std::hex << i;
return stream.str();
}
std::string TruthTable::hash_str(void) {
if (empty()) {
return "";
}
std::string hash_str = "";
uint64_t *data = this->data();
for (const auto &i : irange(nWords())) {
hash_str += std::to_string(data[i]);
// hash_str += int_to_hex(data[i]);
}
return hash_str;
}
void TruthTable::assign(uint64_t value) {
if (empty())
return;
_data->data[0] = (_bits < 64) ? value << (64 - _bits) : value;
return;
}
void TruthTable::assign(int offset, uint64_t value) {
if (empty())
return;
_data->data[offset] = value;
return;
}
void TruthTable::print(Format format) {
if (empty()) {
printf("Empty TruthTable\n");
return;
}
if (format == Format::BIN) {
printf("0b");
} else if (format == Format::HEX) {
printf("0x");
}
printf("%s\n", str(format).c_str());
return;
}
void TruthTable::printReverse(Format format) {
if (empty()) {
printf("Empty TruthTable\n");
return;
}
if (format == Format::BIN) {
printf("0b");
} else if (format == Format::HEX) {
printf("0x");
}
printf("%s\n", strReverse(format).c_str());
return;
}
void TruthTable::printPosition(void) {
if (empty()) {
printf("Empty TruthTable\n");
return;
}
for (const auto &i : irange(nVars())) {
printf("Place: %d -> Var[%ld]\n", i, _data->place_to_variable[i]);
}
}
std::string TruthTable::str(Format format) {
std::string str = "";
if (format == Format::BIN) {
for (const auto &i : irange(nBits())) {
str += (*this)[i] ? "1" : "0";
}
} else if (format == Format::HEX) {
for (const auto &i : irange(nBits() / 4)) {
char value = 0;
for (const auto &j : irange(4)) {
value |= (*this)[i * 4 + j] << (3 - j);
}
char buf[2];
sprintf(buf, "%X", value);
str += buf[0];
}
}
return str;
}
std::string TruthTable::strReverse(Format format) {
std::string str;
int bits = nBits();
if (format == Format::BIN) {
for (const auto &i : irange(bits)) {
str += (*this)[bits - i - 1] ? "1" : "0";
}
} else if (format == Format::HEX) {
for (const auto &i : irange(bits / 4)) {
char value = 0;
for (const auto &j : irange(4)) {
value |= (*this)[bits - (i * 4 + j) - 1] << (3 - j);
}
char buf[2];
sprintf(buf, "%X", value);
str += buf[0];
}
}
return str;
}
std::vector<int64_t> TruthTable::fourierTransform_rec(uint64_t begin, uint64_t end) {
if (begin == end) {
return {operator[](begin) ? 1 : -1};
}
int length = end - begin + 1;
auto mid = begin + length / 2;
auto left = fourierTransform_rec(begin, mid - 1);
auto right = fourierTransform_rec(mid, end);
std::vector<int64_t> result(length);
for (const auto &i : irange(length / 2)) {
result[i] = left[i] + right[i];
result[i + length / 2] = left[i] - right[i];
}
return result;
}
std::vector<float> TruthTable::fourierTransform(void) {
int bits = nBits();
auto result = fourierTransform_rec(0, bits - 1);
std::vector<float> result_norm(bits);
for (const auto &i : irange(bits)) {
result_norm[i] = (float)result[i] / bits;
}
return result_norm;
}
std::vector<float> TruthTable::fourierWeight(void) {
uint64_t bits = nBits();
auto coeffs = this->fourierTransform();
std::vector<float> weights(nVars() + 1);
weights[0] = std::pow(coeffs[0], 2);
std::vector<int> index(1, 1);
for (uint64_t i = 1; i < bits;) {
for (const auto &j : irange(index.size())) {
weights[index[j]] += std::pow(coeffs[i++], 2);
}
for (const auto &j : irange(index.size())) {
index.push_back(index[j] + 1);
}
}
return weights;
}
float TruthTable::fourierCost(void) {
uint64_t bits = nBits();
auto coeffs = this->fourierTransform();
std::vector<float> weights(nVars() + 1);
weights[0] = std::pow(coeffs[0], 2);
std::vector<int> index(1, 1);
for (uint64_t i = 1; i < bits;) {
for (const auto &j : irange(index.size())) {
weights[index[j]] += std::pow(coeffs[i++], 2);
}
for (const auto &j : irange(index.size())) {
index.push_back(index[j] + 1);
}
}
int sparsity_of_coeffs = 0;
for (const auto &i : irange(nBits())) {
sparsity_of_coeffs += coeffs[i] != 0;
}
int sparsity_of_weights = 0;
for (const auto &i : irange(nVars() + 1)) {
sparsity_of_weights += weights[i] != 0;
}
// ::print(weights);
float cost = 0;
for (const auto &i : irange(nVars() + 1)) {
// cost += weights[i] * ((i + 1) * 3);
cost += weights[i] * ((i) * 3);
// cost += weights[i] * ((i));
}
cost += (float)sparsity_of_coeffs / sparsity_of_weights;
return cost;
}
uint64_t reverseBits(uint64_t n) {
uint64_t rev = 0;
for (int i = 0; i < 64; i++)
rev = (rev << (uint64_t)1) | ((n >> i) & (uint64_t)1);
return rev;
}
float TruthTable::cost(void) {
if (nVars() == 4) {
// return func2node[reverseBits(data()[0])];
}
return fourierCost();
}
class Combination {
public:
Combination(int n, int r);
bool next(void);
void next(int n);
std::vector<int>& get(void);
std::vector<int> get_reverse(void);
std::vector<int> get_reverse(int n);
int count(void);
int getN(void);
int getR(void);
private:
int n;
int r;
std::vector<int> data;
};
inline Combination::Combination(int n, int r)
: n(n), r(r), data(r) {
for (const auto& i : irange(r)) {
data[i] = i;
}
}
inline std::vector<int>& Combination::get(void) {
return data;
}
inline int Combination::getN(void) {
return n;
}
inline int Combination::getR(void) {
return r;
}
bool Combination::next(void) {
for (int i = r - 1; i >= 0; i--) {
if (data[i] < n - r + i) {
data[i]++;
for (int j = i + 1; j < r; j++) {
data[j] = data[j - 1] + 1;
}
return true;
}
}
return false;
}
void Combination::next(int n) {
for (const auto& i : irange(n)) {
(void)i;
next();
}
}
int Combination::count(void) {
if (r > n - r) r = n - r;
int ans = 1;
for (const auto& i : irange(1, r + 1)) {
ans *= n - r + i;
ans /= i;
}
return ans;
}
std::vector<int> Combination::get_reverse(void) {
std::vector<int> reverse_data = data;
for (const auto& i : irange(r)) {
reverse_data[i] = n - 1 - data[i];
}
return reverse_data;
}
std::vector<int> Combination::get_reverse(int n) {
next(n);
return get_reverse();
}
void doDecomposition(TruthTable tt_, int nBoundSet, std::vector<int>& supportSet, std::vector<int>& record, std::vector<std::set<std::string> >& recordCof) {
assert(nBoundSet <= supportSet.size());
TruthTable tt = tt_.clone();
Combination generator(supportSet.size(), nBoundSet);
int genIndex = 0;
do {
std::vector<int> combination = generator.get_reverse();
// move bound set to MSB
for (const auto& i : irange(nBoundSet)) {
tt.moveTo(supportSet[combination[i]], tt.nVars() - i - 1);
}
std::vector<TruthTable> cofactors;
std::vector<TruthTable> boundSetFunctions;
int nCofactors = tt.countCofactor(nBoundSet, cofactors, boundSetFunctions, true);
for (auto& cofactor : cofactors) {
recordCof[genIndex].insert(cofactor.hash_str());
recordCof[genIndex].insert((~cofactor).hash_str());
}
record[genIndex++] += nCofactors;
} while (generator.next());
}
class DecisionDiagram;
class DecisionNode {
friend class DecisionDiagram;
public:
DecisionNode();
void buildWithDecisionVariable(DecisionDiagram* mgr, int decisionVariable, bool shared = true, int fInv = 0);
public:
TruthTable& getTruthTable(void);
private:
static std::vector<float> computeEntropyCost(DecisionDiagram* mgr, TruthTable& tt, std::set<int>& invalid_variables);
std::set<int> getInvalidVariables(DecisionDiagram* mgr, std::vector<int>& available_variables);
private:
int decisionVariable;
TruthTable tt;
int left;
int right;
};
inline DecisionNode::DecisionNode()
: decisionVariable(-1), left(-1), right(-1) {
}
inline TruthTable& DecisionNode::getTruthTable(void) {
return tt;
}
class DecisionDiagram {
friend class DecisionNode;
public:
DecisionDiagram();
public:
unsigned randomInt(int fReset);
unsigned randomNum(int nSeed);
void addOutput(TruthTable& tt);
void build(void);
void buildWithDecisionVariable(int node_id, int variable, std::set<int>& newWaitingNodes, int fInv = 0);
void buildOneCluster(std::vector<int>& cluster);
std::vector<std::vector<int> > buildClusters(std::vector<int> waitingNodes);
std::vector<int> computeSupportSet(std::vector<int>& nodes);
std::vector<int> buildWithTheseSupportWithEntropyAndFourier(std::vector<int>& waitingNodes, std::set<int>& supportSet);
private:
void init(int nVars);
DecisionNode& getNode(int id) {
return node_pool[id];
}
void prepareSpace(void);
int newNode(void);
int newNode(TruthTable& tt, bool shared = true, int fInv = 0);
int const0Node(void);
int const1Node(void);
float getEntropyCost(TruthTable& tt);
float getFourierCost(TruthTable& tt);
public:
void writeVerilog(const char* filename);
void buildAig(Gia_Man_t *pAig);
private:
void topoRec(int node, std::set<int>& visited, std::vector<int>& nodes);
void topoSort(int root, std::vector<int>& nodes);
private:
void printRec(int node, int space);
public:
void print(void);
int nVars(void);
int nDecomp(void);
int nNodeAllocated(void) { return _num_node_allocated; }
private:
int _nVars;
int _nDecomp;
int const0;
int _num_node_allocated;
std::vector<DecisionNode> node_pool;
std::vector<int> roots;
std::unordered_map<std::string, int> hash2node;
std::unordered_map<std::string, float> hash2cost;
std::vector<int> sortedNodes;
};
inline DecisionDiagram::DecisionDiagram()
: _nVars(0), _nDecomp(0), const0(0), _num_node_allocated(0) {
randomNum(1);
}
inline int DecisionDiagram::nVars(void) {
return _nVars;
}
inline int DecisionDiagram::nDecomp(void) {
return _nDecomp;
}
inline int DecisionDiagram::const0Node(void) {
return const0;
}
inline int DecisionDiagram::const1Node(void) {
return LitNot(const0);
}
void DecisionDiagram::init(int nVars) {
_nVars = nVars;
const0 = newNode();
getNode(LitToId(const0)).decisionVariable = 0;
getNode(LitToId(const0)).right = -1;
getNode(LitToId(const0)).left = -1;
getNode(LitToId(const0)).tt = TruthTable(((uint64_t)1 << nVars));
hash2node[getNode(LitToId(const0)).getTruthTable().hash_str()] = const0;
hash2node[(~(getNode(LitToId(const0)).getTruthTable())).hash_str()] = LitNot(const0);
}
void DecisionDiagram::prepareSpace(void) {
if (_num_node_allocated + 2 > node_pool.size() / 2) {
// printf("\n\nPrepare node pool\n\n");
node_pool.resize(node_pool.size() * 2);
}
}
int DecisionDiagram::newNode(void) {
if (_num_node_allocated > node_pool.size() / 2) {
// printf("\n\nResizing node pool\n\n");
node_pool.resize(node_pool.size() * 2);
}
// printf("\n\nNEW NODE Allocating node %d\n\n", _num_node_allocated);
return IdToLit(_num_node_allocated++, false);
}
int DecisionDiagram::newNode(TruthTable& tt, bool shared, int fInv) {
(void)shared;
std::string hash_pos = tt.hash_str();
auto iter = hash2node.begin();
if ((iter = hash2node.find(hash_pos)) != hash2node.end()) {
return iter->second;
}
int new_node = newNode();
// printf("New node %d for TT ", LitToId(new_node));
// tt.print(TruthTable::Format::BIN);
if (fInv && tt.countOne() > tt.nBits() / 2) {
hash2node[hash_pos] = LitNot(new_node);
hash2node[(~tt).hash_str()] = new_node;
getNode(LitToId(new_node)).tt = ~tt;
return LitNot(new_node);
} else {
hash2node[hash_pos] = new_node;
hash2node[(~tt).hash_str()] = LitNot(new_node);
getNode(LitToId(new_node)).tt = tt;
return new_node;
}
return const0;
}
void DecisionDiagram::addOutput(TruthTable& tt) {
if (node_pool.empty()) {
node_pool.resize((uint64_t)1 << tt.nVars());
this->init(tt.nVars());
}
roots.emplace_back(newNode(tt));
}
unsigned DecisionDiagram::randomInt(int fReset) {
static unsigned int m_z = 3716960521u;
static unsigned int m_w = 2174103536u;
if (fReset) {
m_z = 3716960521u;
m_w = 2174103536u;
}
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
return (m_z << 16) + m_w;
}
unsigned DecisionDiagram::randomNum(int Seed) {
static unsigned RandMask = 0;
if (Seed == 0)
return RandMask ^ randomInt(0);
RandMask = randomInt(1);
for (int i = 0; i < Seed; i++)
RandMask = randomInt(0);
return RandMask;
}
float DecisionDiagram::getFourierCost(TruthTable& tt) {
std::string hash_str = tt.hash_str();
std::string neg_hash_str = (~tt).hash_str();
if (hash2node.find(hash_str) != hash2node.end()) {
return 0;
} else if (hash2node.find(neg_hash_str) != hash2node.end()) {
return 0;
}
auto iter = hash2cost.begin();
if ((iter = hash2cost.find(hash_str)) != hash2cost.end()) {
return iter->second;
} else if ((iter = hash2cost.find(neg_hash_str)) != hash2cost.end()) {
return iter->second;
}
return hash2cost[hash_str] = tt.fourierCost();
}
float DecisionDiagram::getEntropyCost(TruthTable& tt) {
int out1 = tt.countOne();
float p1 = (float)out1 / tt.nBits();
float p0 = 1 - p1;
float entropy = -(((p0 != 0) ? p0 * log2(p0) : 0) + ((p1 != 0) ? p1 * log2(p1) : 0));
return entropy;
}
std::set<int> getInvalidVariables(DecisionDiagram* mgr, std::vector<int>& available_variables) {
std::vector<int> all_variable(mgr->nVars());
std::iota(all_variable.begin(), all_variable.end(), 0);
std::set<int> all_variable_set(all_variable.begin(), all_variable.end());
std::set<int> invalid_variables;
std::set_difference(all_variable_set.begin(), all_variable_set.end(), available_variables.begin(), available_variables.end(), std::inserter(invalid_variables, invalid_variables.begin()));
return invalid_variables;
}
std::vector<float> DecisionNode::computeEntropyCost(DecisionDiagram* mgr, TruthTable& tt, std::set<int>& invalid_variables) {
(void)mgr;
float oriInfo = mgr->getEntropyCost(tt);
std::vector<float> costs(tt.nVars(), 1024);
for (const auto& i : irange(tt.nVars())) {
if (invalid_variables.count(i)) continue;
auto cofactor0 = tt.cofactor(i, 1);
auto cofactor1 = tt.cofactor(i, 0);
if (cofactor0 == tt || cofactor1 == tt) {
continue;
}
int out1_0 = cofactor0.countOne();
int out0_0 = cofactor0.nBits() - out1_0;
int out1_1 = cofactor1.countOne();
int out0_1 = cofactor1.nBits() - out1_1;
float p1_0 = (float)out1_0 / (out1_0 + out0_0 + 1e-8);
float p0_0 = (float)out0_0 / (out1_0 + out0_0 + 1e-8);
float p1_1 = (float)out1_1 / (out1_1 + out0_1 + 1e-8);
float p0_1 = (float)out0_1 / (out1_1 + out0_1 + 1e-8);
int n0 = out1_0 + out0_0;
int n1 = out1_1 + out0_1;
int nAll = n0 + n1;
float info0 = ((float)n0 / nAll) * -(((p0_0 != 0) ? p0_0 * log2(p0_0) : 0) + ((p1_0 != 0) ? p1_0 * log2(p1_0) : 0));
float info1 = ((float)n1 / nAll) * -(((p0_1 != 0) ? p0_1 * log2(p0_1) : 0) + ((p1_1 != 0) ? p1_1 * log2(p1_1) : 0));
// printf("Decision variable: %d\n", i);
// printf("Ori: %f, Info0: %f, Info1: %f\n", oriInfo, info0, info1);
// printf("p0_0: %f, p1_0: %f, p0_1: %f, p1_1: %f\n", p0_0, p1_0, p0_1, p1_1);
costs[i] = -(oriInfo - info0 - info1);
}
return costs;
}
double jaccardSimilarity(const std::unordered_set<int>& a, const std::unordered_set<int>& b) {
std::unordered_set<int> intersection, unionSet = a;
for (int num : b) {
if (a.count(num)) intersection.insert(num);
unionSet.insert(num);
}
return (double)intersection.size() / unionSet.size();
}
std::vector<std::vector<int> > autoClusterIndex(std::vector<std::vector<int> >& sequences, double threshold = 0.95) {
int n = sequences.size();
std::vector<std::unordered_set<int> > sets(n);
for (int i = 0; i < n; i++) {
sets[i] = std::unordered_set<int>(sequences[i].begin(), sequences[i].end());
}
std::vector<std::vector<int> > graph(n);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (jaccardSimilarity(sets[i], sets[j]) >= threshold) {
graph[i].push_back(j);
graph[j].push_back(i);
}
}
}
std::vector<bool> visited(n, false);
std::vector<std::vector<int> > clusters;
std::function<void(int, std::vector<int>&)> dfs = [&](int node, std::vector<int>& cluster) {
visited[node] = true;
cluster.push_back(node);
for (int neighbor : graph[node]) {
if (!visited[neighbor]) {
dfs(neighbor, cluster);
}
}
};
for (int i = 0; i < n; i++) {
if (!visited[i]) {
std::vector<int> cluster;
dfs(i, cluster);
clusters.push_back(cluster);
}
}
return clusters;
}
std::vector<std::vector<int> > DecisionDiagram::buildClusters(std::vector<int> waitingNodes) {
int nVars = getNode(waitingNodes[0]).getTruthTable().nVars();
// compute support set
std::set<int> supportSet;
std::vector<int> supportCount(nVars, 0);
std::vector<std::vector<int> > supportSetVec(waitingNodes.size());
for (const auto& i : irange(waitingNodes.size())) {
auto& node = getNode(waitingNodes[i]);
for (const auto& var : irange(nVars)) {
if (node.getTruthTable().hasVariable(var)) {
supportSet.insert(var);
supportCount[var]++;
supportSetVec[i].push_back(var);
}
}
}
auto clustersIndex = autoClusterIndex(supportSetVec);
std::vector<std::vector<int> > clusters;
for (const auto& cluster : clustersIndex) {
std::vector<int> newCluster;
for (const auto& index : cluster) {
if (waitingNodes[index] == LitToId(const0Node())) { // FIX
continue;
}
newCluster.push_back(waitingNodes[index]);
}
if (newCluster.empty()) { // FIX
continue;
}
clusters.push_back(newCluster);
}
return clusters;
}
void DecisionNode::buildWithDecisionVariable(DecisionDiagram* mgr, int decisionVariable, bool shared, int fInv) {
auto& tt = this->tt;
// terminate condition
if (this->left != -1 || this->right != -1) {
return;
}
// set the decision variable and calculate the cofactor
// printf(" with decision variable %d\n", decisionVariable);
this->decisionVariable = decisionVariable;
auto ltt = tt.cofactor(decisionVariable, 0);
auto rtt = tt.cofactor(decisionVariable, 1);
// printf(" left: "); ltt.print(TruthTable::Format::BIN);
// printf(" right: "); rtt.print(TruthTable::Format::BIN);
if (tt == rtt || tt == ltt) {
return;
}
// build the left and right child
left = mgr->newNode(ltt, shared, fInv);
right = mgr->newNode(rtt, shared, fInv);
// printf(" left id: %d, right id: %d\n", LitToId(left), LitToId(right));
// printf(" left check: "); if (LitToId(left) != 0) { mgr->getNode(LitToId(left)).getTruthTable().print(TruthTable::Format::BIN); } else { printf("NULL\n"); }
// printf(" right check: "); if (LitToId(right) != 0) { mgr->getNode(LitToId(right)).getTruthTable().print(TruthTable::Format::BIN); } else { printf("NULL\n"); }
}
void DecisionDiagram::buildWithDecisionVariable(int node_id, int variable, std::set<int>& newWaitingNodes, int fInv) {
prepareSpace();
auto& node = getNode(node_id);
if (node.left != -1 || node.right != -1) {
return;
}
// printf("Build node %d: ", node_id); node.getTruthTable().print(TruthTable::Format::BIN);
node.buildWithDecisionVariable(this, variable, true, fInv);
if (node.left == -1 && node.right == -1) {
newWaitingNodes.insert(node_id);
return;
}
if (!fInv) {
if (!LitIsComplement(node.left) && (node.left != const0Node())) {
// printf("Insert left node id %d\n", LitToId(node.left));
newWaitingNodes.insert(LitToId(node.left));
}
if (!LitIsComplement(node.right) && (node.right != const0Node())) {
// printf("Insert right node id %d\n", LitToId(node.right));
newWaitingNodes.insert(LitToId(node.right));
}
} else {
if (node.left != const0Node() && node.left != const1Node()) {
newWaitingNodes.insert(LitToId(node.left));
}
if (node.right != const0Node() && node.right != const1Node()) {
newWaitingNodes.insert(LitToId(node.right));
}
}
// printf(" newWaitingNodes: \n");
// for (const auto& n : newWaitingNodes) {
// printf("%d ", n);
// getNode(n).getTruthTable().print(TruthTable::Format::BIN);
// }
// printf("\n");
}
std::vector<int> DecisionDiagram::computeSupportSet(std::vector<int>& nodes) {
std::set<int> supportSet;
for (const auto& node_id : nodes) {
auto& node = getNode(node_id);
for (const auto& var : irange(node.getTruthTable().nVars())) {
if (node.getTruthTable().hasVariable(var)) {
supportSet.insert(var);
}
}
}
return std::vector<int>(supportSet.begin(), supportSet.end());
}
template<typename T>
float min(std::vector<T>& array) {
return *std::min_element(array.begin(), array.end());
}
void DecisionDiagram::build(void) {
std::vector<int> trueNodeIds;
for (const auto& root : roots) {
int root_id = LitToId(root);
if (std::find(trueNodeIds.begin(), trueNodeIds.end(), root) == trueNodeIds.end())
trueNodeIds.emplace_back(root_id);
}
std::vector<int> waitingNodes = trueNodeIds;
auto clusters = buildClusters(waitingNodes);
for (auto& cluster : clusters) {
// printf("cluster: ");
// for (auto& c : cluster) {
// printf("%d ", c);
// }
// printf("\n");
buildOneCluster(cluster);
}
// topo sort
std::set<int> visited;
for (const auto& root : roots) {
this->topoRec(LitToId(root), visited, this->sortedNodes);
}
}
std::vector<int> DecisionDiagram::buildWithTheseSupportWithEntropyAndFourier(std::vector<int>& waitingNodes, std::set<int>& supportSet) {
int nVars = getNode(waitingNodes[0]).getTruthTable().nVars();
std::set<int> invalid_variables;
for (const auto& i : irange(nVars)) {
if (supportSet.find(i) == supportSet.end()) {
invalid_variables.insert(i);
}
}
while (!waitingNodes.empty() && !supportSet.empty()) {
// compute gain
std::vector<float> costs(nVars, 1024);
for (const auto& nodeId : waitingNodes) {
auto& node = getNode(nodeId);
auto cost = DecisionNode::computeEntropyCost(this, node.getTruthTable(), invalid_variables);
// ::print(cost);
for (const auto& i : irange(nVars)) {
costs[i] += (cost[i] == 1024) ? 0 : cost[i];
}
}
// find decision variable
std::vector<int> decisionSet;
int decisionVariable = std::distance(costs.begin(), std::min_element(costs.begin(), costs.end()));
float decisionCost = costs[decisionVariable];
for (const auto& i : irange(nVars)) {
if (costs[i] == decisionCost && !invalid_variables.count(i)) {
decisionSet.push_back(i);
}
}
if (min(costs) == 1024) {
decisionVariable = *supportSet.begin();
}
if (decisionSet.size() > 1) {
std::vector<float> fouerierCost(decisionSet.size(), 0);
for (const auto& nodeId : waitingNodes) {
auto& node = getNode(nodeId);
std::vector<float> cost(decisionSet.size(), 8192);
for (const auto& i : irange(decisionSet.size())) {
if (invalid_variables.find(decisionSet[i]) != invalid_variables.end()) continue;
// consider two childs
auto cofactor0 = node.getTruthTable().cofactor(decisionSet[i], 1);
auto cofactor1 = node.getTruthTable().cofactor(decisionSet[i], 0);
if (cofactor0 == node.getTruthTable() || cofactor1 == node.getTruthTable()) {
continue;
}
cost[i] = getFourierCost(cofactor0) + getFourierCost(cofactor1);
}
for (const auto& i : irange(decisionSet.size())) {
fouerierCost[i] += cost[i];
}
}
int decisionIndex = std::distance(fouerierCost.begin(), std::min_element(fouerierCost.begin(), fouerierCost.end()));
decisionVariable = decisionSet[decisionIndex];
}
std::set<int> new_waiting_nodes;
for (const auto& nodeId : waitingNodes) {
buildWithDecisionVariable(nodeId, decisionVariable, new_waiting_nodes);
}
waitingNodes = std::vector<int>(new_waiting_nodes.begin(), new_waiting_nodes.end());
invalid_variables.insert(decisionVariable);
supportSet.erase(decisionVariable);
}
return waitingNodes;
}
void DecisionDiagram::buildOneCluster(std::vector<int>& cluster) {
int enableFourier = 1;
int enableDecompose = 1;
std::vector<int> waiting_nodes = cluster;
int nVars = getNode(cluster[0]).getTruthTable().nVars();
while (!waiting_nodes.empty()) {
// printf("=========================\n");
// printf("construct nodes: \n");
// for (auto node : waiting_nodes) {
// printf("Node %d: ", node);
// getNode(node).getTruthTable().print(TruthTable::Format::BIN);
// }
// printf("-------------------------\n");
// compute support set
std::vector<int> supportSet = computeSupportSet(waiting_nodes);
std::set<int> invalid_variables = getInvalidVariables(this, supportSet);
// printf("Support set: ");
// for (const auto& var : supportSet) {
// printf("%d ", var);
// }
// printf("\n");
// printf("Invalid set: ");
// for (const auto& var : invalid_variables) {
// printf("%d ", var);
// }
// printf("\n");
// compute multi-output decomposition
// if the multi-output decomposition exist, then build the nodes
int nBoundSet = std::max(2, std::min(4, (int)supportSet.size()));
if (supportSet.size() > nBoundSet && enableDecompose) {
// printf("Try to decompose with bound set size %d\n", nBoundSet);
std::vector<int> record(Combination(supportSet.size(), nBoundSet).count(), 0);
std::vector<std::set<std::string> > recordCofoactor(Combination(supportSet.size(), nBoundSet).count());
for (const auto& nodeIndex : waiting_nodes) {
auto& node = getNode(nodeIndex);
doDecomposition(node.getTruthTable(), nBoundSet, supportSet, record, recordCofoactor);
}
auto record_copy = record;
for (const auto& i : irange(recordCofoactor.size())) {
record[i] = recordCofoactor[i].size();
}
int minRecord = *std::min_element(record.begin(), record.end());
if (minRecord < (1 << nBoundSet) * 3 / 4) {
std::vector<int> minRecordIndex;
std::vector<int> ref;
for (const auto& i : irange(record.size())) {
if (record[i] == minRecord) {
minRecordIndex.push_back(i);
ref.push_back(record_copy[i]);
}
}
std::set<int> unionSet;
for (const auto& index : minRecordIndex) {
auto comb = Combination(supportSet.size(), nBoundSet).get_reverse(index);
for (const auto& i : comb) {
unionSet.insert(supportSet[i]);
}
}
if (unionSet.size() == nBoundSet && min(ref) > nBoundSet) {
waiting_nodes = buildWithTheseSupportWithEntropyAndFourier(waiting_nodes, unionSet);
continue;
}
supportSet = std::vector<int>(unionSet.begin(), unionSet.end());
}
}
int decisionVariable = 0;
// compute gain
std::vector<float> costs(nVars, 1024);
for (const auto& nodeId : waiting_nodes) {
auto& node = getNode(nodeId);
auto cost = DecisionNode::computeEntropyCost(this, node.getTruthTable(), invalid_variables);
for (const auto& i : irange(nVars)) {
costs[i] += (cost[i] == 1024) ? 0 : cost[i];
}
}
// printf("Costs: ");
// for (const auto& cost : costs) {
// printf("%f ", cost);
// }
// printf("\n");
// find decision variable
std::vector<int> decisionSet;
decisionVariable = std::distance(costs.begin(), std::min_element(costs.begin(), costs.end()));
float decisionCost = costs[decisionVariable];
for (const auto& i : irange(nVars)) {
if (costs[i] == decisionCost && !invalid_variables.count(i)) {
decisionSet.push_back(i);
}
}
if (min(costs) == 1024) {
decisionVariable = supportSet[0];
}
// printf("First decision variable: %d\n", decisionVariable);
if (decisionSet.size() > 1 && enableFourier) {
std::vector<float> fouerierCost(decisionSet.size(), 0);
for (const auto& nodeId : waiting_nodes) {
auto& node = getNode(nodeId);
std::vector<float> cost(decisionSet.size(), 8192);
for (const auto& i : irange(decisionSet.size())) {
if (invalid_variables.find(decisionSet[i]) != invalid_variables.end()) continue;
// consider two childs
auto cofactor0 = node.getTruthTable().cofactor(decisionSet[i], 1);
auto cofactor1 = node.getTruthTable().cofactor(decisionSet[i], 0);
if (cofactor0 == node.getTruthTable() || cofactor1 == node.getTruthTable()) {
continue;
}
cost[i] = getFourierCost(cofactor0) + getFourierCost(cofactor1);
}
for (const auto& i : irange(decisionSet.size())) {
fouerierCost[i] += cost[i];
}
}
int decisionIndex = std::distance(fouerierCost.begin(), std::min_element(fouerierCost.begin(), fouerierCost.end()));
decisionVariable = decisionSet[decisionIndex];
// printf("Second decision variable: %d\n", decisionVariable);
}
std::set<int> next_waiting_nodes;
for (const auto& nodeId : waiting_nodes) {
buildWithDecisionVariable(nodeId, decisionVariable, next_waiting_nodes, 0);
// printf("Check every nodes:\n");
// for (int i = 0; i < _num_node_allocated; i++) {
// printf("Node id %d: left %d right %d\n", i, getNode(i).left, getNode(i).right);
// getNode(i).getTruthTable().print(TruthTable::Format::BIN);
// }
// printf("\n");
}
// for (auto next_node : next_waiting_nodes) {
// printf("Next waiting node: ");
// getNode(next_node).getTruthTable().print(TruthTable::Format::BIN);
// }
waiting_nodes = std::vector<int>(next_waiting_nodes.begin(), next_waiting_nodes.end());
// printf("=========================\n\n");
}
}
void DecisionDiagram::topoRec(int node, std::set<int>& visited, std::vector<int>& nodes) {
if (visited.find(node) != visited.end() || node == const0) {
return;
}
visited.insert(node);
if (getNode(node).left != -1) {
topoRec(LitToId(getNode(node).left), visited, nodes);
}
if (getNode(node).right != -1) {
topoRec(LitToId(getNode(node).right), visited, nodes);
}
nodes.push_back(node);
}
void DecisionDiagram::topoSort(int root, std::vector<int>& nodes) {
std::set<int> visited;
topoRec(root, visited, nodes);
}
void DecisionDiagram::printRec(int root_, int space) {
int root = LitToId(root_);
bool is_complement = LitIsComplement(root_);
// Base case
if (root == const0)
return;
// Increase distance between levels
space += 11;
// Process right child first
if (getNode(root).right == -1) return;
printRec(getNode(root).right, space);
// Print current node after space
printf("\n");
for (int i = 11; i < space; i++)
printf(" ");
if (root == const0Node()) {
if (is_complement) {
printf("CONST1\n");
} else {
printf("CONST0\n");
}
} else {
printf("%sMUX[%2d](%2d)\n", (is_complement) ? "~" : "", getNode(root).decisionVariable, root);
}
// Process left child
if (getNode(root).left == root) return;
printRec(getNode(root).left, space);
}
void DecisionDiagram::print(void) {
// Pass initial space count as 0
for (const auto& root : roots)
printRec(root, 0);
}
void DecisionDiagram::writeVerilog(const char* filename) {
if (roots.size() == 0) {
printf("[ERROR] Empty network\n");
return;
}
FILE* f = fopen(filename, "w");
if (f == NULL) {
printf("[ERROR] Cannot open file %s\n", filename);
return;
}
fprintf(f, "module decision_diagram(");
for (const auto& i : irange(getNode(LitToId(roots[0])).tt.nVars())) {
fprintf(f, "in_%u, ", i);
}
for (const auto& i : irange(roots.size() - 1)) {
fprintf(f, "output_%lu, ", i);
}
fprintf(f, "output_%zu);\n", roots.size() - 1);
for (const auto& i : irange(getNode(LitToId(roots[0])).tt.nVars())) {
fprintf(f, "input in_%d;\n", i);
}
for (const auto& i : irange(roots.size())) {
fprintf(f, "output output_%lu;\n", i);
}
fprintf(f, "wire node_%d = 0;\n", const0);
for (const auto& node : sortedNodes) {
fprintf(f, "wire node_%d;\n", node);
}
for (const auto& node : sortedNodes) {
std::string decisionVariable = "in_" + std::to_string(getNode(node).decisionVariable);
if (getNode(node).left == -1 || getNode(node).right == -1) continue;
int lhs = LitToId(getNode(node).left);
bool is_lhs_const = (lhs == const0);
bool is_lhs_complement = LitIsComplement(getNode(node).left);
int rhs = LitToId(getNode(node).right);
bool is_rhs_const = (rhs == const0);
bool is_rhs_complement = LitIsComplement(getNode(node).right);
if (is_lhs_const && !is_lhs_complement && is_rhs_const && !is_rhs_complement) {
fprintf(f, "assign node_%d = 0;\n", node);
} else if (is_lhs_const && is_lhs_complement && is_rhs_const && is_rhs_complement) {
fprintf(f, "assign node_%d = 1;\n", node);
} else if (is_lhs_const && is_lhs_complement && is_rhs_const && !is_rhs_complement) {
fprintf(f, "assign node_%d = %s;\n", node, decisionVariable.c_str());
} else if (is_lhs_const && !is_lhs_complement && is_rhs_const && is_rhs_complement) {
fprintf(f, "assign node_%d = ~%s;\n", node, decisionVariable.c_str());
} else if (!is_lhs_const && is_rhs_const && !is_rhs_complement) {
fprintf(f, "assign node_%d = %s & %snode_%d;\n", node, decisionVariable.c_str(), (is_lhs_complement) ? "~" : "", lhs);
} else if (!is_rhs_const && is_lhs_const && !is_lhs_complement) {
fprintf(f, "assign node_%d = ~%s & %snode_%d;\n", node, decisionVariable.c_str(), (is_rhs_complement) ? "~" : "", rhs);
} else if (is_lhs_const && is_lhs_complement && !is_rhs_const) {
fprintf(f, "assign node_%d = %s | %snode_%d;\n", node, decisionVariable.c_str(), (is_rhs_complement) ? "~" : "", rhs);
} else if (is_rhs_const && is_rhs_complement && !is_lhs_const) {
fprintf(f, "assign node_%d = ~%s | %snode_%d;\n", node, decisionVariable.c_str(), (is_lhs_complement) ? "~" : "", lhs);
} else {
fprintf(f, "assign node_%d = (%s) ? %snode_%d : %snode_%d;\n", node, decisionVariable.c_str(), (is_lhs_complement) ? "~" : "", lhs, (is_rhs_complement) ? "~" : "", rhs);
}
}
for (const auto& i : irange(roots.size())) {
int root = LitToId(roots[i]);
bool is_complement = LitIsComplement(roots[i]);
fprintf(f, "assign output_%lu = %snode_%d;\n", i, (is_complement) ? "~" : "", root);
}
fprintf(f, "endmodule\n");
fclose(f);
}
void DecisionDiagram::buildAig(Gia_Man_t *pAig) {
if (roots.size() == 0) {
printf("[ERROR] Empty network\n");
return;
}
std::vector<int> copy(sortedNodes.size() + 1, -1);
copy[const0] = Gia_ManConst0Lit();
for (const auto& node : sortedNodes) {
// printf("node id %d: left %d right %d\n", node, getNode(node).left, getNode(node).right);
if (getNode(node).left == -1 || getNode(node).right == -1) continue;
int lhs = LitToId(getNode(node).left);
bool is_lhs_const = (lhs == const0);
bool is_lhs_complement = LitIsComplement(getNode(node).left);
int rhs = LitToId(getNode(node).right);
bool is_rhs_const = (rhs == const0);
bool is_rhs_complement = LitIsComplement(getNode(node).right);
// printf("lhs: %s%d rhs: %s%d\n", (is_lhs_complement) ? "~" : "", lhs, (is_rhs_complement) ? "~" : "", rhs);
if (is_lhs_const && !is_lhs_complement && is_rhs_const && !is_rhs_complement) {
copy[node] = Gia_ManConst0Lit();
} else if (is_lhs_const && is_lhs_complement && is_rhs_const && is_rhs_complement) {
copy[node] = Gia_ManConst1Lit();
} else if (is_lhs_const && is_lhs_complement && is_rhs_const && !is_rhs_complement) {
copy[node] = Gia_ManCiLit(pAig, getNode(node).decisionVariable);
} else if (is_lhs_const && !is_lhs_complement && is_rhs_const && is_rhs_complement) {
copy[node] = LitNot(Gia_ManCiLit(pAig, getNode(node).decisionVariable));
} else if (!is_lhs_const && is_rhs_const && !is_rhs_complement) {
copy[node] = Gia_ManHashAnd(pAig, Gia_ManCiLit(pAig, getNode(node).decisionVariable), LitNotCond(copy[lhs], is_lhs_complement));
} else if (!is_rhs_const && is_lhs_const && !is_lhs_complement) {
copy[node] = Gia_ManHashAnd(pAig, LitNot(Gia_ManCiLit(pAig, getNode(node).decisionVariable)), LitNotCond(copy[rhs], is_rhs_complement));
} else if (is_lhs_const && is_lhs_complement && !is_rhs_const) {
copy[node] = Gia_ManHashOr(pAig, Gia_ManCiLit(pAig, getNode(node).decisionVariable), LitNotCond(copy[rhs], is_rhs_complement));
} else if (is_rhs_const && is_rhs_complement && !is_lhs_const) {
copy[node] = Gia_ManHashOr(pAig, LitNot(Gia_ManCiLit(pAig, getNode(node).decisionVariable)), LitNotCond(copy[lhs], is_lhs_complement));
} else {
copy[node] = Gia_ManHashMux(pAig, Gia_ManCiLit(pAig, getNode(node).decisionVariable), LitNotCond(copy[lhs], is_lhs_complement), LitNotCond(copy[rhs], is_rhs_complement));
}
}
for (const auto& i : irange(roots.size())) {
int root_id = LitToId(roots[i]);
bool is_complement = LitIsComplement(roots[i]);
Gia_ManAppendCo(pAig, LitNotCond(copy[root_id], is_complement));
}
}
} // namespace DecGraph
Gia_Man_t* Gia_ManDecGraph(Gia_Man_t* p) {
int nIns = Gia_ManCiNum(p);
int nOuts = Gia_ManCoNum(p);
Gia_Man_t* pNew;
Gia_Obj_t* pObj;
word v;
word* pTruth;
int i, k;
Gia_ManLevelNum(p);
pNew = Gia_ManStart(Gia_ManObjNum(p));
pNew->pName = Abc_UtilStrsav(p->pName);
pNew->pSpec = Abc_UtilStrsav(p->pSpec);
Gia_ManForEachCi(p, pObj, k)
Gia_ManAppendCi(pNew);
Gia_ObjComputeTruthTableStart(p, nIns);
Gia_ManHashStart(pNew);
DecGraph::DecisionDiagram dd;
for (k = 0; k < nOuts; k++) {
DecGraph::TruthTable tt;
tt.create(1lu << nIns);
int num_words = tt.nWords();
pObj = Gia_ManCo(p, k);
pTruth = Gia_ObjComputeTruthTable(p, Gia_ObjFanin0(pObj));
if (nIns >= 6) {
for (i = num_words - 1; i >= 0; --i) {
tt.data()[i] = Gia_ObjFaninC0(pObj) ? ~DecGraph::reverseBits(pTruth[i]) : DecGraph::reverseBits(pTruth[i]);
}
} else {
v = DecGraph::reverseBits((Gia_ObjFaninC0(pObj) ? ~pTruth[0] : pTruth[0]) & DecGraph::ones_mask[nIns]);
tt.data()[0] = v;
}
dd.addOutput(tt);
}
dd.build();
dd.buildAig(pNew);
Gia_ObjComputeTruthTableStop(p);
Gia_ManHashStop(pNew);
Gia_ManSetRegNum(pNew, Gia_ManRegNum(p));
return pNew;
}
Gia_Man_t* Gia_ManDecGraphFromFile(char* pFileName) {
char* pBuffer = Extra_FileReadContents(pFileName);
char *table = strtok(pBuffer, " \r\n\t|");
DecGraph::DecisionDiagram dd;
while (table) {
if (strlen(table) == 0) {
break;
}
DecGraph::TruthTable tt;
tt.readBinaryReverse(table);
dd.addOutput(tt);
table = strtok(NULL, " \r\n\t|");
}
free(pBuffer);
dd.build();
Gia_Man_t* pNew = Gia_ManStart(dd.nNodeAllocated());
pNew->pName = Abc_UtilStrsav("top");
for (int i = 0; i < dd.nVars(); ++i)
Gia_ManAppendCi(pNew);
Gia_ManHashStart(pNew);
dd.buildAig(pNew);
return pNew;
}
ABC_NAMESPACE_IMPL_END