diff --git a/src/cut/BUILD b/src/cut/BUILD index 46642ced68b..60e46b2117c 100644 --- a/src/cut/BUILD +++ b/src/cut/BUILD @@ -36,6 +36,7 @@ cc_library( "//src/rsz", "//src/sta:opensta_lib", "//src/utl", + "//third-party:mockturtle", "@abc", "@boost.bind", "@boost.config", diff --git a/src/cut/include/cut/abc_library_factory.h b/src/cut/include/cut/abc_library_factory.h index ac2ad19f3e2..7f3603e4a9b 100644 --- a/src/cut/include/cut/abc_library_factory.h +++ b/src/cut/include/cut/abc_library_factory.h @@ -35,6 +35,7 @@ class AbcLibrary abc::SC_Lib* abc_library() { return abc_library_.get(); } abc::Mio_Library_t* mio_library(); bool IsSupportedCell(const std::string& cell_name); + const std::set& SupportedCells(); bool IsConst0Cell(const std::string& cell_name); bool IsConst1Cell(const std::string& cell_name); bool IsConstCell(const std::string& cell_name); @@ -64,7 +65,8 @@ class AbcLibraryFactory explicit AbcLibraryFactory(utl::Logger* logger) : logger_(logger) {} AbcLibraryFactory& AddDbSta(sta::dbSta* db_sta); AbcLibraryFactory& AddResizer(rsz::Resizer* resizer); - AbcLibraryFactory& SetCorner(sta::Scene* corner); + utl::UniquePtrWithDeleter BuildScl(); + AbcLibraryFactory& SetScene(sta::Scene* scene); AbcLibrary Build(); private: @@ -82,12 +84,12 @@ class AbcLibraryFactory void AbcPopulateAbcSurfaceFromSta(abc::SC_Surface* abc_table, const sta::TableModel* model, sta::Units* units); - std::vector GetLibertyCellsFromCorner(sta::Scene* corner); + std::vector GetLibertyCellsFromScene(sta::Scene* scene); std::vector CreateAbcInputPins(sta::LibertyCell* cell); utl::Logger* logger_; sta::dbSta* db_sta_ = nullptr; - sta::Scene* corner_ = nullptr; + sta::Scene* scene_ = nullptr; rsz::Resizer* resizer_ = nullptr; }; diff --git a/src/cut/include/cut/logic_cut.h b/src/cut/include/cut/logic_cut.h index 4f6350999ac..ec515c3f4c0 100644 --- a/src/cut/include/cut/logic_cut.h +++ b/src/cut/include/cut/logic_cut.h @@ -3,18 +3,26 @@ #pragma once +#include #include #include #include "base/abc/abc.h" #include "cut/abc_library_factory.h" #include "db_sta/dbNetwork.hh" +#include "kitty/dynamic_truth_table.hpp" +#include "mockturtle/algorithms/decomposition.hpp" +#include "mockturtle/io/genlib_reader.hpp" +#include "mockturtle/networks/aig.hpp" +#include "mockturtle/utils/tech_library.hpp" +#include "mockturtle/views/names_view.hpp" #include "sta/NetworkClass.hh" #include "utl/Logger.h" #include "utl/deleter.h" #include "utl/unique_name.h" namespace cut { + class LogicCut { public: @@ -49,6 +57,12 @@ class LogicCut sta::dbNetwork* network, utl::Logger* logger); + template + mockturtle::names_view BuildMappedMockturtleNetwork( + mockturtle::tech_library& mockturtle_library, + sta::dbNetwork* network, + utl::Logger* logger); + void InsertMappedAbcNetwork(abc::Abc_Ntk_t* abc_network, AbcLibrary& abc_library, sta::dbNetwork* network, @@ -60,4 +74,141 @@ class LogicCut std::vector primary_outputs_; sta::InstanceSet cut_instances_; }; + +template +mockturtle::names_view +LogicCut::BuildMappedMockturtleNetwork( + mockturtle::tech_library& mockturtle_library, + sta::dbNetwork* network, + utl::Logger* logger) +{ + using aig_ntk = mockturtle::names_view; + using signal = mockturtle::signal; + using gate = mockturtle::gate; + + aig_ntk ntk; + + // Assert that constant-0 node exists. + ntk.get_constant(false); + + // Build a map: cell name -> mockturtle gate* + std::unordered_map gate_by_name; + + const auto gates = mockturtle_library.get_gates(); + + for (const mockturtle::gate& g : gates) { + gate_by_name.emplace(fmt::format("{}_{}", g.name, g.output_name), &g); + } + + // Build a set of CUT instances for quick membership checks + std::unordered_set cut_instances_set( + cut_instances_.begin(), cut_instances_.end()); + + // Net -> AIG signal memoization + std::unordered_map net_to_signal; + + // Create PIs for primary_inputs_ (CUT boundary inputs) + for (sta::Net* net : primary_inputs_) { + const signal pi = ntk.create_pi(network->name(net)); + net_to_signal[net] = pi; + } + + // Recursive builder: given a STA net, return the corresponding AIG signal + std::function build_net_signal + = [&](sta::Net* net, int depth) -> signal { + // Memoized? + if (auto it = net_to_signal.find(net); it != net_to_signal.end()) { + return it->second; + } + + sta::PinSet* drivers = network->drivers(net); + if (!drivers || drivers->empty()) { + logger->error(utl::CUT, + 50, + "Net driven from outside the cut {}", + network->name(net)); + } + + if (drivers->size() != 1) { + logger->error(utl::CUT, + 51, + "Net {} has {} drivers; CUT expects exactly one.", + network->name(net), + drivers->size()); + } + + const sta::Pin* driver_pin = *drivers->begin(); + sta::Instance* driver_inst = network->instance(driver_pin); + sta::LibertyCell* driver_cell = network->libertyCell(driver_inst); + + // Driver should not be sequential or outside the cut (handled by logic + // extractor) + if (!driver_cell) { + logger->error( + utl::CUT, 52, "Driver pin not found: {}", network->name(driver_inst)); + } + if (driver_cell->hasSequentials() + || !cut_instances_set.contains(driver_inst)) { + logger->error( + utl::CUT, 53, "Invalid driver for: {}", network->name(driver_inst)); + } + + const std::string cell_name = driver_cell->name(); + const std::string pin_name = network->portName(driver_pin); + + auto gate_it = gate_by_name.find(fmt::format("{}_{}", cell_name, pin_name)); + if (gate_it == gate_by_name.end()) { + logger->error(utl::CUT, + 54, + "Cell {} not found in mockturtle technology library.", + cell_name); + } + + const gate* g = gate_it->second; + + if (g->pins.size() != g->function.num_vars()) { + logger->error(utl::CUT, 55, "Invalid pin count"); + } + + // Build fanin signals in the same order as GENLIB pins + std::vector fanins; + fanins.reserve(g->pins.size()); + + for (const auto& p : g->pins) { + sta::Pin* sta_pin = network->findPin(driver_inst, p.name.c_str()); + if (sta_pin == nullptr) { + logger->error(utl::CUT, + 56, + "Cannot find pin {} on instance {}.", + p.name, + network->name(driver_inst)); + } + + sta::Net* fanin_net = network->net(sta_pin); + fanins.push_back(build_net_signal(fanin_net, depth + 1)); + } + + // Realize gate function as AIG fragment. + std::vector vars(g->function.num_vars()); + std::iota(vars.begin(), vars.end(), 0u); + + const signal out_sig + = mockturtle::shannon_decomposition(ntk, g->function, vars, fanins); + if (!ntk.has_name(out_sig)) { + ntk.set_name(out_sig, network->name(net)); + } + + net_to_signal[net] = out_sig; + return out_sig; + }; + + // Hook up primary outputs + for (sta::Net* net : primary_outputs_) { + const signal s = build_net_signal(net, 0); + ntk.create_po(s, network->name(net)); + } + + return ntk; +} + } // namespace cut diff --git a/src/cut/include/cut/logic_extractor.h b/src/cut/include/cut/logic_extractor.h index 57b2ac2fd62..17d3431a8db 100644 --- a/src/cut/include/cut/logic_extractor.h +++ b/src/cut/include/cut/logic_extractor.h @@ -3,14 +3,19 @@ #pragma once +#include +#include #include #include #include "cut/abc_library_factory.h" #include "cut/logic_cut.h" #include "db_sta/dbSta.hh" +#include "mockturtle/utils/tech_library.hpp" +#include "sta/Bfs.hh" #include "sta/Graph.hh" #include "sta/NetworkClass.hh" +#include "sta/PortDirection.hh" #include "sta/SearchPred.hh" #include "utl/Logger.h" @@ -20,25 +25,44 @@ class Mode; namespace cut { +template +struct is_mockturtle_library : std::false_type +{ +}; + +template +struct is_mockturtle_library> + : std::true_type +{ +}; + +template +inline constexpr bool is_mockturtle_library_v + = is_mockturtle_library>::value; + +template +concept Library = std::same_as, AbcLibrary> + || is_mockturtle_library_v; + // This class is an implementation of an sta::SearchPred that constrains // the edges BfsBwkIterators are allowed to traverse. In this case this // particular class will not allow the iterator to walk through DFFs, latches // or any cell that is not supported by ABC. This allows our logic extractor // to only extract cells that we could reasonably put in ABC. -class SearchPredCombAbcSupport : public sta::SearchPred1 +class SearchPredCombLibrarySupport : public sta::SearchPred1 { public: // supported_liberty_cells is a set of cells that are supported by ABC. - SearchPredCombAbcSupport(sta::dbSta* open_sta, - AbcLibrary* abc_library, - sta::Graph* graph) - : sta::SearchPred1(open_sta), abc_library_(abc_library), graph_(graph) + SearchPredCombLibrarySupport(sta::dbSta* open_sta, + const std::set& supported_cells, + sta::Graph* graph) + : sta::SearchPred1(open_sta), supported_(supported_cells), graph_(graph) { } bool searchThru(sta::Edge* edge, const sta::Mode* mode) const override; private: - AbcLibrary* abc_library_; + const std::set& supported_; sta::Graph* graph_; }; @@ -50,7 +74,8 @@ class LogicExtractorFactory { } LogicExtractorFactory& AppendEndpoint(sta::Vertex* vertex); - LogicCut BuildLogicCut(AbcLibrary& abc_network); + template + LogicCut BuildLogicCut(T& library); private: // Process vertices from BFS STA output to find the primary inputs. @@ -58,19 +83,244 @@ class LogicExtractorFactory std::vector& cut_vertices); std::vector GetPrimaryOutputs( std::vector& cut_vertices); - std::vector GetCutVertices(AbcLibrary& abc_network); - sta::InstanceSet GetCutInstances(std::vector& cut_vertices); + std::vector GetCutVertices( + const std::set& supported_cells); + sta::InstanceSet GetCutInstances( + std::vector& cut_vertices, + const std::set& supported_cells); std::vector FilterUndrivenOutputs( std::vector& primary_outputs, sta::InstanceSet& cut_instances); std::vector ConvertIoPinsToNets( std::vector& primary_io_pins); + template std::vector AddMissingVertices( std::vector& cut_vertices, - AbcLibrary& abc_library); + T& library); + template T> + std::vector AddMisingTopIO(std::vector& pins, T pred); + std::vector AddMisingPrimaryInputs( + std::vector& primary_inputs); + std::vector AddMisingPrimaryOutputs( + std::vector& primary_outputs); std::vector endpoints_; sta::dbSta* open_sta_; utl::Logger* logger_; }; + +namespace { + +// Returns the vertex of a constant 1 or 0 cell if the driver of the input +// pin is a constant 0 or 1 cell. Otherwise return nullptr. +template +sta::Vertex* GetConstantVertexIfExists(sta::dbNetwork* network, + sta::Vertex* input_vertex, + T& library, + utl::Logger* logger) +{ + sta::Graph* graph = network->graph(); + // If it's constant add the driving pin. If we have multiple drivers + // bad things are happening. + sta::PinSet* constant_driver = network->drivers(input_vertex->pin()); + if (constant_driver->size() != 1) { + logger->error( + utl::CUT, + 47, + "constant vertex: {} should have exactly one constant driver. " + "Has {}, please report this internal error.", + input_vertex->name(network), + constant_driver->size()); + } + + const sta::Pin* constant_pin = *constant_driver->begin(); + sta::Instance* instance = network->instance(constant_pin); + if (!instance) { + return nullptr; + } + // Okay if the cell we're looking for is actually a const cell add it. + sta::LibertyCell* liberty_cell = network->libertyCell(instance); + + if constexpr (is_mockturtle_library_v) { + auto const& gates = library.get_gates(); + + auto gate = std::find_if( + gates.begin(), gates.end(), [&](mockturtle::gate const& g) { + return g.name == liberty_cell->name(); + }); + + if (gate == gates.end()) { + return nullptr; + } + + auto const& f = gate->function; + + if (!(kitty::is_const0(f) || kitty::is_const0(~f))) { + return nullptr; + } + } else { + if (!library.IsConstCell(liberty_cell->name())) { + return nullptr; + } + } + + sta::Vertex* constant_vertex = graph->vertex(network->vertexId(constant_pin)); + + return constant_vertex; +} + +} // anonymous namespace + +template +LogicCut LogicExtractorFactory::BuildLogicCut(T& library) +{ + open_sta_->ensureGraph(); + open_sta_->ensureLevelized(); + + std::set supported_cells; + if constexpr (is_mockturtle_library_v) { + for (auto const& g : library.get_gates()) { + supported_cells.insert(g.name); + } + } else { + supported_cells = library.SupportedCells(); + } + + std::vector cut_vertices = GetCutVertices(supported_cells); + // Dealing with constant cells 1/0 and disabled timing paths. + cut_vertices = AddMissingVertices(cut_vertices, library); + + std::vector primary_inputs = GetPrimaryInputs(cut_vertices); + std::vector primary_outputs = GetPrimaryOutputs(cut_vertices); + sta::InstanceSet cut_instances + = GetCutInstances(cut_vertices, supported_cells); + + if constexpr (is_mockturtle_library_v) { + // sometimes part of top I/O is missing, causing errors when mockturtle is + // used + primary_inputs = AddMisingPrimaryInputs(primary_inputs); + primary_outputs = AddMisingPrimaryOutputs(primary_outputs); + } + + // Remove primary outputs who are undriven. This can happen when a flop + // feeds into another flop where the logic cone is essentially just a wire. + // Just remove them. + std::vector filtered_primary_outputs + = FilterUndrivenOutputs(primary_outputs, cut_instances); + + std::vector primary_input_nets + = ConvertIoPinsToNets(primary_inputs); + std::vector primary_output_nets + = ConvertIoPinsToNets(filtered_primary_outputs); + + auto network = open_sta_->network(); + + std::ranges::stable_sort(primary_input_nets, + [network](const sta::Net* a, const sta::Net* b) { + return network->id(a) < network->id(b); + }); + std::ranges::stable_sort(primary_output_nets, + [network](const sta::Net* a, const sta::Net* b) { + return network->id(a) < network->id(b); + }); + + return LogicCut(std::move(primary_input_nets), + std::move(primary_output_nets), + std::move(cut_instances)); +} + +// Annoyingly STA does not include input pins that are filtered out in the +// search. for example constant inputs, or disabled timing paths. We need to +// loop through all the instances attached to the vertices to make sure we +// grabbed all the input pins. +// +// Some vertices can be const even if they're not constant cells themselves +// for example an AND gate with one of its inputs set to 0. The output will +// be marked as constant, and thus not included. +// +// If OpenSTA offers a search predicate that allows constant cells to be +// included remove the while loop below. +template +std::vector LogicExtractorFactory::AddMissingVertices( + std::vector& cut_vertices, + T& library) +{ + std::vector result(cut_vertices.begin(), cut_vertices.end()); + sta::dbNetwork* network = open_sta_->getDbNetwork(); + std::unordered_set endpoint_set(endpoints_.begin(), + endpoints_.end()); + std::unordered_set cut_vertex_set(cut_vertices.begin(), + cut_vertices.end()); + + for (sta::Vertex* vertex : cut_vertices) { + // Skip over DFFs and other primary outs. We don't actually + // want to add vertices from these cells. We really just + // want the ones in the fan-in set who are filtered by STA + // for one reason or another. + if (endpoint_set.contains(vertex)) { + continue; + } + + sta::Instance* instance = network->instance(vertex->pin()); + std::unique_ptr iter( + network->pinIterator(instance)); + sta::Graph* graph = network->graph(); + while (iter->hasNext()) { + sta::Pin* pin = iter->next(); + sta::Vertex* vertex = graph->vertex(network->vertexId(pin)); + // Don't want any duplicate entries + if (cut_vertex_set.contains(vertex)) { + continue; + } + // Don't want any non-inputs. + sta::PortDirection* direction = network->direction(pin); + if (!direction->isInput()) { + continue; + } + // Found a vertex who should be in the cut set, but isn't; + // add it. The output of this instance is not constant( or is disabled for + // some reason), but one of its inputs is. We need to add this pin, and + // possibly its driver. + result.push_back(vertex); + cut_vertex_set.insert(vertex); + + // Figure out if we should add the driver. + if (!open_sta_->isConstant(pin, open_sta_->cmdMode())) { + continue; + } + + sta::Vertex* constant_vertex + = GetConstantVertexIfExists(network, vertex, library, logger_); + if (!constant_vertex) { + continue; + } + + result.push_back(constant_vertex); + cut_vertex_set.insert(constant_vertex); + } + } + return result; +} + +template T> +std::vector LogicExtractorFactory::AddMisingTopIO( + std::vector& pins, + T pred) +{ + std::vector result(pins.begin(), pins.end()); + std::unordered_set used_pins(pins.begin(), pins.end()); + auto network = open_sta_->getDbNetwork(); + + auto pin_iterator = std::unique_ptr( + network->pinIterator(network->topInstance())); + while (pin_iterator->hasNext()) { + sta::Pin* pin = pin_iterator->next(); + if (pred(pin) && used_pins.insert(pin).second) { + result.push_back(pin); + } + } + + return result; +} + } // namespace cut diff --git a/src/cut/src/CMakeLists.txt b/src/cut/src/CMakeLists.txt index ac1a6537a2f..948e5268792 100644 --- a/src/cut/src/CMakeLists.txt +++ b/src/cut/src/CMakeLists.txt @@ -28,6 +28,7 @@ target_link_libraries(cut PUBLIC rsz_lib ${ABC_LIBRARY} + ${MOCKTURTLE_LIBRARY} PRIVATE odb dbSta diff --git a/src/cut/src/abc_library_factory.cpp b/src/cut/src/abc_library_factory.cpp index 149dc2428c7..53d406195de 100644 --- a/src/cut/src/abc_library_factory.cpp +++ b/src/cut/src/abc_library_factory.cpp @@ -369,25 +369,25 @@ AbcLibraryFactory& AbcLibraryFactory::AddResizer(rsz::Resizer* resizer) return *this; } -AbcLibraryFactory& AbcLibraryFactory::SetCorner(sta::Scene* corner) +AbcLibraryFactory& AbcLibraryFactory::SetScene(sta::Scene* scene) { - corner_ = corner; + scene_ = scene; return *this; } -AbcLibrary AbcLibraryFactory::Build() +utl::UniquePtrWithDeleter AbcLibraryFactory::BuildScl() { if (!db_sta_) { logger_->error(utl::CUT, 19, "Build called with null sta library"); } - if (db_sta_->scenes().size() > 1 && !corner_) { + if (db_sta_->scenes().size() > 1 && !scene_) { logger_->error( - utl::CUT, 20, "More than one corner is loaded, and no corner was set"); + utl::CUT, 20, "More than one scene is loaded, and no scene was set"); } - if (!corner_) { - corner_ = db_sta_->scenes()[0]; + if (!scene_) { + scene_ = db_sta_->scenes()[0]; } // Populate units from default liberty @@ -396,9 +396,9 @@ AbcLibrary AbcLibraryFactory::Build() = db_sta_->network()->defaultLibertyLibrary(); PopulateLibraryDetails(abc_library, default_library); - // Grab cells from requested corner. + // Grab cells from requested scene. std::vector liberty_cells - = GetLibertyCellsFromCorner(corner_); + = GetLibertyCellsFromScene(scene_); PopulateAbcSclLibFromSta( abc_library, liberty_cells, default_library->units()); @@ -407,18 +407,21 @@ AbcLibrary AbcLibraryFactory::Build() abc::Abc_SclHashCells(abc_library); abc::Abc_SclLinkCells(abc_library); - return AbcLibrary( - utl::UniquePtrWithDeleter( - abc_library, [](abc::SC_Lib* lib) { abc::Abc_SclLibFree(lib); }), - logger_); + return utl::UniquePtrWithDeleter( + abc_library, [](abc::SC_Lib* lib) { abc::Abc_SclLibFree(lib); }); +} + +AbcLibrary AbcLibraryFactory::Build() +{ + return AbcLibrary(BuildScl(), logger_); } -std::vector AbcLibraryFactory::GetLibertyCellsFromCorner( - sta::Scene* corner) +std::vector AbcLibraryFactory::GetLibertyCellsFromScene( + sta::Scene* scene) { std::vector result; const sta::LibertySeq& libraries - = corner->libertyLibraries(sta::MinMax::max()); + = scene->libertyLibraries(sta::MinMax::max()); for (sta::LibertyLibrary* library : libraries) { sta::LibertyCellIterator cell_iterator(library); while (cell_iterator.hasNext()) { @@ -598,6 +601,21 @@ bool AbcLibrary::IsSupportedCell(const std::string& cell_name) return supported_cells_.find(cell_name) != supported_cells_.end(); } +const std::set& AbcLibrary::SupportedCells() +{ + if (supported_cells_.empty()) { + int num_gates = abc::SC_LibCellNum(abc_library_.get()); + for (int i = 0; i < num_gates; i++) { + abc::SC_Cell* cell = abc::SC_LibCell(abc_library_.get(), i); + if (cell->n_outputs != 1) { + continue; + } + supported_cells_.insert(cell->pName); + } + } + return supported_cells_; +} + void AbcLibrary::InitializeConstGates() { const_gates_initalized_ = true; diff --git a/src/cut/src/logic_cut.cpp b/src/cut/src/logic_cut.cpp index fba6a9896f1..aeb8d85b187 100644 --- a/src/cut/src/logic_cut.cpp +++ b/src/cut/src/logic_cut.cpp @@ -543,8 +543,7 @@ void DeleteExistingLogicCut(sta::dbNetwork* network, // If pin isn't a primary input or output add to deleted list. The only // way this can happen is if a net is only used within the cutset, and // in that case we want to delete it. - if (primary_input_or_output_nets.find(connected_net) - == primary_input_or_output_nets.end()) { + if (!primary_input_or_output_nets.contains(connected_net)) { nets_to_be_deleted.insert(connected_net); } } diff --git a/src/cut/src/logic_extractor.cpp b/src/cut/src/logic_extractor.cpp index 4c15e8e3b08..e2f95daf262 100644 --- a/src/cut/src/logic_extractor.cpp +++ b/src/cut/src/logic_extractor.cpp @@ -25,8 +25,8 @@ namespace cut { -bool SearchPredCombAbcSupport::searchThru(sta::Edge* edge, - const sta::Mode* mode) const +bool SearchPredCombLibrarySupport::searchThru(sta::Edge* edge, + const sta::Mode* mode) const { const sta::TimingRole* role = edge->role(); if (role->genericRole() == sta::TimingRole::regClkToQ() @@ -45,7 +45,7 @@ bool SearchPredCombAbcSupport::searchThru(sta::Edge* edge, return false; } - if (!abc_library_->IsSupportedCell(cell->name())) { + if (!supported_.contains(cell->name())) { return false; } @@ -60,21 +60,77 @@ LogicExtractorFactory& LogicExtractorFactory::AppendEndpoint( } std::vector LogicExtractorFactory::GetCutVertices( - AbcLibrary& abc_network) + const std::set& supported_cells) { - cut::SearchPredCombAbcSupport pred( - open_sta_, &abc_network, open_sta_->graph()); + cut::SearchPredCombLibrarySupport pred( + open_sta_, supported_cells, open_sta_->graph()); sta::BfsBkwdIterator iter(sta::BfsIndex::other, &pred, open_sta_); for (const auto& end_point : endpoints_) { iter.enqueue(end_point); } + sta::dbNetwork* network = open_sta_->getDbNetwork(); + sta::Graph* graph = open_sta_->graph(); + std::vector cut_vertices; + std::unordered_set cut_set; + while (iter.hasNext()) { sta::Vertex* vertex = iter.next(); iter.enqueueAdjacentVertices(vertex); - cut_vertices.push_back(vertex); + + if (cut_set.insert(vertex).second) { + cut_vertices.push_back(vertex); + } + } + + // Add all output pin vertices to the cut + for (sta::Vertex* v : cut_vertices) { + sta::Pin* pin = v->pin(); + if (!pin) { + continue; + } + + sta::PortDirection* dir = network->direction(pin); + if (!dir || !dir->isOutput()) { + continue; + } + + sta::Instance* inst = network->instance(pin); + if (!inst) { + continue; + } + + auto pin_it + = std::unique_ptr(network->pinIterator(inst)); + + while (pin_it->hasNext()) { + sta::Pin* p = pin_it->next(); + if (!p) { + continue; + } + + sta::PortDirection* dir = network->direction(p); + if (!dir || !dir->isOutput()) { + continue; + } + + sta::VertexId vid = network->vertexId(p); + if (vid == 0) { + continue; + } + + sta::Vertex* pv = graph->vertex(vid); + if (!pv) { + continue; + } + + if (cut_set.insert(pv).second) { + cut_vertices.push_back(pv); + } + } } + return cut_vertices; } @@ -87,16 +143,14 @@ std::vector LogicExtractorFactory::GetPrimaryInputs( pins.insert(vertex->pin()); } - std::unordered_set endpoint_vertex; - for (sta::Vertex* vertex : endpoints_) { - endpoint_vertex.insert(vertex); - } + std::unordered_set endpoint_vertex(endpoints_.begin(), + endpoints_.end()); std::vector primary_inputs; for (sta::Vertex* vertex : cut_vertices) { // If the pins in the cutset are primary outputs don't call it // a primary input - if (endpoint_vertex.find(vertex) != endpoint_vertex.end()) { + if (endpoint_vertex.contains(vertex)) { continue; } @@ -109,7 +163,7 @@ std::vector LogicExtractorFactory::GetPrimaryInputs( for (const sta::Pin* pin : *pin_set) { // If a driver pin of the pin under consideration is not in the cut // vertices, then it is a primary input. - if (pins.find(pin) != pins.end()) { + if (pins.contains(pin)) { is_primary_input = false; break; } @@ -123,114 +177,6 @@ std::vector LogicExtractorFactory::GetPrimaryInputs( return primary_inputs; } -// Returns the vertex of a constant 1 or 0 cell if the driver of the input -// pin is a constant 0 or 1 cell. Otherwise return nullptr. -sta::Vertex* GetConstantVertexIfExists(sta::dbNetwork* network, - sta::Vertex* input_vertex, - AbcLibrary& abc_library, - utl::Logger* logger) -{ - sta::Graph* graph = network->graph(); - // If it's constant add the driving pin. If we have multiple drivers - // bad things are happening. - sta::PinSet* constant_driver = network->drivers(input_vertex->pin()); - if (constant_driver->size() != 1) { - logger->error( - utl::CUT, - 47, - "constant vertex: {} should have exactly one constant driver. " - "Has {}, please report this internal error.", - input_vertex->name(network), - constant_driver->size()); - } - - const sta::Pin* constant_pin = *constant_driver->begin(); - sta::Instance* instance = network->instance(constant_pin); - if (!instance) { - return nullptr; - } - // Okay if the cell we're looking for is actually a const cell add it. - sta::LibertyCell* liberty_cell = network->libertyCell(instance); - if (!abc_library.IsConstCell(liberty_cell->name())) { - return nullptr; - } - sta::Vertex* constant_vertex = graph->vertex(network->vertexId(constant_pin)); - - return constant_vertex; -} - -// Annoyingly STA does not include input pins that are filtered out in the -// search. for example constant inputs, or disabled timing paths. We need to -// loop through all the instances attached to the vertices to make sure we -// grabbed all the input pins. -// -// Some vertices can be const even if they're not constant cells themselves -// for example an AND gate with one of its inputs set to 0. The output will -// be marked as constant, and thus not included. -// -// If OpenSTA offers a search predicate that allows constant cells to be -// included remove the while loop below. -std::vector LogicExtractorFactory::AddMissingVertices( - std::vector& cut_vertices, - AbcLibrary& abc_library) -{ - std::vector result(cut_vertices.begin(), cut_vertices.end()); - sta::dbNetwork* network = open_sta_->getDbNetwork(); - std::unordered_set endpoint_set(endpoints_.begin(), - endpoints_.end()); - std::unordered_set cut_vertex_set(cut_vertices.begin(), - cut_vertices.end()); - - for (sta::Vertex* vertex : cut_vertices) { - // Skip over DFFs and other primary outs. We don't actually - // want to add vertices from these cells. We really just - // want the ones in the fan-in set who are filtered by STA - // for one reason or another. - if (endpoint_set.find(vertex) != endpoint_set.end()) { - continue; - } - - sta::Instance* instance = network->instance(vertex->pin()); - std::unique_ptr iter( - network->pinIterator(instance)); - sta::Graph* graph = network->graph(); - while (iter->hasNext()) { - sta::Pin* pin = iter->next(); - sta::Vertex* vertex = graph->vertex(network->vertexId(pin)); - // Don't want any duplicate entries - if (cut_vertex_set.find(vertex) != cut_vertex_set.end()) { - continue; - } - // Don't want any non-inputs. - sta::PortDirection* direction = network->direction(pin); - if (!direction->isInput()) { - continue; - } - // Found a vertex who should be in the cut set, but isn't; - // add it. The output of this instance is not constant( or is disabled for - // some reason), but one of its inputs is. We need to add this pin, and - // possibly its driver. - result.push_back(vertex); - cut_vertex_set.insert(vertex); - - // Figure out if we should add the driver. - if (!open_sta_->isConstant(pin, open_sta_->cmdMode())) { - continue; - } - - sta::Vertex* constant_vertex - = GetConstantVertexIfExists(network, vertex, abc_library, logger_); - if (!constant_vertex) { - continue; - } - - result.push_back(constant_vertex); - cut_vertex_set.insert(constant_vertex); - } - } - return result; -} - std::vector LogicExtractorFactory::GetPrimaryOutputs( std::vector& cut_vertices) { @@ -259,7 +205,7 @@ std::vector LogicExtractorFactory::GetPrimaryOutputs( while (pin_iterator->hasNext()) { const sta::Pin* connected_pin = pin_iterator->next(); // Pin is not in our cutset, and therefore is a primary output. - if (cut_set_vertices.find(connected_pin) == cut_set_vertices.end()) { + if (!cut_set_vertices.contains(connected_pin)) { sta::VertexId vertex_id = network->vertexId(connected_pin); sta::Vertex* vertex = network->graph()->vertex(vertex_id); primary_outputs.push_back(vertex->pin()); @@ -271,7 +217,8 @@ std::vector LogicExtractorFactory::GetPrimaryOutputs( } sta::InstanceSet LogicExtractorFactory::GetCutInstances( - std::vector& cut_vertices) + std::vector& cut_vertices, + const std::set& supported_cells) { // Loop through all the verticies in the cut set, and then turn their pins // into instances. Verticies are pretty much pins which means for any given @@ -295,7 +242,10 @@ sta::InstanceSet LogicExtractorFactory::GetCutInstances( // want to put those cells in ABC. Remove them. for (sta::Vertex* vertex : endpoints_) { sta::Instance* endpoint_instance = network->instance(vertex->pin()); - cut_instances.erase(endpoint_instance); + sta::LibertyCell* cell = network->libertyCell(endpoint_instance); + if (!cell || !supported_cells.contains(cell->name())) { + cut_instances.erase(endpoint_instance); + } } return cut_instances; @@ -343,8 +293,7 @@ std::vector LogicExtractorFactory::ConvertIoPinsToNets( { sta::dbNetwork* network = open_sta_->getDbNetwork(); - std::unordered_set primary_input_nets; - primary_input_nets.reserve(primary_io_pins.size()); + std::set primary_input_nets; for (sta::Pin* pin : primary_io_pins) { sta::Net* net = nullptr; // check if this pin is a terminal @@ -371,33 +320,20 @@ std::vector LogicExtractorFactory::ConvertIoPinsToNets( return result; } -LogicCut LogicExtractorFactory::BuildLogicCut(AbcLibrary& abc_network) +std::vector LogicExtractorFactory::AddMisingPrimaryInputs( + std::vector& primary_inputs) +{ + return AddMisingTopIO(primary_inputs, [&](sta::Pin* pin) { + return open_sta_->network()->direction(pin)->isInput(); + }); +} + +std::vector LogicExtractorFactory::AddMisingPrimaryOutputs( + std::vector& primary_outputs) { - open_sta_->ensureGraph(); - open_sta_->ensureLevelized(); - - std::vector cut_vertices = GetCutVertices(abc_network); - // Dealing with constant cells 1/0 and disabled timing paths. - cut_vertices = AddMissingVertices(cut_vertices, abc_network); - - std::vector primary_inputs = GetPrimaryInputs(cut_vertices); - std::vector primary_outputs = GetPrimaryOutputs(cut_vertices); - sta::InstanceSet cut_instances = GetCutInstances(cut_vertices); - - // Remove primary outputs who are undriven. This can happen when a flop feeds - // into another flop where the logic cone is essentially just a wire. Just - // remove them. - std::vector filtered_primary_outputs - = FilterUndrivenOutputs(primary_outputs, cut_instances); - - std::vector primary_input_nets - = ConvertIoPinsToNets(primary_inputs); - std::vector primary_output_nets - = ConvertIoPinsToNets(filtered_primary_outputs); - - return LogicCut(std::move(primary_input_nets), - std::move(primary_output_nets), - std::move(cut_instances)); + return AddMisingTopIO(primary_outputs, [&](sta::Pin* pin) { + return open_sta_->network()->direction(pin)->isOutput(); + }); } } // namespace cut diff --git a/src/cut/test/BUILD b/src/cut/test/BUILD index 1b8e0cb6089..656f2289150 100644 --- a/src/cut/test/BUILD +++ b/src/cut/test/BUILD @@ -34,6 +34,7 @@ cc_test( ], deps = [ "//src/cut", + "//src/cut/test/cpp/helper", "//src/dbSta", "//src/dbSta:dbReadVerilog", "//src/odb/src/db", @@ -41,15 +42,65 @@ cc_test( "//src/sta:opensta_lib", "//src/tst", "//src/utl", + "@googletest//:gtest", + "@googletest//:gtest_main", + # TODO Workaround for a bug where `main` is taken from abc leading to false positives + # Remove when https://github.com/bazelbuild/bazel-central-registry/blob/main/modules/abc/0.64-yosyshq.bcr.1/overlay/BUILD.bazel will be synchronized with https://github.com/The-OpenROAD-Project/abc/blob/master/BUILD + # buildifier: leave-alone "@abc", + ], +) + +cc_test( + name = "cut_mockturtle_test", + srcs = ["cpp/TestMockturtle.cc"], + data = [ + "Nangate45/Nangate45_stdcell.lef", + "Nangate45/Nangate45_tech.lef", + "Nangate45/Nangate45_typ.lib", + "aes_nangate45.v", + "asap7/asap7_tech_1x_201209.lef", + "asap7/asap7sc7p5t_28_R_1x_220121a.lef", + "asap7/asap7sc7p5t_AO_RVT_FF_nldm_211120.lib.gz", + "asap7/asap7sc7p5t_INVBUF_RVT_FF_nldm_220122.lib.gz", + "asap7/asap7sc7p5t_OA_RVT_FF_nldm_211120.lib.gz", + "asap7/asap7sc7p5t_SEQ_RVT_FF_nldm_220123.lib", + "asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz", + "empty_cut_set.v", + "side_outputs_extract.v", + "side_outputs_extract_logic_depth.v", + "simple_and_gate_extract.v", + "sky130/sky130_fd_sc_hd__ss_n40C_1v40.lib", + "sky130/sky130hd.tlef", + "sky130/sky130hd_std_cell.lef", + "sky130_const_cell.v", + ], + deps = [ + "//src/cut", + "//src/cut/test/cpp/helper", + "//src/dbSta", + "//src/dbSta:dbReadVerilog", + "//src/odb/src/db", + "//src/odb/src/lefin", + "//src/sta:opensta_lib", + "//src/tst", + "//src/utl", + "//third-party:mockturtle", "@googletest//:gtest", "@googletest//:gtest_main", + # TODO Workaround for a bug where `main` is taken from abc leading to false positives + # Remove when https://github.com/bazelbuild/bazel-central-registry/blob/main/modules/abc/0.64-yosyshq.bcr.1/overlay/BUILD.bazel will be synchronized with https://github.com/The-OpenROAD-Project/abc/blob/master/BUILD + # buildifier: leave-alone + "@abc", ], ) cc_test( name = "CutGTests", - srcs = ["cpp/TestAbc.cc"], + srcs = [ + "cpp/TestAbc.cc", + "cpp/TestMockturtle.cc", + ], data = [ "Nangate45/Nangate45_stdcell.lef", "Nangate45/Nangate45_tech.lef", @@ -73,6 +124,7 @@ cc_test( ], deps = [ "//src/cut", + "//src/cut/test/cpp/helper", "//src/dbSta", "//src/dbSta:dbReadVerilog", "//src/odb/src/db", @@ -80,8 +132,12 @@ cc_test( "//src/sta:opensta_lib", "//src/tst", "//src/utl", - "@abc", + "//third-party:mockturtle", "@googletest//:gtest", "@googletest//:gtest_main", + # TODO Workaround for a bug where `main` is taken from abc leading to false positives + # Remove when https://github.com/bazelbuild/bazel-central-registry/blob/main/modules/abc/0.64-yosyshq.bcr.1/overlay/BUILD.bazel will be synchronized with https://github.com/The-OpenROAD-Project/abc/blob/master/BUILD + # buildifier: leave-alone + "@abc", ], ) diff --git a/src/cut/test/cpp/CMakeLists.txt b/src/cut/test/cpp/CMakeLists.txt index 466329ed414..d02a1247143 100644 --- a/src/cut/test/cpp/CMakeLists.txt +++ b/src/cut/test/cpp/CMakeLists.txt @@ -1,6 +1,6 @@ include(openroad) -add_executable(CutGTests TestAbc.cc) +add_executable(CutGTests TestAbc.cc TestMockturtle.cc) target_link_libraries(CutGTests OpenSTA GTest::gtest @@ -12,6 +12,7 @@ target_link_libraries(CutGTests cut tst ${TCL_LIBRARY} + cut_test_helper ) target_include_directories(CutGTests @@ -25,3 +26,5 @@ gtest_discover_tests(CutGTests add_dependencies(build_and_test CutGTests ) + +add_subdirectory(helper) diff --git a/src/cut/test/cpp/TestAbc.cc b/src/cut/test/cpp/TestAbc.cc index 37bb72c0afe..4d34451de01 100644 --- a/src/cut/test/cpp/TestAbc.cc +++ b/src/cut/test/cpp/TestAbc.cc @@ -25,6 +25,7 @@ #include "db_sta/dbSta.hh" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "helper.h" #include "map/mio/mio.h" #include "map/scl/sclLib.h" #include "misc/vec/vecPtr.h" @@ -57,68 +58,9 @@ using cut::LogicCut; using cut::LogicExtractorFactory; using ::testing::Contains; -static std::once_flag init_abc_flag; - -static const std::string kPrefix("_main/src/cut/test/"); - -class AbcTest : public tst::Fixture +class AbcTest : public CutFixture { protected: - void SetUp() override - { - std::call_once(init_abc_flag, []() { abc::Abc_Start(); }); - library_ = readLiberty(kPrefix + "Nangate45/Nangate45_typ.lib"); - - odb::dbTech* tech - = loadTechLef("nangate45", kPrefix + "Nangate45/Nangate45_tech.lef"); - loadLibaryLef( - tech, "nangate45", kPrefix + "Nangate45/Nangate45_stdcell.lef"); - - sta::Units* units = library_->units(); - power_unit_ = units->powerUnit(); - } - - void LoadVerilog(const std::string& file_name, const std::string& top = "top") - { - // Assumes module name is "top" and clock name is "clk" - sta::dbNetwork* network = sta_->getDbNetwork(); - ord::dbVerilogNetwork verilog_network(sta_.get()); - - sta::VerilogReader verilog_reader(&verilog_network); - verilog_reader.read(getFilePath(file_name).c_str()); - - ord::dbLinkDesign(top.c_str(), - &verilog_network, - db_.get(), - &logger_, - /*hierarchy = */ false); - - sta_->postReadDb(db_.get()); - - sta::Cell* top_cell = network->cell(network->topInstance()); - sta::Port* clk_port = network->findPort(top_cell, "clk"); - sta::Pin* clk_pin = network->findPin(network->topInstance(), clk_port); - - sta::PinSet pinset(network); - pinset.insert(clk_pin); - - // 0.5ns - double period = sta_->units()->timeUnit()->userToSta(0.5); - sta::FloatSeq waveform; - waveform.push_back(0); - waveform.push_back(period / 2.0); - - sta_->makeClock("core_clock", - pinset, - /*add_to_pins=*/false, - /*period=*/period, - waveform, - /*comment=*/"", - /*mode=*/sta_->cmdMode()); - - sta_->ensureGraph(); - sta_->ensureLevelized(); - } std::map AbcLogicNetworkNameToPrimaryOutputIds( abc::Abc_Ntk_t* network) { @@ -131,17 +73,12 @@ class AbcTest : public tst::Fixture return primary_output_name_to_index; } - - sta::Unit* power_unit_; - sta::LibertyLibrary* library_; }; class AbcTestSky130 : public AbcTest { - void SetUp() override + void InitLibrary() override { - std::call_once(init_abc_flag, []() { abc::Abc_Start(); }); - library_ = readLiberty(kPrefix + "sky130/sky130_fd_sc_hd__ss_n40C_1v40.lib"); @@ -150,18 +87,13 @@ class AbcTestSky130 : public AbcTest tech, "sky130", kPrefix + "sky130/sky130hd_std_cell.lef"); sta_->postReadLef(tech, lib); - - sta::Units* units = library_->units(); - power_unit_ = units->powerUnit(); } }; class AbcTestAsap7 : public AbcTest { - void SetUp() override + void InitLibrary() override { - std::call_once(init_abc_flag, []() { abc::Abc_Start(); }); - std::array liberty_paths = {"asap7/asap7sc7p5t_AO_RVT_FF_nldm_211120.lib.gz", "asap7/asap7sc7p5t_INVBUF_RVT_FF_nldm_220122.lib.gz", @@ -179,9 +111,6 @@ class AbcTestAsap7 : public AbcTest tech, "asap7", kPrefix + "asap7/asap7sc7p5t_28_R_1x_220121a.lef"); sta_->postReadLef(tech, lib); - - sta::Units* units = library_->units(); - power_unit_ = units->powerUnit(); } }; diff --git a/src/cut/test/cpp/TestMockturtle.cc b/src/cut/test/cpp/TestMockturtle.cc new file mode 100644 index 00000000000..57da7cd5f85 --- /dev/null +++ b/src/cut/test/cpp/TestMockturtle.cc @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2023-2025, The OpenROAD Authors + +#include // NOLINT(modernize-deprecated-headers): for strdup() +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/abc/abc.h" +#include "base/main/abcapis.h" +#include "cut/abc_library_factory.h" +#include "cut/logic_cut.h" +#include "cut/logic_extractor.h" +#include "db_sta/dbReadVerilog.hh" +#include "db_sta/dbSta.hh" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "helper.h" +#include "lorina/common.hpp" +#include "map/mio/mio.h" +#include "map/scl/sclLib.h" +#include "misc/vec/vecPtr.h" +#include "mockturtle/algorithms/emap.hpp" +#include "mockturtle/io/genlib_reader.hpp" +#include "mockturtle/networks/aig.hpp" +#include "mockturtle/networks/block.hpp" +#include "mockturtle/utils/name_utils.hpp" +#include "mockturtle/utils/tech_library.hpp" +#include "mockturtle/views/names_view.hpp" +#include "mockturtle/views/topo_view.hpp" +#include "odb/db.h" +#include "odb/dbSet.h" +#include "odb/lefin.h" +#include "sta/Graph.hh" +#include "sta/Liberty.hh" +#include "sta/NetworkClass.hh" +#include "sta/SdcClass.hh" +#include "sta/Sta.hh" +#include "sta/Units.hh" +#include "sta/VerilogReader.hh" +#include "tst/fixture.h" +#include "utl/deleter.h" +#include "utl/unique_name.h" + +namespace abc { +Vec_Str_t* Abc_SclProduceGenlibStr(SC_Lib* p, + float Slew, + float Gain, + int nGatesMin, + int fUseAll, + int* pnCellCount); +} // namespace abc + +namespace cut { + +using cut::LogicCut; +using cut::LogicExtractorFactory; +using ::testing::Contains; +using ::testing::ElementsAre; +using ::testing::Pair; + +namespace { + +std::vector> GetAigFanins( + const mockturtle::aig_network& ntk, + mockturtle::aig_network::node node) +{ + std::vector> fanins; + ntk.foreach_fanin(node, [&](const auto& signal) { + fanins.emplace_back(signal.index, signal.complement); + }); + + return fanins; +} + +} // namespace + +class MockturtleTest : public CutFixture +{ + protected: + auto CreateTechLib() + { + cut::AbcLibraryFactory factory(&logger_); + factory.AddDbSta(sta_.get()); + auto abc_library = factory.BuildScl(); + auto lib = abc_library.get(); + int cell_count = 0; + auto* genlib_vec = abc::Abc_SclProduceGenlibStr( + lib, abc::Abc_SclComputeAverageSlew(lib), 200.0f, 0, true, &cell_count); + // ABC ends the file with '.end', but mockturtle doesn't like that + for (int i = 0; i < sizeof(".end\n\0"); i++) { + abc::Vec_StrPop(genlib_vec); + } + abc::Vec_StrPush(genlib_vec, '\0'); + auto* genlib_str = abc::Vec_StrArray(genlib_vec); + std::istringstream genlib(genlib_str); + abc::Vec_StrFree(genlib_vec); + + std::vector gates; + EXPECT_EQ(lorina::read_genlib(genlib, mockturtle::genlib_reader(gates)), + lorina::return_code::success); + + return mockturtle::tech_library<9u>(gates); + } +}; + +TEST_F(MockturtleTest, ExtractsAndGateCorrectly) +{ + auto tech_lib = CreateTechLib(); + LoadVerilog(kPrefix + "simple_and_gate_extract.v"); + + sta::Network* network = sta_->getDbNetwork(); + sta::Vertex* flop_input_vertex = nullptr; + for (sta::Vertex* vertex : sta_->endpoints()) { + if (std::string(vertex->name(network)) == "output_flop/D") { + flop_input_vertex = vertex; + } + } + EXPECT_NE(flop_input_vertex, nullptr); + + LogicExtractorFactory logic_extractor(sta_.get(), &logger_); + logic_extractor.AppendEndpoint(flop_input_vertex); + LogicCut cut = logic_extractor.BuildLogicCut(tech_lib); + + EXPECT_EQ(cut.cut_instances().size(), 1); + EXPECT_EQ(std::string(network->name(*cut.cut_instances().begin())), "_403_"); +} + +TEST_F(MockturtleTest, ExtractSideOutputsCorrectly) +{ + auto tech_lib = CreateTechLib(); + + LoadVerilog(kPrefix + "side_outputs_extract.v"); + + sta::Network* network = sta_->getDbNetwork(); + sta::Vertex* flop_input_vertex = nullptr; + for (sta::Vertex* vertex : sta_->endpoints()) { + if (std::string(vertex->name(network)) == "output_flop/D") { + flop_input_vertex = vertex; + } + } + EXPECT_NE(flop_input_vertex, nullptr); + + LogicExtractorFactory logic_extractor(sta_.get(), &logger_); + logic_extractor.AppendEndpoint(flop_input_vertex); + LogicCut cut = logic_extractor.BuildLogicCut(tech_lib); + + std::unordered_set primary_output_names; + for (sta::Net* net : cut.primary_outputs()) { + primary_output_names.insert(network->name(net)); + } + + // Since a single net feeds both of these outputs should expect just 1 output + EXPECT_EQ(cut.primary_outputs().size(), 1); + EXPECT_THAT(primary_output_names, Contains("flop_net")); +} + +TEST(MockturtleAigTest, CreateIteBuildsDeterministicIntermediateSignals) +{ + mockturtle::aig_network ntk; + + const auto cond = ntk.create_pi(); + const auto f_then = ntk.create_pi(); + const auto f_else = ntk.create_pi(); + + const auto ite = ntk.create_ite(cond, f_then, f_else); + + EXPECT_EQ(ntk.num_gates(), 3); + EXPECT_EQ(ite.index, 6); + EXPECT_EQ(ite.complement, 1); + + EXPECT_THAT(GetAigFanins(ntk, ntk.index_to_node(4)), + ElementsAre(Pair(1, 0), Pair(2, 0))); + EXPECT_THAT(GetAigFanins(ntk, ntk.index_to_node(5)), + ElementsAre(Pair(1, 1), Pair(3, 0))); + EXPECT_THAT(GetAigFanins(ntk, ntk.index_to_node(6)), + ElementsAre(Pair(4, 1), Pair(5, 1))); +} + +TEST(MockturtleAigTest, CreateMajBuildsDeterministicIntermediateSignals) +{ + mockturtle::aig_network ntk; + + const auto a = ntk.create_pi(); + const auto b = ntk.create_pi(); + const auto c = ntk.create_pi(); + + const auto maj = ntk.create_maj(a, b, c); + + EXPECT_EQ(ntk.num_gates(), 4); + EXPECT_EQ(maj.index, 7); + EXPECT_EQ(maj.complement, 1); + + EXPECT_THAT(GetAigFanins(ntk, ntk.index_to_node(4)), + ElementsAre(Pair(1, 0), Pair(2, 0))); + EXPECT_THAT(GetAigFanins(ntk, ntk.index_to_node(5)), + ElementsAre(Pair(1, 1), Pair(2, 1))); + EXPECT_THAT(GetAigFanins(ntk, ntk.index_to_node(6)), + ElementsAre(Pair(3, 0), Pair(5, 1))); + EXPECT_THAT(GetAigFanins(ntk, ntk.index_to_node(7)), + ElementsAre(Pair(4, 1), Pair(6, 1))); +} + +} // namespace cut diff --git a/src/cut/test/cpp/helper/BUILD b/src/cut/test/cpp/helper/BUILD new file mode 100644 index 00000000000..a4d88bbd036 --- /dev/null +++ b/src/cut/test/cpp/helper/BUILD @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2025, The OpenROAD Authors + +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +package( + features = ["layering_check"], +) + +cc_library( + name = "helper", + testonly = 1, + srcs = [ + "helper.cpp", + ], + hdrs = [ + "helper.h", + ], + includes = ["."], # not needed after no cmake + visibility = ["//visibility:public"], + deps = [ + "//src/cut", + "//src/dbSta", + "//src/dbSta:dbReadVerilog", + "//src/odb/src/db", + "//src/odb/src/lefin", + "//src/sta:opensta_lib", + "//src/tst", + "@abc", + ], +) diff --git a/src/cut/test/cpp/helper/CMakeLists.txt b/src/cut/test/cpp/helper/CMakeLists.txt new file mode 100644 index 00000000000..489d6e8f40f --- /dev/null +++ b/src/cut/test/cpp/helper/CMakeLists.txt @@ -0,0 +1,18 @@ +include(openroad) + + +add_library(cut_test_helper helper.cpp) + + +target_link_libraries(cut_test_helper + PRIVATE + cut + tst +) + + +target_include_directories(cut_test_helper + PUBLIC + ${CMAKE_CURRENT_LIST_DIR} +) + diff --git a/src/cut/test/cpp/helper/helper.cpp b/src/cut/test/cpp/helper/helper.cpp new file mode 100644 index 00000000000..b6ec9a19157 --- /dev/null +++ b/src/cut/test/cpp/helper/helper.cpp @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2026-2026, The OpenROAD Authors + +#include "helper.h" + +#include + +#include +#include +#include +#include + +#include "base/abc/abc.h" +#include "base/main/abcapis.h" +#include "db_sta/dbReadVerilog.hh" +#include "db_sta/dbSta.hh" +#include "odb/db.h" +#include "odb/lefin.h" +#include "sta/Liberty.hh" +#include "sta/NetworkClass.hh" +#include "sta/Sta.hh" +#include "sta/Units.hh" +#include "sta/VerilogReader.hh" + +namespace cut { + +std::once_flag CutFixture::init_abc_flag{}; +const std::string CutFixture::kPrefix = "_main/src/cut/test/"; + +void CutFixture::SetUp() +{ + std::call_once(init_abc_flag, []() { abc::Abc_Start(); }); + InitLibrary(); + + sta::Units* units = library_->units(); + power_unit_ = units->powerUnit(); +} + +void CutFixture::InitLibrary() +{ + library_ = readLiberty(kPrefix + "Nangate45/Nangate45_typ.lib"); + + odb::dbTech* tech + = loadTechLef("nangate45", kPrefix + "Nangate45/Nangate45_tech.lef"); + loadLibaryLef(tech, "nangate45", kPrefix + "Nangate45/Nangate45_stdcell.lef"); +} + +void CutFixture::LoadVerilog(const std::string& file_name, + const std::string& top) +{ + // Assumes module name is "top" and clock name is "clk" + sta::dbNetwork* network = sta_->getDbNetwork(); + ord::dbVerilogNetwork verilog_network(sta_.get()); + + sta::VerilogReader verilog_reader(&verilog_network); + verilog_reader.read(getFilePath(file_name).c_str()); + + ord::dbLinkDesign(top.c_str(), + &verilog_network, + db_.get(), + &logger_, + /*hierarchy = */ false); + + sta_->postReadDb(db_.get()); + + sta::Cell* top_cell = network->cell(network->topInstance()); + sta::Port* clk_port = network->findPort(top_cell, "clk"); + sta::Pin* clk_pin = network->findPin(network->topInstance(), clk_port); + + sta::PinSet pinset(network); + pinset.insert(clk_pin); + + // 0.5ns + double period = sta_->units()->timeUnit()->userToSta(0.5); + sta::FloatSeq waveform; + waveform.push_back(0); + waveform.push_back(period / 2.0); + + sta_->makeClock("core_clock", + pinset, + /*add_to_pins=*/false, + /*period=*/period, + waveform, + /*comment=*/"", + /*mode=*/sta_->cmdMode()); + + sta_->ensureGraph(); + sta_->ensureLevelized(); +} + +} // namespace cut diff --git a/src/cut/test/cpp/helper/helper.h b/src/cut/test/cpp/helper/helper.h new file mode 100644 index 00000000000..a64e1b3ea55 --- /dev/null +++ b/src/cut/test/cpp/helper/helper.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2026-2026, The OpenROAD Authors + +#include +#include + +#include "tst/fixture.h" + +namespace sta { +class Unit; +class LibertyLibrary; +}; // namespace sta + +namespace cut { + +class CutFixture : public tst::Fixture +{ + static std::once_flag init_abc_flag; + + protected: + static const std::string kPrefix; + void SetUp() override; + void LoadVerilog(const std::string& file_name, + const std::string& top = "top"); + virtual void InitLibrary(); + + sta::Unit* power_unit_; + sta::LibertyLibrary* library_; +}; + +} // namespace cut diff --git a/src/rmp/BUILD b/src/rmp/BUILD index 4e8ee46ea60..55e04365fc0 100644 --- a/src/rmp/BUILD +++ b/src/rmp/BUILD @@ -35,13 +35,23 @@ cc_library( cc_library( name = "optimization_strategies", srcs = [ + "src/annealing_strategy.cpp", "src/delay_optimization_strategy.cpp", + "src/emap_strategy.cpp", + "src/genetic_strategy.cpp", + "src/gia.cpp", + "src/slack_tuning_strategy.cpp", "src/zero_slack_strategy.cpp", ], hdrs = [ + "src/annealing_strategy.h", "src/delay_optimization_strategy.h", + "src/emap_strategy.h", + "src/genetic_strategy.h", + "src/gia.h", "src/logic_optimization_strategy.h", "src/resynthesis_strategy.h", + "src/slack_tuning_strategy.h", "src/zero_slack_strategy.h", ], defines = ["ABC_NAMESPACE=abc"], @@ -52,11 +62,15 @@ cc_library( "//src/cut", "//src/dbSta", "//src/dbSta:dbNetwork", + "//src/odb/src/db", "//src/rsz", "//src/sta:opensta_lib", "//src/utl", + "//third-party:mockturtle", "@abc", "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/hash", + "@abseil-cpp//absl/random:distributions", "@abseil-cpp//absl/synchronization", ], ) @@ -65,14 +79,6 @@ cc_library( name = "rmp", srcs = [ "src/Restructure.cpp", - "src/annealing_strategy.cpp", - "src/annealing_strategy.h", - "src/genetic_strategy.cpp", - "src/genetic_strategy.h", - "src/gia.cpp", - "src/gia.h", - "src/slack_tuning_strategy.cpp", - "src/slack_tuning_strategy.h", ], hdrs = [ "include/rmp/Restructure.h", @@ -84,7 +90,6 @@ cc_library( ], deps = [ ":optimization_strategies", - ":utils", "//src/cut", "//src/dbSta", "//src/dbSta:dbNetwork", @@ -93,8 +98,6 @@ cc_library( "//src/sta:opensta_lib", "//src/utl", "@abc", - "@abseil-cpp//absl/hash", - "@abseil-cpp//absl/random:distributions", ], ) diff --git a/src/rmp/README.md b/src/rmp/README.md index 6430557b9f0..264e7056ec1 100644 --- a/src/rmp/README.md +++ b/src/rmp/README.md @@ -30,7 +30,7 @@ Example: `restructure -liberty_file ckt.lib -target delay -tielo_pin ABC -tiehi_ restructure [-slack_threshold slack_val] [-depth_threshold depth_threshold] - [-target area|delay] + [-target area|timing] [-abc_logfile logfile] [-liberty_file liberty_file] [-tielo_port tielo_pin_name] @@ -43,7 +43,7 @@ restructure | Switch Name | Description | | ----- | ----- | | `-liberty_file` | Liberty file with description of cells used in design. This is passed to ABC. | -| `-target` | Either `area` or `delay`. In area mode, the focus is area reduction, and timing may degrade. In delay mode, delay is likely reduced, but the area may increase. The default value is `area`. | +| `-target` | Either `area` or `timing`. In area mode, the focus is area reduction, and timing may degrade. In timing mode, delay is likely reduced, but the area may increase. The default value is `area`. | | `-slack_threshold` | Specifies a (setup) timing slack value below which timing paths need to be analyzed for restructuring. The default value is `0`, and the allowed values are floats `[0, MAX_FLOAT]`. | | `-depth_threshold` | Specifies the path depth above which a timing path would be considered for restructuring. The default value is `16`, and the allowed values are `[0, MAX_INT]`. | | `-tielo_pin` | Tie cell pin that can drive constant zero. The format is `/`. | @@ -57,14 +57,14 @@ Resynthesize parts of the design in an attempt to fix negative slack. ```tcl resynth - [-corner corner] + [-scene scene] ``` #### Options | Switch Name | Description | | ----- | ----- | -| `-corner` | Process corner to use. | +| `-scene` | Process scene to use. | ### Resynth with simulated annealing @@ -75,7 +75,7 @@ The optimization function is defined as the worst slack. ```tcl resynth_annealing - [-corner corner] + [-scene scene] [-slack_threshold slack_threshold] [-seed seed] [-temp temp] @@ -88,7 +88,7 @@ resynth_annealing | Switch Name | Description | | ----- | ----- | -| `-corner` | Process corner to use. | +| `-scene` | Process scene to use. | | `-slack_threshold` | Specifies a (setup) timing slack value below which timing paths need to be analyzed for restructuring. The default value is `0`. | | `-seed` | Seed to use for randomness in simulated annealing. | | `-temp` | Initial temperature for simulated annealing. The default is the required arrival time on the worst slack endpoint. | @@ -107,7 +107,7 @@ The optimization function is defined as the worst slack. ```tcl resynth_genetic - [-corner corner] + [-scene scene] [-slack_threshold slack_threshold] [-seed seed] [-population_size population_size] @@ -123,7 +123,7 @@ resynth_genetic | Switch Name | Description | | ----- | ----- | -| `-corner` | Process corner to use. | +| `-scene` | Process scene to use. | | `-slack_threshold` | Specifies a (setup) timing slack value below which timing paths need to be analyzed for restructuring. The default value is `0`. | | `-seed` | Seed to use for randomness. | | `-population_size` | Population size. | @@ -135,6 +135,39 @@ resynth_genetic | `-initial_ops` | Size of the initial random solution (number of commands in the script for ABC). | +### Resynth with extended technology mapping + +Resynthesize parts of the design with `mockturtle` extended technology mapping. +The selected logic is exported through a logic cut, mapped with +[`emap`](https://mockturtle.readthedocs.io/en/latest/algorithms/mapper.html) +against the currently loaded library, and then imported back into OpenROAD. +With `-map_multioutput`, mapping can use multioutput cells such as adders. + +Currently, cells with up to 9 inputs are supported. + +```tcl +resynth_emap + [-scene scene] + [-map_multioutput] + [-verbose] + [-create_po_buffers] + [-insert_buffers] + [-min_drive_resistance min_drive_resistance] + [-max_drive_resistance max_drive_resistance] +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `-scene` | Process scene to use. | +| `-map_multioutput` | Map multioutput cells. | +| `-verbose` | Enable verbose output from `mockturtle` during mapping. | +| `-create_po_buffers` | Allow `mockturtle` to create buffers on primary outputs. | +| `-insert_buffers` | Allow `mockturtle` to insert buffers during mapping. | +| `-min_drive_resistance` | Keep only library cells whose average drive resistance is greater than or equal to the given threshold. Takes precedence over `-max_drive_resistance`. | +| `-max_drive_resistance` | Keep only library cells whose average drive resistance is less than or equal to the given threshold. | + ## Example scripts Example scripts on running `rmp` for a sample design of `gcd` as follows: diff --git a/src/rmp/include/rmp/Restructure.h b/src/rmp/include/rmp/Restructure.h index cb697371725..33402e924d9 100644 --- a/src/rmp/include/rmp/Restructure.h +++ b/src/rmp/include/rmp/Restructure.h @@ -66,7 +66,9 @@ class Restructure void reset(); void resynth(sta::Scene* corner); void resynthAnnealing(sta::Scene* corner); + void resynthEmap(sta::Scene* corner); void resynthGenetic(sta::Scene* corner); + void run(char* liberty_file_name, float slack_threshold, unsigned max_depth, @@ -81,6 +83,27 @@ class Restructure annealing_revert_after_ = revert_after; } void setAnnealingInitialOps(unsigned ops) { annealing_init_ops_ = ops; } + void setEmapCreatePoBuffers(bool create_po_buffers) + { + emap_create_po_buffers_ = create_po_buffers; + } + void setEmapInsertBuffers(bool insert_buffers) + { + emap_insert_buffers_ = insert_buffers; + } + void setEmapMapMultioutput(bool map_multioutput) + { + emap_map_multioutput_ = map_multioutput; + } + void setEmapVerbose(bool verbose) { emap_verbose_ = verbose; } + void setEmapMaxDriveResistance(double max_drive_resistance) + { + emap_max_drive_resistance_ = max_drive_resistance; + } + void setEmapMinDriveResistance(double min_drive_resistance) + { + emap_min_drive_resistance_ = min_drive_resistance; + } void setGeneticSeed(int seed) { genetic_seed_ = seed; } void setGeneticPopulationSize(unsigned population_size) { @@ -148,6 +171,15 @@ class Restructure std::optional annealing_revert_after_; unsigned annealing_init_ops_ = 10; + // Emap + + bool emap_create_po_buffers_ = false; + bool emap_insert_buffers_ = false; + bool emap_map_multioutput_ = false; + bool emap_verbose_ = false; + double emap_max_drive_resistance_ = 0.0; + double emap_min_drive_resistance_ = 0.0; + // Genetic int genetic_seed_ = 0; unsigned genetic_population_size_ = 4; diff --git a/src/rmp/src/CMakeLists.txt b/src/rmp/src/CMakeLists.txt index 67b4339822c..cff85a92e80 100644 --- a/src/rmp/src/CMakeLists.txt +++ b/src/rmp/src/CMakeLists.txt @@ -16,6 +16,7 @@ add_library(rmp_lib Restructure.cpp annealing_strategy.cpp delay_optimization_strategy.cpp + emap_strategy.cpp genetic_strategy.cpp gia.cpp slack_tuning_strategy.cpp @@ -54,6 +55,8 @@ target_include_directories(rmp_lib ) target_link_libraries(rmp_lib + PRIVATE + ${MOCKTURTLE_LIBRARY} PUBLIC odb dbSta_lib @@ -62,9 +65,10 @@ target_link_libraries(rmp_lib utl_lib cut ${ABC_LIBRARY} - absl::synchronization - absl::hash absl::city + absl::hash + absl::random_random + absl::synchronization ) target_link_libraries(rmp diff --git a/src/rmp/src/Restructure.cpp b/src/rmp/src/Restructure.cpp index 69ad28b0f8a..37c909b39e9 100644 --- a/src/rmp/src/Restructure.cpp +++ b/src/rmp/src/Restructure.cpp @@ -24,6 +24,7 @@ #include "cut/abc_init.h" #include "cut/blif.h" #include "db_sta/dbSta.hh" +#include "emap_strategy.h" #include "genetic_strategy.h" #include "odb/db.h" #include "rsz/Resizer.hh" @@ -721,4 +722,18 @@ bool Restructure::readAbcLog(const std::string& abc_file_name, } return status; } + +void Restructure::resynthEmap(sta::Scene* scene) +{ + EmapStrategy emap_strategy(scene, + emap_map_multioutput_, + emap_verbose_, + emap_create_po_buffers_, + emap_insert_buffers_, + emap_min_drive_resistance_, + emap_max_drive_resistance_); + + emap_strategy.OptimizeDesign(open_sta_, name_generator_, resizer_, logger_); +} + } // namespace rmp diff --git a/src/rmp/src/emap_strategy.cpp b/src/rmp/src/emap_strategy.cpp new file mode 100644 index 00000000000..687fba2bfa8 --- /dev/null +++ b/src/rmp/src/emap_strategy.cpp @@ -0,0 +1,760 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#include "emap_strategy.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "aig/aig/aig.h" +#include "base/abc/abc.h" +#include "cut/abc_library_factory.h" +#include "cut/logic_cut.h" +#include "cut/logic_extractor.h" +#include "db_sta/dbSta.hh" +#include "lorina/common.hpp" +#include "lorina/diagnostics.hpp" +#include "map/scl/sclLib.h" +#include "misc/vec/vecStr.h" +#include "mockturtle/algorithms/emap.hpp" +#include "mockturtle/io/genlib_reader.hpp" +#include "mockturtle/networks/aig.hpp" +#include "mockturtle/networks/block.hpp" +#include "mockturtle/utils/name_utils.hpp" +#include "mockturtle/utils/tech_library.hpp" +#include "mockturtle/views/names_view.hpp" +#include "mockturtle/views/topo_view.hpp" +#include "odb/db.h" +#include "odb/dbSet.h" +#include "sta/ConcreteLibrary.hh" +#include "sta/FuncExpr.hh" +#include "sta/GraphDelayCalc.hh" +#include "sta/Liberty.hh" +#include "sta/NetworkClass.hh" +#include "sta/Search.hh" +#include "utl/Logger.h" + +namespace abc { +Vec_Str_t* Abc_SclProduceGenlibStr(SC_Lib* p, + float Slew, + float Gain, + int nGatesMin, + int fUseAll, + int* pnCellCount); +} // namespace abc + +namespace rmp { + +std::tuple, cut::LogicCut> +EmapStrategy::ExtractLogicToMockturtle(sta::dbSta* sta, + sta::Scene* scene, + rsz::Resizer* resizer, + mockturtle::tech_library<9u>& tech_lib, + utl::Logger* logger) +{ + using abc::Abc_Ntk_t; + using abc::Abc_NtkDelete; + using abc::Aig_Man_t; + using abc::Aig_Obj_t; + + using aig_ntk = mockturtle::names_view; + + sta::dbNetwork* network = sta->getDbNetwork(); + + // Disable incremental timing. + sta->graphDelayCalc()->delaysInvalid(); + sta->search()->arrivalsInvalid(); + sta->search()->endpointsInvalid(); + + cut::LogicExtractorFactory logic_extractor(sta, logger); + for (sta::Vertex* endpoint : sta->endpoints()) { + logic_extractor.AppendEndpoint(endpoint); + } + + cut::LogicCut cut = logic_extractor.BuildLogicCut(tech_lib); + + aig_ntk ntk = mockturtle::names_view( + cut.BuildMappedMockturtleNetwork(tech_lib, network, logger)); + + return {ntk, cut}; +} + +std::vector EmapStrategy::GetSignalOutputs(odb::dbMaster* master) +{ + std::vector outs; + + for (auto* mterm : master->getMTerms()) { + auto io = mterm->getIoType(); + auto sig = mterm->getSigType(); + + if (sig == odb::dbSigType::POWER || sig == odb::dbSigType::GROUND) { + continue; + } + if (io == odb::dbIoType::INPUT) { + continue; + } + // Accept OUTPUT, INOUT, FEEDTHRU here as "outputs" + outs.push_back(mterm); + } + + auto by_name = [](odb::dbMTerm* a, odb::dbMTerm* b) { + return std::strcmp(a->getName().c_str(), b->getName().c_str()) < 0; + }; + std::stable_sort(outs.begin(), outs.end(), by_name); + + return outs; +} + +EmapStrategy::CellMapping EmapStrategy::MapCellFromStdCell( + const BlockNtk& ntk, + const BlockNtk::node& n, + utl::Logger* logger) +{ + CellMapping m; + + const auto& sc = ntk.get_cell(n); // cell_view API + m.master_name = sc.name; // must match Liberty/LEF cell name + + if (sc.gates.empty()) { + logger->error(utl::RMP, + 86, + "Standard cell {} has no gates (node {})", + sc.name, + ntk.node_to_index(n)); + } + + const auto& first_gate = sc.gates.front(); + for (const auto& p : first_gate.pins) { + m.input_pins.push_back(p.name); + } + + return m; +} + +std::optional +EmapStrategy::FindLibertyCellByMasterName(sta::Sta* sta, + const std::string& cell_name) +{ + auto* libs = sta->network()->libertyLibraryIterator(); + while (libs->hasNext()) { + auto lib = libs->next(); + if (auto* cell = lib->findCell(cell_name.c_str())) { + return cell->libertyCell(); + } + } + return std::nullopt; +} + +std::optional EmapStrategy::FindTieMaster( + sta::dbSta* sta, + bool value) +{ + std::vector candidates; + + for (const auto& lib : sta->db()->getLibs()) { + for (auto* master : lib->getMasters()) { + int sig_in = 0, sig_out = 0; + std::string out_name; + + for (auto* mt : master->getMTerms()) { + auto sig = mt->getSigType(); + if (sig == odb::dbSigType::POWER || sig == odb::dbSigType::GROUND) { + continue; + } + switch (mt->getIoType().getValue()) { + case odb::dbIoType::INPUT: + sig_in++; + break; + case odb::dbIoType::OUTPUT: + sig_out++; + out_name = mt->getName(); + break; + default: + // ignore INOUT/FEEDTHRU + break; + } + } + + if (sig_in != 0 || sig_out != 1) { + continue; + } + + // likely a constant cell + TieMaster tm = {.master = master, .out_pin = out_name}; + + auto lib_cell = FindLibertyCellByMasterName(sta, master->getName()); + if (!lib_cell.has_value()) { + continue; + } + auto* port_it = lib_cell.value()->portIterator(); + while (port_it->hasNext()) { + auto* port = port_it->next()->libertyPort(); + if (!port->direction()->isOutput()) { + continue; + } + auto* func = port->function(); + if ((value && func->op() == sta::FuncExpr::Op::one) + || (!value && func->op() == sta::FuncExpr::Op::zero)) { + return tm; + } + } + } + } + + return std::nullopt; +} + +odb::dbNet* EmapStrategy::EnsureConstNet(bool value, + odb::dbBlock* block, + sta::dbSta* sta, + utl::Logger* logger) +{ + odb::dbNet*& net = value ? net1_cache_ : net0_cache_; + + if (net) { + return net; + } + + const char* name = value ? "CONST1" : "CONST0"; + + net = block->findNet(name); + if (!net) { + net = odb::dbNet::create(block, name); + } + if (!net) { + logger->error(utl::RMP, 87, "Failed to create const net"); + } + + auto tie = FindTieMaster(sta, value); + if (!tie) { + logger->error( + utl::RMP, + 88, + "No supply net and no tie cell found; cannot create constant driver"); + } + + std::string inst_name = value ? "u_const1" : "u_const0"; + odb::dbInst* inst = block->findInst(inst_name.c_str()); + if (!inst) { + inst = odb::dbInst::create(block, tie->master, inst_name.c_str()); + } + if (!inst) { + logger->error(utl::RMP, 89, "Failed to create tie inst"); + } + + auto* out_it = inst->findITerm(tie->out_pin.c_str()); + if (!out_it) { + logger->error(utl::RMP, 90, "Tie output iterm not found"); + } + out_it->connect(net); + + if (value) { + net1_cache_ = net; + } else { + net0_cache_ = net; + } + + return net; +} + +void EmapStrategy::ImportMockturtleMappedNetwork(sta::dbSta* sta, + const BlockNtk& ntk, + cut::LogicCut& cut, + utl::Logger* logger) +{ + auto topo_ntk = mockturtle::topo_view(ntk); + + odb::dbChip* chip = sta->db()->getChip(); + odb::dbBlock* block = chip->getBlock(); + sta::dbNetwork* db_network = sta->getDbNetwork(); + + // Delete nets that only belong to the cut set. + std::unordered_set primary_input_or_output_nets; + primary_input_or_output_nets.insert(cut.primary_inputs().begin(), + cut.primary_inputs().end()); + primary_input_or_output_nets.insert(cut.primary_outputs().begin(), + cut.primary_outputs().end()); + + for (const sta::Instance* instance : cut.cut_instances()) { + auto pin_iterator = std::unique_ptr( + db_network->pinIterator(instance)); + while (pin_iterator->hasNext()) { + sta::Pin* pin = pin_iterator->next(); + sta::Net* connected_net = db_network->net(pin); + if (connected_net == nullptr) { + // This net is not connected to anything, so we cannot delete it. + // This can happen if you have an unconnected output port. + // For example one of Sky130's tie cell has both high and low outputs + // and only one is connected to a net. + continue; + } + // If pin isn't a primary input or output add to deleted list. The only + // way this can happen is if a net is only used within the cutset, and + // in that case we want to delete it. + if (!primary_input_or_output_nets.contains(connected_net)) { + db_network->deleteNet(connected_net); + } + } + db_network->deleteInstance(const_cast(instance)); + } + + // Add mapped network into design + const auto num_nodes = topo_ntk.size(); + std::vector> node_out_nets(num_nodes); + + auto node_output_signal = [&](const BlockNtk::node& n, uint32_t k) { + auto s = topo_ntk.make_signal(n); // output pin 0 + return mockturtle::signal{ + s.index, s.complement, static_cast(s.output) + k}; + }; + + // Primary inputs + topo_ntk.foreach_pi([&](const BlockNtk::node& n) { + const auto idx = topo_ntk.node_to_index(n); + + const std::string name = ntk.get_name(topo_ntk.make_signal(n)); + if (name.empty()) { + logger->error(utl::RMP, 71, "PI node {} has empty name", idx); + } + + odb::dbNet* net = block->findNet(name.c_str()); + if (!net) { + logger->error(utl::RMP, 72, "Failed to find PI net {}", name); + } + + node_out_nets[idx].push_back(net); + }); + + // Gates: create instances + output nets (no inputs connected yet) + topo_ntk.foreach_gate([&](const BlockNtk::node& n) { + const auto idx = topo_ntk.node_to_index(n); + + CellMapping mapping = MapCellFromStdCell(topo_ntk, n, logger); + odb::dbMaster* master = nullptr; + for (const auto& lib : sta->db()->getLibs()) { + master = lib->findMaster(mapping.master_name.c_str()); + if (master) { + break; + } + } + if (!master) { + logger->error(utl::RMP, 73, "Cannot find master {}", mapping.master_name); + } + + const std::string inst_name = fmt::format("n_{}", idx); + odb::dbInst* inst = odb::dbInst::create(block, master, inst_name.c_str()); + if (!inst) { + logger->error(utl::RMP, + 74, + "Failed to create dbInst {} for master {}", + inst_name, + mapping.master_name); + } + + const auto out_mterms = GetSignalOutputs(master); + const uint32_t num_cell_outputs = static_cast(out_mterms.size()); + const uint32_t num_node_outputs = topo_ntk.num_outputs(n); + + if (num_node_outputs == 0) { + // Shouldn't happen for mapped logic; skip + return; + } + + if (num_cell_outputs < num_node_outputs) { + logger->error( + utl::RMP, + 75, + "Cell {} has only {} signal outputs but node {} has {} outputs", + mapping.master_name, + num_cell_outputs, + idx, + num_node_outputs); + } + + node_out_nets[idx].resize(num_node_outputs); + + for (uint32_t out_pin_idx = 0; out_pin_idx < num_node_outputs; + ++out_pin_idx) { + odb::dbMTerm* o_mterm = out_mterms[out_pin_idx]; + const std::string pin_name = o_mterm->getName(); + + odb::dbITerm* o_iterm = inst->findITerm(pin_name.c_str()); + if (!o_iterm) { + logger->error(utl::RMP, + 76, + "Instance {} has no output ITerm {}", + inst_name, + pin_name); + } + + std::string net_name; + { + auto out_sig = node_output_signal(n, out_pin_idx); + if (ntk.has_name(out_sig)) { + net_name = ntk.get_name(out_sig); + } + } + if (net_name.empty()) { + net_name = fmt::format("n_{}_o_{}", idx, out_pin_idx); + } + + odb::dbNet* net = block->findNet(net_name.c_str()); + if (!net) { + net = odb::dbNet::create(block, net_name.c_str()); + } + if (!net) { + logger->error(utl::RMP, 77, "Failed to create/find net {}", net_name); + } + + o_iterm->connect(net); + node_out_nets[idx][out_pin_idx] = net; + } + }); + + // Connect gate inputs to driver nets + topo_ntk.foreach_gate([&](const BlockNtk::node& n) { + const auto idx = topo_ntk.node_to_index(n); + + CellMapping mapping = MapCellFromStdCell(topo_ntk, n, logger); + + const std::string inst_name = fmt::format("n_{}", idx); + odb::dbInst* inst = block->findInst(inst_name.c_str()); + if (!inst) { + logger->error(utl::RMP, 79, "Instance {} not found", inst_name); + } + + topo_ntk.foreach_fanin( + n, [&](const BlockNtk::signal& f, uint32_t fanin_idx) { + if (fanin_idx >= mapping.input_pins.size()) { + logger->error(utl::RMP, + 80, + "Not enough input pins in cell {} (node {}), " + "fanins={} inputs={}", + mapping.master_name, + idx, + fanin_idx, + mapping.input_pins.size()); + } + + odb::dbNet* src_net + = GetDriverNet(topo_ntk, block, sta, logger, node_out_nets, f); + const std::string& pin_name = mapping.input_pins[fanin_idx]; + odb::dbITerm* it = inst->findITerm(pin_name.c_str()); + if (!it) { + logger->error(utl::RMP, + 82, + "Master {} had no input ITerm {}", + mapping.master_name, + pin_name); + } + it->connect(src_net); + }); + }); + + // Connect mapped POs to the existing cut boundary nets + std::unordered_map boundary_po_dbnets; + for (sta::Net* sta_net : cut.primary_outputs()) { + odb::dbNet* db_net = db_network->staToDb(sta_net); + std::string net_name = db_network->name(sta_net); + if (!db_net) { + logger->error( + utl::RMP, 83, "cut primary output net {} has no dbNet", net_name); + } + boundary_po_dbnets.emplace(net_name, db_net); + } + + // Connect each mapped PO driver to corresponding boundary net by merging. + topo_ntk.foreach_po([&](const BlockNtk::signal& f, uint32_t po_index) { + std::string po_name = topo_ntk.get_output_name(po_index); + auto it = boundary_po_dbnets.find(po_name); + if (it == boundary_po_dbnets.end()) { + logger->error(utl::RMP, 84, "Missing boundary net for PO {}", po_name); + } + odb::dbNet* boundary_net = it->second; + + odb::dbNet* driver_net + = GetDriverNet(topo_ntk, block, sta, logger, node_out_nets, f); + if (driver_net && boundary_net && driver_net != boundary_net) { + driver_net->mergeNet(boundary_net); + } + }); +} + +template +odb::dbNet* EmapStrategy::GetDriverNet( + mockturtle::topo_view& topo_ntk, + odb::dbBlock* block, + sta::dbSta* sta, + utl::Logger* logger, + std::vector>& node_out_nets, + const BlockNtk::signal& f) +{ + auto src_node = topo_ntk.get_node(f); + + if (topo_ntk.is_constant(src_node)) { + auto get_constant_value = [topo_ntk, logger](const BlockNtk::node& src_node, + const BlockNtk::signal& f) { + auto gate = topo_ntk.get_cell(src_node).gates[topo_ntk.get_output_pin(f)]; + bool value; + if (gate.expression == "CONST1") { + value = true; + } else if (gate.expression == "CONST0") { + value = false; + } else { + logger->error( + utl::RMP, 78, "Unknown constant expression: {}", gate.expression); + } + return value ^ topo_ntk.is_complemented(f); + }; + + bool value = get_constant_value(src_node, f); + return EnsureConstNet(value, block, sta, logger); + } + const uint32_t src_idx = topo_ntk.node_to_index(src_node); + const uint32_t out_pin_idx + = topo_ntk.is_multioutput(src_node) ? topo_ntk.get_output_pin(f) : 0; + + if (src_idx >= node_out_nets.size() + || out_pin_idx >= node_out_nets[src_idx].size() + || node_out_nets[src_idx][out_pin_idx] == nullptr) { + logger->error(utl::RMP, + 85, + "Missing driver net for src_idx={}, out_pin_idx={}", + src_idx, + out_pin_idx); + } + return node_out_nets[src_idx][out_pin_idx]; +} + +static void FilterDriverResistance(std::vector& gates, + sta::dbSta* sta, + utl::Logger* logger, + double min_drive_resistance) +{ + sta::dbNetwork* const network = sta->getDbNetwork(); + std::erase_if( + gates, [network, logger, min_drive_resistance](const auto& gate) { + sta::LibertyCell* const cell = network->findLibertyCell(gate.name); + if (!cell) { + logger->info(utl::RMP, 2221, "Should have found cell {}", gate.name); + return false; + } + sta::LibertyCellPortIterator port_iter(cell); + float drive_resistance_sum = 0.0; + size_t ports = 0; + while (port_iter.hasNext()) { + sta::LibertyPort* port = port_iter.next(); + drive_resistance_sum += port->driveResistance(); + ++ports; + } + if (ports == 0) { + return false; + } + const float drive_resistance = drive_resistance_sum / ports; + if (drive_resistance >= min_drive_resistance) { + logger->info(utl::RMP, + 2281, + "Accepting cell {} resistance={}, threshold={}", + gate.name, + drive_resistance, + min_drive_resistance); + } else { + logger->info(utl::RMP, + 2261, + "Rejecting cell {} resistance={}, threshold={}", + gate.name, + drive_resistance, + min_drive_resistance); + } + return drive_resistance < min_drive_resistance; + }); + + // Fixup gate ids for mockturtle + for (size_t i = 0; i < gates.size(); ++i) { + gates[i].id = i; + } +} + +static void FilterDriverResistanceMax(std::vector& gates, + sta::dbSta* sta, + utl::Logger* logger, + double max_drive_resistance) +{ + sta::dbNetwork* const network = sta->getDbNetwork(); + std::erase_if( + gates, [network, logger, max_drive_resistance](const auto& gate) { + sta::LibertyCell* const cell = network->findLibertyCell(gate.name); + if (!cell) { + logger->info(utl::RMP, 2231, "Should have found cell {}", gate.name); + return false; + } + sta::LibertyCellPortIterator port_iter(cell); + float drive_resistance_sum = 0.0; + size_t ports = 0; + while (port_iter.hasNext()) { + sta::LibertyPort* port = port_iter.next(); + drive_resistance_sum += port->driveResistance(); + ++ports; + } + if (ports == 0) { + return false; + } + const float drive_resistance = drive_resistance_sum / ports; + if (drive_resistance <= max_drive_resistance) { + logger->info(utl::RMP, + 2241, + "Accepting cell {} resistance={}, threshold={}", + gate.name, + drive_resistance, + max_drive_resistance); + } else { + logger->info(utl::RMP, + 2251, + "Rejecting cell {} resistance={}, threshold={}", + gate.name, + drive_resistance, + max_drive_resistance); + } + return drive_resistance >= max_drive_resistance; + }); + + // Fixup gate ids for mockturtle + for (size_t i = 0; i < gates.size(); ++i) { + gates[i].id = i; + } +} + +void EmapStrategy::OptimizeDesign(sta::dbSta* sta, + utl::UniqueName& name_generator, + rsz::Resizer* resizer, + utl::Logger* logger) +{ + sta->ensureGraph(); + sta->ensureLevelized(); + sta->searchPreamble(); + for (auto mode : sta->modes()) { + sta->ensureClkNetwork(mode); + } + + // Configure emap parameters + mockturtle::emap_params ps; + + ps.area_oriented_mapping = true; + ps.create_po_buffers = create_po_buffers_; + ps.insert_buffers = insert_buffers_; + ps.map_multioutput = map_multioutput_; + ps.verbose = verbose_; + + // Read genlib + std::vector gates; + { + cut::AbcLibraryFactory factory(logger); + factory.AddDbSta(sta); + factory.AddResizer(resizer); + factory.SetScene(scene_); + auto abc_library = factory.BuildScl(); + auto lib = abc_library.get(); + int cell_count = 0; + auto* genlib_vec = abc::Abc_SclProduceGenlibStr( + lib, abc::Abc_SclComputeAverageSlew(lib), 200.0f, 0, true, &cell_count); + // ABC ends the file with '.end', but mockturtle doesn't like that + for (int i = 0; i < sizeof(".end\n\0"); i++) { + abc::Vec_StrPop(genlib_vec); + } + abc::Vec_StrPush(genlib_vec, '\0'); + auto* genlib_str = abc::Vec_StrArray(genlib_vec); + std::istringstream genlib(genlib_str); + abc::Vec_StrFree(genlib_vec); + + class diagnostic_consumer : public lorina::diagnostic_consumer + { + public: + diagnostic_consumer(utl::Logger* logger) : logger_(logger) {} + + void handle_diagnostic(lorina::diagnostic_level level, + const std::string& message) const override + { + switch (level) { + case lorina::diagnostic_level::ignore: + break; + case lorina::diagnostic_level::note: + case lorina::diagnostic_level::remark: + logger_->info(utl::RMP, 424, "{}", message); + break; + case lorina::diagnostic_level::warning: + logger_->warn(utl::RMP, 427, "{}", message); + break; + case lorina::diagnostic_level::error: + logger_->error(utl::RMP, 430, "{}", message); + break; + case lorina::diagnostic_level::fatal: + logger_->critical(utl::RMP, 433, "{}", message); + break; + } + } + + private: + utl::Logger* logger_; + }; + + diagnostic_consumer diag_consumer(logger); + lorina::diagnostic_engine diag_engine(&diag_consumer); + + auto code = lorina::read_genlib( + genlib, mockturtle::genlib_reader(gates), &diag_engine); + + if (code != lorina::return_code::success) { + logger->report("Error reading genlib file"); + return; + } + } + if (min_drive_resistance_ != 0) { + FilterDriverResistance(gates, sta, logger, min_drive_resistance_); + } else if (max_drive_resistance_ != 0) { + FilterDriverResistanceMax(gates, sta, logger, max_drive_resistance_); + } + + mockturtle::tech_library<9u> tech_lib(gates); + + // Create mockturtle AIG network + auto [ntk, cut] + = ExtractLogicToMockturtle(sta, scene_, resizer, tech_lib, logger); + + // Extended technology mapping statistics + mockturtle::emap_stats st; + + // Run emap + auto mapped_ntk + = mockturtle::names_view(mockturtle::emap(ntk, tech_lib, ps, &st)); + + // Restore priamry IO names + mockturtle::restore_pio_names_by_order(ntk, mapped_ntk); + + // Print statitstics + logger->report( + "Extended technology mapping stats:\n\tarea: {:.3f}\n\tdelay: " + "{:.3f}\n\tpower: {:.3f}\n\tinverters: {}\n\tmultioutput gates: {}\n", + st.area, + st.delay, + st.power, + st.inverters, + st.multioutput_gates); + + mapped_ntk.report_cells_usage(); + mapped_ntk.report_stats(); + + // Clear const network cache + net0_cache_ = nullptr; + net1_cache_ = nullptr; + + ImportMockturtleMappedNetwork(sta, mapped_ntk, cut, logger); + + sta->db()->triggerPostReadDb(); +} + +} // namespace rmp diff --git a/src/rmp/src/emap_strategy.h b/src/rmp/src/emap_strategy.h new file mode 100644 index 00000000000..4dd3669513f --- /dev/null +++ b/src/rmp/src/emap_strategy.h @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#include "cut/logic_cut.h" +#include "db_sta/dbSta.hh" +#include "mockturtle/networks/aig.hpp" +#include "mockturtle/networks/block.hpp" +#include "mockturtle/utils/tech_library.hpp" +#include "mockturtle/views/cell_view.hpp" +#include "mockturtle/views/names_view.hpp" +#include "resynthesis_strategy.h" +#include "rsz/Resizer.hh" +#include "sta/Scene.hh" +#include "utl/Logger.h" + +namespace rmp { + +class EmapStrategy : public ResynthesisStrategy +{ + using BlockNtk = mockturtle::names_view< + mockturtle::cell_view>; + + public: + explicit EmapStrategy(sta::Scene* scene, + bool map_multioutput, + bool verbose, + bool create_po_buffers, + bool insert_buffers, + double min_drive_resistance, + double max_drive_resistance) + : scene_(scene), + map_multioutput_(map_multioutput), + verbose_(verbose), + create_po_buffers_(create_po_buffers), + insert_buffers_(insert_buffers), + min_drive_resistance_(min_drive_resistance), + max_drive_resistance_(max_drive_resistance) + { + } + + void OptimizeDesign(sta::dbSta* sta, + utl::UniqueName& name_generator, + rsz::Resizer* resizer, + utl::Logger* logger) override; + + private: + // Mapping info for one cell instance + struct CellMapping + { + std::string master_name; // dbMaster / Liberty cell name + std::vector input_pins; // in fanin order (from gate::pins) + }; + + struct TieMaster + { + odb::dbMaster* master = nullptr; + std::string out_pin; + }; + + std::tuple, cut::LogicCut> + ExtractLogicToMockturtle(sta::dbSta* sta, + sta::Scene* scene, + rsz::Resizer* resizer, + mockturtle::tech_library<9u>& tech_lib, + utl::Logger* logger); + + template + odb::dbNet* GetDriverNet(mockturtle::topo_view& topo_ntk, + odb::dbBlock* block, + sta::dbSta* sta, + utl::Logger* logger, + std::vector>& node_out_nets, + const BlockNtk::signal& f); + + std::vector GetSignalOutputs(odb::dbMaster* master); + + CellMapping MapCellFromStdCell(const BlockNtk& ntk, + const BlockNtk::node& n, + utl::Logger* logger); + + std::optional FindLibertyCellByMasterName( + sta::Sta* sta, + const std::string& cell_name); + + std::optional FindTieMaster(sta::dbSta* sta, bool value); + + odb::dbNet* EnsureConstNet(bool value, + odb::dbBlock* block, + sta::dbSta* sta, + utl::Logger* logger); + + void ImportMockturtleMappedNetwork(sta::dbSta* sta, + const BlockNtk& ntk, + cut::LogicCut& cut, + utl::Logger* logger); + + sta::Scene* scene_; + bool map_multioutput_; + bool verbose_; + bool create_po_buffers_; + bool insert_buffers_; + double min_drive_resistance_; + double max_drive_resistance_; + + // Cached const networks for mapped network import + odb::dbNet* net0_cache_ = nullptr; + odb::dbNet* net1_cache_ = nullptr; +}; + +} // namespace rmp diff --git a/src/rmp/src/rmp.i b/src/rmp/src/rmp.i index 68acfbfa5ab..b09e0313148 100644 --- a/src/rmp/src/rmp.i +++ b/src/rmp/src/rmp.i @@ -78,6 +78,42 @@ set_annealing_initial_ops(int set_annealing_initial_ops) getRestructure()->setAnnealingInitialOps(set_annealing_initial_ops); } +void +set_emap_create_po_buffers(bool create_po_buffers) +{ + getRestructure()->setEmapCreatePoBuffers(create_po_buffers); +} + +void +set_emap_insert_buffers(bool insert_buffers) +{ + getRestructure()->setEmapInsertBuffers(insert_buffers); +} + +void +set_emap_map_multioutput(bool map_multioutput) +{ + getRestructure()->setEmapMapMultioutput(map_multioutput); +} + +void +set_emap_verbose(bool verbose) +{ + getRestructure()->setEmapVerbose(verbose); +} + +void +set_emap_max_drive_resistance(double max_drive_resistance) +{ + getRestructure()->setEmapMaxDriveResistance(max_drive_resistance); +} + +void +set_emap_min_drive_resistance(double min_drive_resistance) +{ + getRestructure()->setEmapMinDriveResistance(min_drive_resistance); +} + void set_genetic_seed(int genetic_seed) { @@ -91,7 +127,7 @@ set_genetic_population_size(int genetic_population_size) } void - set_genetic_mutation_probability(float genetic_mutation_probability) +set_genetic_mutation_probability(float genetic_mutation_probability) { getRestructure()->setGeneticMutationProbability(genetic_mutation_probability); } @@ -126,15 +162,21 @@ set_genetic_initial_ops(int genetic_initial_ops) getRestructure()->setGeneticInitialOps(genetic_initial_ops); } -void resynth_cmd(Scene* corner) { - getRestructure()->resynth(corner); +void +resynth_cmd(Scene* scene) +{ + getRestructure()->resynth(scene); } -void resynth_annealing_cmd(Scene* corner) { - getRestructure()->resynthAnnealing(corner); +void +resynth_annealing_cmd(Scene* scene) +{ + getRestructure()->resynthAnnealing(scene); } -void resynth_genetic_cmd(Scene* corner) { +void +resynth_genetic_cmd(Scene* corner) +{ getRestructure()->resynthGenetic(corner); } @@ -164,4 +206,10 @@ int blif_read(cut::Blif* blif_, const char* file_name){ return blif_->readBlif(file_name, getOpenRoad()->getDb()->getChip()->getBlock()); } +void +resynth_emap_cmd(Scene* scene) +{ + getRestructure()->resynthEmap(scene); +} + %} diff --git a/src/rmp/src/rmp.tcl b/src/rmp/src/rmp.tcl index 91c8ef58385..5b620cdcf7c 100644 --- a/src/rmp/src/rmp.tcl +++ b/src/rmp/src/rmp.tcl @@ -105,18 +105,18 @@ proc restructure { args } { $depth_threshold_value $workdir_name $abc_logfile } -sta::define_cmd_args "resynth" {[-corner corner]} +sta::define_cmd_args "resynth" {[-scene scene]} proc resynth { args } { sta::parse_key_args "resynth" args \ - keys {-corner} \ + keys {-scene} \ flags {} - set corner [sta::parse_scene keys] - rmp::resynth_cmd $corner + set scene [sta::parse_scene keys] + rmp::resynth_cmd $scene } sta::define_cmd_args "resynth_annealing" { - [-corner corner] + [-scene scene] [-slack_threshold slack_threshold] [-seed seed] [-temp temp] @@ -127,10 +127,10 @@ sta::define_cmd_args "resynth_annealing" { proc resynth_annealing { args } { sta::parse_key_args "resynth_annealing" args \ - keys {-corner -iters -revert_after -seed -temp -initial_ops -slack_threshold} \ + keys {-scene -iters -revert_after -seed -temp -initial_ops -slack_threshold} \ flags {} - set corner [sta::parse_scene keys] + set scene [sta::parse_scene keys] if { [info exists keys(-slack_threshold)] } { rmp::set_slack_threshold $keys(-slack_threshold) } @@ -150,11 +150,41 @@ proc resynth_annealing { args } { rmp::set_annealing_initial_ops $keys(-initial_ops) } - rmp::resynth_annealing_cmd $corner + rmp::resynth_annealing_cmd $scene +} + +sta::define_cmd_args "resynth_emap" { + [-scene scene] + [-map_multioutput] + [-verbose] + [-create_po_buffers] + [-insert_buffers] + [-min_drive_resistance min_drive_resistance] + [-max_drive_resistance max_drive_resistance] + } + +proc resynth_emap { args } { + sta::parse_key_args "resynth_emap" args \ + keys {-scene -min_drive_resistance -max_drive_resistance} \ + flags {-map_multioutput -verbose -create_po_buffers -insert_buffers} + + set scene [sta::parse_scene keys] + rmp::set_emap_map_multioutput [info exists flags(-map_multioutput)] + rmp::set_emap_create_po_buffers [info exists flags(-create_po_buffers)] + rmp::set_emap_insert_buffers [info exists flags(-insert_buffers)] + rmp::set_emap_verbose [info exists flags(-verbose)] + if { [info exists keys(-min_drive_resistance)] } { + rmp::set_emap_min_drive_resistance $keys(-min_drive_resistance) + } + if { [info exists keys(-max_drive_resistance)] } { + rmp::set_emap_max_drive_resistance $keys(-max_drive_resistance) + } + + rmp::resynth_emap_cmd $scene } sta::define_cmd_args "resynth_genetic" { - [-corner corner] + [-scene scene] [-slack_threshold slack_threshold] [-seed seed] [-population_size population_size] @@ -168,11 +198,11 @@ sta::define_cmd_args "resynth_genetic" { proc resynth_genetic { args } { sta::parse_key_args "resynth_genetic" args \ - keys {-corner -iters -seed -population_size -mutation_probability -crossover_probability \ + keys {-scene -iters -seed -population_size -mutation_probability -crossover_probability \ -tournament_size -tournament_probability -initial_ops -slack_threshold} \ flags {} - set corner [sta::parse_scene keys] + set scene [sta::parse_scene keys] if { [info exists keys(-slack_threshold)] } { rmp::set_slack_threshold $keys(-slack_threshold) } @@ -201,5 +231,5 @@ proc resynth_genetic { args } { rmp::set_genetic_initial_ops $keys(-initial_ops) } - rmp::resynth_genetic_cmd $corner + rmp::resynth_genetic_cmd $scene } diff --git a/src/rmp/src/slack_tuning_strategy.cpp b/src/rmp/src/slack_tuning_strategy.cpp index ea153e8f0fe..0e6d351518a 100644 --- a/src/rmp/src/slack_tuning_strategy.cpp +++ b/src/rmp/src/slack_tuning_strategy.cpp @@ -168,7 +168,7 @@ void SlackTuningStrategy::OptimizeDesign(sta::dbSta* sta, cut::AbcLibraryFactory factory(logger); factory.AddDbSta(sta); factory.AddResizer(resizer); - factory.SetCorner(corner_); + factory.SetScene(corner_); cut::AbcLibrary abc_library = factory.Build(); std::vector all_ops = GiaOps(logger); diff --git a/src/rmp/src/zero_slack_strategy.cpp b/src/rmp/src/zero_slack_strategy.cpp index 157dad261fb..00c21b0063c 100644 --- a/src/rmp/src/zero_slack_strategy.cpp +++ b/src/rmp/src/zero_slack_strategy.cpp @@ -50,7 +50,7 @@ void ZeroSlackStrategy::OptimizeDesign(sta::dbSta* sta, cut::AbcLibraryFactory factory(logger); factory.AddDbSta(sta); factory.AddResizer(resizer); - factory.SetCorner(corner_); + factory.SetScene(corner_); cut::AbcLibrary abc_library = factory.Build(); // Disable incremental timing. diff --git a/src/rmp/test/BUILD b/src/rmp/test/BUILD index fb868faffe4..159a7556528 100644 --- a/src/rmp/test/BUILD +++ b/src/rmp/test/BUILD @@ -12,6 +12,11 @@ package(features = ["layering_check"]) # From CMakeLists.txt or_integration_tests(TESTS COMPULSORY_TESTS = [ + "aes_annealing", + "aes_asap7", + "aes_donttouch_nangate45", + "aes_dontuse_nangate45", + "aes_genetic", "aes_nangate45", "blif_reader", "blif_reader_const", @@ -22,17 +27,14 @@ COMPULSORY_TESTS = [ "blif_writer_input_output", "blif_writer_sequential", "const_cell_removal", - "gcd_restructure", - "aes_asap7", - "gcd_asap7", - "memory_nangate45", - "aes_dontuse_nangate45", - "aes_donttouch_nangate45", - "aes_annealing", + "emap_aes_asap7", + "emap_gcd_asap7", "gcd_annealing1", "gcd_annealing2", - "aes_genetic", + "gcd_asap7", "gcd_genetic", + "gcd_restructure", + "memory_nangate45", ] ALL_TESTS = COMPULSORY_TESTS @@ -102,6 +104,7 @@ filegroup( "rcon.def", "rcon.sdc", "sky130/sky130_fd_sc_hd__ss_n40C_1v40.lib", + "sky130/sky130hd.rc", "sky130/sky130hd.tlef", "sky130/sky130hd_std_cell.lef", ], diff --git a/src/rmp/test/CMakeLists.txt b/src/rmp/test/CMakeLists.txt index 9475be34d07..7f0439f4db7 100644 --- a/src/rmp/test/CMakeLists.txt +++ b/src/rmp/test/CMakeLists.txt @@ -11,6 +11,8 @@ or_integration_tests( blif_writer_input_output blif_writer_sequential const_cell_removal + emap_aes_asap7 + emap_gcd_asap7 gcd_restructure aes_asap7 gcd_asap7 diff --git a/src/rmp/test/aes_annealing.tcl b/src/rmp/test/aes_annealing.tcl index 099dec66400..a2c5727048a 100644 --- a/src/rmp/test/aes_annealing.tcl +++ b/src/rmp/test/aes_annealing.tcl @@ -28,7 +28,7 @@ report_tns puts "-- After --\n" -resynth_annealing -corner fast -initial_ops 5 -iters 30 -seed 189 +resynth_annealing -scene fast -initial_ops 5 -iters 30 -seed 189 report_timing_histogram report_cell_usage report_checks diff --git a/src/rmp/test/aes_asap7.tcl b/src/rmp/test/aes_asap7.tcl index 61f2ab2fd60..91e0d54e409 100644 --- a/src/rmp/test/aes_asap7.tcl +++ b/src/rmp/test/aes_asap7.tcl @@ -28,14 +28,14 @@ report_tns puts "-- After --\n" -resynth -corner fast +resynth -scene fast report_timing_histogram report_cell_usage report_checks report_wns report_tns -resynth -corner fast +resynth -scene fast report_timing_histogram report_cell_usage report_checks diff --git a/src/rmp/test/aes_genetic.tcl b/src/rmp/test/aes_genetic.tcl index 54e0786fd2d..083a6dfaabb 100644 --- a/src/rmp/test/aes_genetic.tcl +++ b/src/rmp/test/aes_genetic.tcl @@ -28,7 +28,7 @@ report_tns puts "-- After --\n" resynth_genetic \ - -corner slow \ + -scene slow \ -initial_ops 5 \ -iters 15 \ -population_size 50 \ diff --git a/src/rmp/test/emap_aes_asap7.ok b/src/rmp/test/emap_aes_asap7.ok new file mode 100644 index 00000000000..f4fbc082c3a --- /dev/null +++ b/src/rmp/test/emap_aes_asap7.ok @@ -0,0 +1,297 @@ +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13156, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13189, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13222, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13255, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13288, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13321, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13354, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 14748, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 14781, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 14814, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13178, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13211, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13244, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13277, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13310, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13343, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13376, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 14772, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 14805, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 14838, timing group from output port. +[INFO ODB-0227] LEF file: ./asap7/asap7_tech_1x_201209.lef, created 30 layers, 9 vias +[INFO ODB-0227] LEF file: ./asap7/asap7sc7p5t_28_R_1x_220121a.lef, created 212 library cells +[WARNING STA-0441] set_input_delay relative to a clock defined on the same port/pin not allowed. +-- Before -- + +Cell type report: Count Area + Buffer 1081 78.80 + Inverter 458 20.03 + Sequential cell 562 163.88 + Multi-Input combinational cell 9806 1050.62 + Total 11907 1313.34 +[-30.917, 54.906): ***** (21) +[ 54.906, 140.728): ********************** (92) +[140.728, 226.551): *********** (47) +[226.551, 312.373): ************************************************** (212) +[312.373, 398.196): ******* (29) +[398.196, 484.018): **** (19) +[484.018, 569.841): ** (10) +[569.841, 655.664): * (3) +[655.664, 741.486): ****************************** (129) +[741.486, 827.309]: ****************************** (129) +Startpoint: ld (input port clocked by core_clock) +Endpoint: u0/subword[19]$_DFF_P_ + (rising edge-triggered flip-flop clocked by core_clock) +Path Group: core_clock +Path Type: max +Corner: slow + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock core_clock (rise edge) + 0.00 0.00 clock network delay (ideal) + 200.00 200.00 ^ input external delay + 0.00 200.00 ^ ld (in) + 43.55 243.55 ^ _12054_/Y (BUFx2_ASAP7_75t_R) + 61.28 304.83 ^ _12066_/Y (BUFx2_ASAP7_75t_R) + 61.08 365.91 ^ _12067_/Y (BUFx2_ASAP7_75t_R) + 48.16 414.07 ^ _12197_/Y (OA21x2_ASAP7_75t_R) + 65.83 479.90 ^ _12198_/Y (BUFx2_ASAP7_75t_R) + 78.77 558.68 v _13022_/Y (INVx1_ASAP7_75t_R) + 85.51 644.19 ^ _23139_/CON (HAxp5_ASAP7_75t_R) + 39.37 683.56 v _23139_/SN (HAxp5_ASAP7_75t_R) + 56.95 740.51 v _13045_/Y (BUFx2_ASAP7_75t_R) + 53.72 794.23 v _13111_/Y (OA21x2_ASAP7_75t_R) + 46.41 840.64 v _13289_/Y (AO221x1_ASAP7_75t_R) + 52.20 892.84 v _13290_/Y (OA211x2_ASAP7_75t_R) + 42.82 935.66 v _13295_/Y (OR3x1_ASAP7_75t_R) + 52.15 987.81 v _13296_/Y (OA211x2_ASAP7_75t_R) + 33.57 1021.38 v _13297_/Y (AO21x1_ASAP7_75t_R) + 0.00 1021.38 v u0/subword[19]$_DFF_P_/D (DFFHQNx1_ASAP7_75t_R) + 1021.38 data arrival time + +1000.00 1000.00 clock core_clock (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ u0/subword[19]$_DFF_P_/CLK (DFFHQNx1_ASAP7_75t_R) + -9.54 990.46 library setup time + 990.46 data required time +--------------------------------------------------------- + 990.46 data required time + -1021.38 data arrival time +--------------------------------------------------------- + -30.92 slack (VIOLATED) + + +wns max -30.92 +tns max -191.30 +-- After emap -- + +[i] WARNING: library does not contain cells that can implement output pin 1 of the multi-output cell FAx1_ASAP7_75t_R +[i] WARNING: library does not contain cells that could match the delay of output pin 1 of multi-output cell HAxp5_ASAP7_75t_R +[i] WARNING: technology mapping using multi-output cells with warnings might generate required time violations or circuits with dangling pins +[i] WARNING: failed to compute topological order for multi-output matching, falling back to non-topological order +Extended technology mapping stats: + area: 808.160 + delay: 328.390 + power: 0.000 + inverters: 0 + multioutput gates: 0 + +[i] Report cells usage: +[i] A2O1A1Ixp33_ASAP7_75t_R Instance = 399 Area = 35.91 4.44 % +[i] A2O1A1O1Ixp25_ASAP7_75t_R Instance = 49 Area = 6.37 0.79 % +[i] AO21x1_ASAP7_75t_R Instance = 166 Area = 14.94 1.85 % +[i] AO22x1_ASAP7_75t_R Instance = 3 Area = 0.39 0.05 % +[i] AOI211xp5_ASAP7_75t_R Instance = 297 Area = 26.73 3.31 % +[i] AOI21xp33_ASAP7_75t_R Instance = 1805 Area = 126.35 15.63 % +[i] AOI221xp5_ASAP7_75t_R Instance = 17 Area = 1.70 0.21 % +[i] AOI22xp33_ASAP7_75t_R Instance = 49 Area = 4.41 0.55 % +[i] AOI311xp33_ASAP7_75t_R Instance = 95 Area = 9.50 1.18 % +[i] AOI31xp33_ASAP7_75t_R Instance = 387 Area = 34.83 4.31 % +[i] AOI321xp33_ASAP7_75t_R Instance = 10 Area = 1.20 0.15 % +[i] AOI32xp33_ASAP7_75t_R Instance = 19 Area = 1.90 0.24 % +[i] INVx1_ASAP7_75t_R Instance = 1085 Area = 43.40 5.37 % +[i] O2A1O1Ixp33_ASAP7_75t_R Instance = 402 Area = 36.18 4.48 % +[i] OA211x2_ASAP7_75t_R Instance = 16 Area = 1.92 0.24 % +[i] OA21x2_ASAP7_75t_R Instance = 80 Area = 8.00 0.99 % +[i] OAI211xp5_ASAP7_75t_R Instance = 315 Area = 28.35 3.51 % +[i] OAI21xp33_ASAP7_75t_R Instance = 1543 Area = 108.01 13.36 % +[i] OAI221xp5_ASAP7_75t_R Instance = 15 Area = 1.50 0.19 % +[i] OAI22xp33_ASAP7_75t_R Instance = 89 Area = 8.01 0.99 % +[i] OAI311xp33_ASAP7_75t_R Instance = 105 Area = 10.50 1.30 % +[i] OAI31xp33_ASAP7_75t_R Instance = 466 Area = 41.94 5.19 % +[i] OAI321xp33_ASAP7_75t_R Instance = 9 Area = 1.08 0.13 % +[i] OAI32xp33_ASAP7_75t_R Instance = 23 Area = 2.30 0.28 % +[i] AND2x2_ASAP7_75t_R Instance = 7 Area = 0.63 0.08 % +[i] AND3x1_ASAP7_75t_R Instance = 16 Area = 1.44 0.18 % +[i] AND4x1_ASAP7_75t_R Instance = 8 Area = 0.80 0.10 % +[i] NAND2xp33_ASAP7_75t_R Instance = 770 Area = 46.20 5.72 % +[i] NAND3xp33_ASAP7_75t_R Instance = 258 Area = 18.06 2.23 % +[i] NAND4xp25_ASAP7_75t_R Instance = 22 Area = 1.98 0.25 % +[i] NOR2xp33_ASAP7_75t_R Instance = 1030 Area = 61.80 7.65 % +[i] NOR3xp33_ASAP7_75t_R Instance = 261 Area = 18.27 2.26 % +[i] NOR4xp25_ASAP7_75t_R Instance = 16 Area = 1.44 0.18 % +[i] NOR5xp2_ASAP7_75t_R Instance = 3 Area = 0.30 0.04 % +[i] OR2x2_ASAP7_75t_R Instance = 2 Area = 0.18 0.02 % +[i] OR3x1_ASAP7_75t_R Instance = 7 Area = 0.63 0.08 % +[i] OR4x1_ASAP7_75t_R Instance = 4 Area = 0.40 0.05 % +[i] OR5x1_ASAP7_75t_R Instance = 1 Area = 0.12 0.01 % +[i] XNOR2xp5_ASAP7_75t_R Instance = 525 Area = 68.25 8.45 % +[i] XOR2xp5_ASAP7_75t_R Instance = 248 Area = 32.24 3.99 % +[i] TOTAL Instance = 10622 Area = 808.16 100.00 % +[i] Report stats: area = 808.16; delay = 328.39; +Cell type report: Count Area + Inverter 1085 47.46 + Sequential cell 562 163.88 + Multi-Input combinational cell 9537 767.94 + Total 11184 979.28 +[-2616.068, -2270.312): ****** (32) +[-2270.312, -1924.557): * (6) +[-1924.557, -1578.801): * (6) +[-1578.801, -1233.045): ************ (64) +[-1233.045, -887.289): ************** (77) +[ -887.289, -541.534): ****************************************** (229) +[ -541.534, -195.778): (0) +[ -195.778, 149.978): (0) +[ 149.978, 495.734): * (6) +[ 495.734, 841.490]: ************************************************** (271) +Startpoint: ld (input port clocked by core_clock) +Endpoint: u0/subword[28]$_DFF_P_ + (rising edge-triggered flip-flop clocked by core_clock) +Path Group: core_clock +Path Type: max +Corner: slow + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock core_clock (rise edge) + 0.00 0.00 clock network delay (ideal) + 200.00 200.00 v input external delay + 0.00 200.00 v ld (in) +1130.36 1330.36 ^ n_831/Y (INVx1_ASAP7_75t_R) +1195.20 2525.56 v n_869/Y (OAI21xp33_ASAP7_75t_R) + 596.24 3121.80 ^ n_875/Y (INVx1_ASAP7_75t_R) + 219.20 3341.00 v n_961/Y (NOR2xp33_ASAP7_75t_R) + 57.42 3398.43 ^ n_1144/Y (O2A1O1Ixp33_ASAP7_75t_R) + 34.91 3433.34 v n_1145/Y (NAND3xp33_ASAP7_75t_R) + 47.32 3480.66 ^ n_1146/Y (OAI211xp5_ASAP7_75t_R) + 49.03 3529.70 v n_1147/Y (OAI31xp33_ASAP7_75t_R) + 39.94 3569.63 ^ n_1159/Y (AOI21xp33_ASAP7_75t_R) + 32.02 3601.65 v n_1176/Y (OAI21xp33_ASAP7_75t_R) + 0.00 3601.65 v u0/subword[28]$_DFF_P_/D (DFFHQNx1_ASAP7_75t_R) + 3601.65 data arrival time + +1000.00 1000.00 clock core_clock (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ u0/subword[28]$_DFF_P_/CLK (DFFHQNx1_ASAP7_75t_R) + -14.42 985.58 library setup time + 985.58 data required time +--------------------------------------------------------- + 985.58 data required time + -3601.65 data arrival time +--------------------------------------------------------- + -2616.07 slack (VIOLATED) + + +wns max -2616.07 +tns max -461282.38 +-- After repair -- + +[WARNING EST-0027] no estimated parasitics. Using wire load models. +[INFO RSZ-0100] Repair move sequence: UnbufferMove SizeUpMove SwapPinsMove BufferMove CloneMove SplitLoadMove +[INFO RSZ-0094] Found 414 endpoints with setup violations. +[INFO RSZ-0099] Repairing 414 out of 414 (100.00%) violating endpoints... + Iter | Removed | Resized | Inserted | Cloned | Pin | Area | WNS | StTNS | EnTNS | Viol | Worst + | Buffers | Gates | Buffers | Gates | Swaps | | | | | Endpts | St/EnPt +------------------------------------------------------------------------------------------------------------------------------ + 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -2616.068 | -132339.0 | -461282.4 | 414 | u0\/subword\[28\]$_DFF_P_/D +[WARNING RSZ-0075] makeBufferedNet failed for driver clone21/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone58/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_855/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_871/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_2334/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_2334/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_2334/Y + 764* | 0 | 259 | 3 | 74 | 10 | +0.9% | -16.725 | -41.0 | -16.7 | 414 | sa03_sr\[4\]$_DFF_P_/D + final | 0 | 262 | 3 | 74 | 10 | +0.9% | 0.234 | 0.0 | 0.0 | 0 | u0\/subword\[0\]$_DFF_P_/D +------------------------------------------------------------------------------------------------------------------------------ +[INFO RSZ-0045] Inserted 3 buffers, 3 to split loads. +[INFO RSZ-0051] Resized 262 instances: 262 up, 0 up match, 0 down, 0 VT +[INFO RSZ-0043] Swapped pins on 10 instances. +[INFO RSZ-0049] Cloned 74 instances. +[INFO RSZ-0033] No hold violations found. +Cell type report: Count Area + Timing Repair Buffer 3 0.34 + Inverter 1085 48.74 + Timing Repair inverter 61 2.74 + Sequential cell 562 163.91 + Multi-Input combinational cell 9550 772.70 + Total 11261 988.42 +[ 0.234, 84.360): ************************************** (158) +[ 84.360, 168.485): * (2) +[168.485, 252.611): * (2) +[252.611, 336.736): * (2) +[336.736, 420.862): *** (12) +[420.862, 504.988): * (4) +[504.988, 589.113): ********* (37) +[589.113, 673.239): ************************************************** (208) +[673.239, 757.364): ********************************* (137) +[757.364, 841.490]: ******************************* (129) +Startpoint: u0/r0/out[25]$_SDFF_PP0_ + (rising edge-triggered flip-flop clocked by core_clock) +Endpoint: u0/subword[0]$_DFF_P_ + (rising edge-triggered flip-flop clocked by core_clock) +Path Group: core_clock +Path Type: max +Corner: slow + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock core_clock (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ u0/r0/out[25]$_SDFF_PP0_/CLK (DFFHQNx1_ASAP7_75t_R) + 69.15 69.15 v u0/r0/out[25]$_SDFF_PP0_/QN (DFFHQNx1_ASAP7_75t_R) + 41.62 110.77 v n_2280/Y (XNOR2xp5_ASAP7_75t_R) + 42.19 152.96 v n_2281/Y (XNOR2xp5_ASAP7_75t_R) + 42.40 195.36 v n_2282/Y (XNOR2xp5_ASAP7_75t_R) + 18.61 213.97 ^ n_2283/Y (INVx1_ASAP7_75t_R) + 16.70 230.67 v n_2284/Y (NAND2xp33_ASAP7_75t_R) + 28.33 259.00 ^ n_2285/Y (OAI21xp33_ASAP7_75t_R) + 51.32 310.32 ^ n_2286/Y (XOR2xp5_ASAP7_75t_R) + 41.06 351.38 v n_2288/Y (AOI21x1_ASAP7_75t_R) + 53.54 404.92 ^ n_2310/Y (INVx2_ASAP7_75t_R) + 50.69 455.61 v n_2319/Y (NOR2x1p5_ASAP7_75t_R) + 35.73 491.34 ^ n_2326/Y (INVx2_ASAP7_75t_R) + 52.19 543.54 v n_2330/Y (OAI21xp5_ASAP7_75t_R) + 54.17 597.70 ^ n_2331/Y (INVx1_ASAP7_75t_R) + 38.98 636.68 v n_2332/Y (NOR2xp33_ASAP7_75t_R) + 40.02 676.70 ^ n_2333/Y (OAI21xp33_ASAP7_75t_R) + 62.19 738.88 v n_2334/Y (NAND2xp5_ASAP7_75t_R) + 43.19 782.08 ^ n_2335/Y (INVx2_ASAP7_75t_R) + 27.52 809.59 v n_2360/Y (AOI22xp33_ASAP7_75t_R) + 32.46 842.06 ^ n_2361/Y (O2A1O1Ixp33_ASAP7_75t_R) + 27.20 869.25 v n_2362/Y (AOI31xp33_ASAP7_75t_R) + 31.35 900.61 ^ n_2376/Y (OAI211xp5_ASAP7_75t_R) + 38.41 939.02 v n_2377/Y (OAI31xp33_ASAP7_75t_R) + 29.85 968.86 ^ n_2415/Y (AOI21xp33_ASAP7_75t_R) + 0.00 968.86 ^ u0/subword[0]$_DFF_P_/D (DFFHQNx1_ASAP7_75t_R) + 968.86 data arrival time + +1000.00 1000.00 clock core_clock (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ u0/subword[0]$_DFF_P_/CLK (DFFHQNx1_ASAP7_75t_R) + -30.90 969.10 library setup time + 969.10 data required time +--------------------------------------------------------- + 969.10 data required time + -968.86 data arrival time +--------------------------------------------------------- + 0.23 slack (MET) + + +wns max 0.00 +tns max 0.00 +Repair timing output passed/skipped equivalence test diff --git a/src/rmp/test/emap_aes_asap7.tcl b/src/rmp/test/emap_aes_asap7.tcl new file mode 100644 index 00000000000..2ba1190cebb --- /dev/null +++ b/src/rmp/test/emap_aes_asap7.tcl @@ -0,0 +1,70 @@ +source "helpers.tcl" + +set test_name emap_aes_asap7 + +define_corners fast slow +set lib_files_slow {\ + ./asap7/asap7sc7p5t_AO_RVT_SS_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_INVBUF_RVT_SS_nldm_220122.lib.gz \ + ./asap7/asap7sc7p5t_OA_RVT_SS_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_SEQ_RVT_SS_nldm_220123.lib \ + ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz\ +} +set lib_files_fast {\ + ./asap7/asap7sc7p5t_AO_RVT_FF_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_INVBUF_RVT_FF_nldm_220122.lib.gz \ + ./asap7/asap7sc7p5t_OA_RVT_FF_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_SEQ_RVT_FF_nldm_220123.lib \ + ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz\ +} + +foreach lib_file $lib_files_slow { + read_liberty -corner slow $lib_file +} +foreach lib_file $lib_files_fast { + read_liberty -corner fast $lib_file +} + +read_lef ./asap7/asap7_tech_1x_201209.lef +read_lef ./asap7/asap7sc7p5t_28_R_1x_220121a.lef + +read_verilog ./aes_asap7.v +link_design aes +read_sdc ./aes_asap7.sdc + +source asap7/setRC.tcl + +puts "-- Before --\n" +report_cell_usage +report_timing_histogram +report_checks +report_wns +report_tns + +write_verilog_for_eqy $test_name before "None" + +puts "-- After emap --\n" + +resynth_emap -scene fast \ + -map_multioutput + +report_cell_usage +report_timing_histogram +report_checks +report_wns +report_tns + +puts "-- After repair --\n" + +repair_timing + +report_cell_usage +report_timing_histogram +report_checks +report_wns +report_tns + +set liberty_files [concat $lib_files_slow $lib_files_fast] +run_equivalence_test $test_name \ + -liberty_files $liberty_files \ + -remove_cells "None" diff --git a/src/rmp/test/emap_gcd_asap7.ok b/src/rmp/test/emap_gcd_asap7.ok new file mode 100644 index 00000000000..fc43df0be6b --- /dev/null +++ b/src/rmp/test/emap_gcd_asap7.ok @@ -0,0 +1,1015 @@ +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13156, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13189, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13222, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13255, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13288, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13321, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 13354, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 14748, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 14781, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz line 14814, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13178, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13211, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13244, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13277, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13310, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13343, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 13376, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 14772, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 14805, timing group from output port. +[WARNING STA-1212] ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz line 14838, timing group from output port. +[INFO ODB-0227] LEF file: ./asap7/asap7_tech_1x_201209.lef, created 30 layers, 9 vias +[INFO ODB-0227] LEF file: ./asap7/asap7sc7p5t_28_R_1x_220121a.lef, created 212 library cells +[WARNING STA-0441] set_input_delay relative to a clock defined on the same port/pin not allowed. +-- Before -- + +Cell type report: Count Area + Buffer 13 0.95 + Inverter 40 1.75 + Sequential cell 35 10.21 + Multi-Input combinational cell 157 18.76 + Total 245 31.67 +[-430.882, -361.927): ************************************************** (34) +[-361.927, -292.972): * (1) +[-292.972, -224.018): **** (3) +[-224.018, -155.063): *** (2) +[-155.063, -86.108): *** (2) +[ -86.108, -17.153): * (1) +[ -17.153, 51.802): *** (2) +[ 51.802, 120.756): * (1) +[ 120.756, 189.711): ****** (4) +[ 189.711, 258.666]: **** (3) +Startpoint: dpath/a_lt_b$in1[0]$_DFFE_PP_ + (rising edge-triggered flip-flop clocked by core_clock) +Endpoint: dpath/a_lt_b$in1[1]$_DFFE_PP_ + (rising edge-triggered flip-flop clocked by core_clock) +Path Group: core_clock +Path Type: max +Corner: slow + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock core_clock (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ dpath/a_lt_b$in1[0]$_DFFE_PP_/CLK (DFFHQNx1_ASAP7_75t_R) + 71.18 71.18 ^ dpath/a_lt_b$in1[0]$_DFFE_PP_/QN (DFFHQNx1_ASAP7_75t_R) + 23.62 94.80 v _228_/Y (INVx1_ASAP7_75t_R) + 25.22 120.02 ^ _421_/CON (HAxp5_ASAP7_75t_R) + 26.50 146.52 v _341_/Y (INVx1_ASAP7_75t_R) + 45.61 192.13 ^ _420_/CON (FAx1_ASAP7_75t_R) + 21.71 213.84 v _234_/Y (INVx1_ASAP7_75t_R) + 37.93 251.76 v _235_/Y (OA21x2_ASAP7_75t_R) + 38.67 290.43 v _236_/Y (OA21x2_ASAP7_75t_R) + 36.56 326.99 v _237_/Y (OA21x2_ASAP7_75t_R) + 38.65 365.64 v _238_/Y (OA21x2_ASAP7_75t_R) + 36.56 402.21 v _239_/Y (OA21x2_ASAP7_75t_R) + 38.65 440.86 v _240_/Y (OA21x2_ASAP7_75t_R) + 36.56 477.42 v _241_/Y (OA21x2_ASAP7_75t_R) + 38.65 516.07 v _242_/Y (OA21x2_ASAP7_75t_R) + 36.56 552.64 v _243_/Y (OA21x2_ASAP7_75t_R) + 38.65 591.29 v _244_/Y (OA21x2_ASAP7_75t_R) + 36.56 627.85 v _245_/Y (OA21x2_ASAP7_75t_R) + 38.65 666.50 v _246_/Y (OA21x2_ASAP7_75t_R) + 39.54 706.05 v _247_/Y (OA21x2_ASAP7_75t_R) + 44.66 750.70 ^ _248_/Y (OAI21x1_ASAP7_75t_R) + 59.57 810.27 ^ _280_/Y (OA21x2_ASAP7_75t_R) + 47.28 857.56 ^ _281_/Y (BUFx2_ASAP7_75t_R) + 44.40 901.96 ^ _289_/Y (AO222x2_ASAP7_75t_R) + 0.00 901.96 ^ dpath/a_lt_b$in1[1]$_DFFE_PP_/D (DFFHQNx1_ASAP7_75t_R) + 901.96 data arrival time + + 500.00 500.00 clock core_clock (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ dpath/a_lt_b$in1[1]$_DFFE_PP_/CLK (DFFHQNx1_ASAP7_75t_R) + -28.92 471.08 library setup time + 471.08 data required time +--------------------------------------------------------- + 471.08 data required time + -901.96 data arrival time +--------------------------------------------------------- + -430.88 slack (VIOLATED) + + +wns max -430.88 +tns max -15641.01 +-- After emap -- + +[i] WARNING: library does not contain cells that can implement output pin 1 of the multi-output cell FAx1_ASAP7_75t_R +[i] WARNING: library does not contain cells that could match the delay of output pin 1 of multi-output cell HAxp5_ASAP7_75t_R +[i] WARNING: technology mapping using multi-output cells with warnings might generate required time violations or circuits with dangling pins +Extended technology mapping stats: + area: 16.680 + delay: 534.670 + power: 0.000 + inverters: 0 + multioutput gates: 12 + +[i] Report cells usage: +[i] AO21x1_ASAP7_75t_R Instance = 26 Area = 2.34 14.03 % +[i] AOI211xp5_ASAP7_75t_R Instance = 1 Area = 0.09 0.54 % +[i] AOI21xp33_ASAP7_75t_R Instance = 4 Area = 0.28 1.68 % +[i] AOI31xp33_ASAP7_75t_R Instance = 17 Area = 1.53 9.17 % +[i] AOI321xp33_ASAP7_75t_R Instance = 15 Area = 1.80 10.79 % +[i] AOI32xp33_ASAP7_75t_R Instance = 1 Area = 0.10 0.60 % +[i] INVx1_ASAP7_75t_R Instance = 38 Area = 1.52 9.11 % +[i] O2A1O1Ixp33_ASAP7_75t_R Instance = 9 Area = 0.81 4.86 % +[i] OAI211xp5_ASAP7_75t_R Instance = 1 Area = 0.09 0.54 % +[i] OAI21xp33_ASAP7_75t_R Instance = 19 Area = 1.33 7.97 % +[i] OAI22xp33_ASAP7_75t_R Instance = 17 Area = 1.53 9.17 % +[i] OAI31xp33_ASAP7_75t_R Instance = 1 Area = 0.09 0.54 % +[i] OAI32xp33_ASAP7_75t_R Instance = 1 Area = 0.10 0.60 % +[i] FAx1_ASAP7_75t_R Instance = 12 Area = 2.40 14.39 % +[i] MAJIxp5_ASAP7_75t_R Instance = 1 Area = 0.10 0.60 % +[i] MAJx2_ASAP7_75t_R Instance = 1 Area = 0.13 0.78 % +[i] NAND2xp33_ASAP7_75t_R Instance = 15 Area = 0.90 5.40 % +[i] NAND5xp2_ASAP7_75t_R Instance = 2 Area = 0.20 1.20 % +[i] NOR2xp33_ASAP7_75t_R Instance = 5 Area = 0.30 1.80 % +[i] NOR3xp33_ASAP7_75t_R Instance = 1 Area = 0.07 0.42 % +[i] NOR4xp25_ASAP7_75t_R Instance = 1 Area = 0.09 0.54 % +[i] NOR5xp2_ASAP7_75t_R Instance = 1 Area = 0.10 0.60 % +[i] XNOR2xp5_ASAP7_75t_R Instance = 5 Area = 0.65 3.90 % +[i] XOR2xp5_ASAP7_75t_R Instance = 1 Area = 0.13 0.78 % +[i] TOTAL Instance = 195 Area = 16.68 100.00 % +[i] Report stats: area = 16.68; delay = 543.99; +Cell type report: Count Area + Inverter 38 1.66 + Sequential cell 35 10.21 + Multi-Input combinational cell 157 15.03 + Total 230 26.90 +[-788.970, -690.194): ************************************************** (17) +[-690.194, -591.418): ************************************************** (17) +[-591.418, -492.642): *** (1) +[-492.642, -393.866): ****** (2) +[-393.866, -295.090): ********* (3) +[-295.090, -196.314): ****** (2) +[-196.314, -97.538): ****** (2) +[ -97.538, 1.239): ****** (2) +[ 1.239, 100.015): ************ (4) +[ 100.015, 198.791]: ********* (3) +Startpoint: dpath/a_lt_b$in1[0]$_DFFE_PP_ + (rising edge-triggered flip-flop clocked by core_clock) +Endpoint: dpath/a_lt_b$in0[1]$_DFFE_PP_ + (rising edge-triggered flip-flop clocked by core_clock) +Path Group: core_clock +Path Type: max +Corner: slow + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock core_clock (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ dpath/a_lt_b$in1[0]$_DFFE_PP_/CLK (DFFHQNx1_ASAP7_75t_R) + 75.16 75.16 v dpath/a_lt_b$in1[0]$_DFFE_PP_/QN (DFFHQNx1_ASAP7_75t_R) + 24.16 99.32 ^ n_96/Y (INVx1_ASAP7_75t_R) + 49.46 148.78 v n_97/Y (NAND2xp33_ASAP7_75t_R) + 69.21 217.99 ^ n_98/CON (FAx1_ASAP7_75t_R) + 44.50 262.49 v n_99/CON (FAx1_ASAP7_75t_R) + 33.84 296.33 ^ n_100/Y (O2A1O1Ixp33_ASAP7_75t_R) + 39.91 336.24 ^ n_101/Y (AO21x1_ASAP7_75t_R) + 29.83 366.07 v n_102/CON (FAx1_ASAP7_75t_R) + 32.74 398.82 ^ n_103/Y (O2A1O1Ixp33_ASAP7_75t_R) + 39.68 438.49 ^ n_104/Y (AO21x1_ASAP7_75t_R) + 29.83 468.32 v n_105/CON (FAx1_ASAP7_75t_R) + 32.74 501.07 ^ n_106/Y (O2A1O1Ixp33_ASAP7_75t_R) + 39.68 540.74 ^ n_107/Y (AO21x1_ASAP7_75t_R) + 29.83 570.57 v n_108/CON (FAx1_ASAP7_75t_R) + 32.74 603.32 ^ n_109/Y (O2A1O1Ixp33_ASAP7_75t_R) + 39.68 642.99 ^ n_110/Y (AO21x1_ASAP7_75t_R) + 29.83 672.83 v n_111/CON (FAx1_ASAP7_75t_R) + 30.85 703.67 ^ n_112/Y (O2A1O1Ixp33_ASAP7_75t_R) + 27.23 730.90 v n_113/Y (AOI21xp33_ASAP7_75t_R) + 28.33 759.24 ^ n_116/Y (AOI21xp33_ASAP7_75t_R) + 27.86 787.10 v n_119/Y (OAI22xp33_ASAP7_75t_R) + 28.28 815.38 ^ n_120/Y (INVx1_ASAP7_75t_R) + 34.45 849.83 v n_121/CON (FAx1_ASAP7_75t_R) + 172.62 1022.45 ^ n_139/Y (AOI211xp5_ASAP7_75t_R) + 127.39 1149.85 v n_140/Y (INVx1_ASAP7_75t_R) + 45.65 1195.49 ^ n_181/Y (OAI211xp5_ASAP7_75t_R) + 23.58 1219.07 v n_182/Y (AOI31xp33_ASAP7_75t_R) + 37.10 1256.17 ^ n_183/Y (AOI31xp33_ASAP7_75t_R) + 0.00 1256.17 ^ dpath/a_lt_b$in0[1]$_DFFE_PP_/D (DFFHQNx1_ASAP7_75t_R) + 1256.17 data arrival time + + 500.00 500.00 clock core_clock (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ dpath/a_lt_b$in0[1]$_DFFE_PP_/CLK (DFFHQNx1_ASAP7_75t_R) + -32.80 467.20 library setup time + 467.20 data required time +--------------------------------------------------------- + 467.20 data required time + -1256.17 data arrival time +--------------------------------------------------------- + -788.97 slack (VIOLATED) + + +wns max -788.97 +tns max -27624.90 +-- After repair -- + +[WARNING EST-0027] no estimated parasitics. Using wire load models. +[INFO RSZ-0100] Repair move sequence: UnbufferMove SizeUpMove SwapPinsMove BufferMove CloneMove SplitLoadMove +[INFO RSZ-0094] Found 46 endpoints with setup violations. +[INFO RSZ-0099] Repairing 46 out of 46 (100.00%) violating endpoints... + Iter | Removed | Resized | Inserted | Cloned | Pin | Area | WNS | StTNS | EnTNS | Viol | Worst + | Buffers | Gates | Buffers | Gates | Swaps | | | | | Endpts | St/EnPt +------------------------------------------------------------------------------------------------------------------------------ + 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -788.970 | -13204.1 | -27624.9 | 46 | dpath\/a_lt_b$in0\[1\]$_DFFE_PP_/D +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_140/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone2/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_137/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone5/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone24/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone24/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone4/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver clone1/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_139/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_130/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_130/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_130/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_125/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_130/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_121/CON +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_113/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_248/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver dpath\/a_lt_b$in1\[0\]$_DFFE_PP_/QN +[WARNING RSZ-0075] makeBufferedNet failed for driver n_96/Y +[WARNING RSZ-0075] makeBufferedNet failed for driver n_97/Y + 199* | 0 | 19 | 0 | 6 | 10 | +3.5% | -531.201 | -7283.8 | -19315.0 | 46 | dpath\/a_lt_b$in0\[1\]$_DFFE_PP_/D + final | 0 | 19 | 0 | 6 | 10 | +3.5% | -531.201 | -7283.8 | -19315.0 | 44 | dpath\/a_lt_b$in0\[1\]$_DFFE_PP_/D +------------------------------------------------------------------------------------------------------------------------------ +[INFO RSZ-0051] Resized 19 instances: 19 up, 0 up match, 0 down, 0 VT +[INFO RSZ-0043] Swapped pins on 10 instances. +[INFO RSZ-0049] Cloned 6 instances. +[WARNING RSZ-0062] Unable to repair all setup violations. +[INFO RSZ-0033] No hold violations found. +Cell type report: Count Area + Inverter 38 1.69 + Timing Repair inverter 2 0.10 + Sequential cell 35 10.21 + Multi-Input combinational cell 161 15.83 + Total 236 27.83 +[-531.201, -453.000): ************************************************** (34) +[-453.000, -374.799): * (1) +[-374.799, -296.597): **** (3) +[-296.597, -218.396): *** (2) +[-218.396, -140.195): * (1) +[-140.195, -61.994): *** (2) +[ -61.994, 16.208): * (1) +[ 16.208, 94.409): ****** (4) +[ 94.409, 172.610): **** (3) +[ 172.610, 250.812]: *** (2) +Startpoint: dpath/a_lt_b$in1[0]$_DFFE_PP_ + (rising edge-triggered flip-flop clocked by core_clock) +Endpoint: dpath/a_lt_b$in0[1]$_DFFE_PP_ + (rising edge-triggered flip-flop clocked by core_clock) +Path Group: core_clock +Path Type: max +Corner: slow + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock core_clock (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ dpath/a_lt_b$in1[0]$_DFFE_PP_/CLK (DFFHQNx1_ASAP7_75t_R) + 79.23 79.23 ^ dpath/a_lt_b$in1[0]$_DFFE_PP_/QN (DFFHQNx1_ASAP7_75t_R) + 21.19 100.41 v n_96/Y (INVx2_ASAP7_75t_R) + 25.39 125.80 ^ n_97/Y (NAND2x1p5_ASAP7_75t_R) + 38.80 164.60 v n_98/CON (FAx1_ASAP7_75t_R) + 40.34 204.94 ^ n_99/CON (FAx1_ASAP7_75t_R) + 24.45 229.39 v n_100/Y (O2A1O1Ixp5_ASAP7_75t_R) + 43.29 272.68 v n_101/Y (AO21x1_ASAP7_75t_R) + 32.53 305.21 ^ n_102/CON (FAx1_ASAP7_75t_R) + 24.68 329.89 v n_103/Y (O2A1O1Ixp5_ASAP7_75t_R) + 43.36 373.25 v n_104/Y (AO21x1_ASAP7_75t_R) + 32.53 405.78 ^ n_105/CON (FAx1_ASAP7_75t_R) + 24.68 430.46 v n_106/Y (O2A1O1Ixp5_ASAP7_75t_R) + 43.36 473.82 v n_107/Y (AO21x1_ASAP7_75t_R) + 32.53 506.35 ^ n_108/CON (FAx1_ASAP7_75t_R) + 24.68 531.03 v n_109/Y (O2A1O1Ixp5_ASAP7_75t_R) + 43.36 574.39 v n_110/Y (AO21x1_ASAP7_75t_R) + 32.53 606.92 ^ n_111/CON (FAx1_ASAP7_75t_R) + 24.45 631.37 v n_112/Y (O2A1O1Ixp5_ASAP7_75t_R) + 35.73 667.10 ^ n_113/Y (AOI21xp5_ASAP7_75t_R) + 22.62 689.72 v n_116/Y (AOI21xp5_ASAP7_75t_R) + 23.87 713.59 ^ n_119/Y (OAI22xp5_ASAP7_75t_R) + 25.56 739.15 v n_120/Y (INVx1_ASAP7_75t_R) + 71.45 810.60 ^ n_121/CON (FAx1_ASAP7_75t_R) + 82.45 893.05 v n_139/Y (AOI211x1_ASAP7_75t_R) + 32.93 925.98 ^ n_140/Y (INVx2_ASAP7_75t_R) + 26.68 952.66 v n_181/Y (OAI211xp5_ASAP7_75t_R) + 39.87 992.53 ^ n_182/Y (AOI31xp33_ASAP7_75t_R) + 21.88 1014.41 v n_183/Y (AOI31xp33_ASAP7_75t_R) + 0.00 1014.41 v dpath/a_lt_b$in0[1]$_DFFE_PP_/D (DFFHQNx1_ASAP7_75t_R) + 1014.41 data arrival time + + 500.00 500.00 clock core_clock (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ dpath/a_lt_b$in0[1]$_DFFE_PP_/CLK (DFFHQNx1_ASAP7_75t_R) + -16.79 483.21 library setup time + 483.21 data required time +--------------------------------------------------------- + 483.21 data required time + -1014.41 data arrival time +--------------------------------------------------------- + -531.20 slack (VIOLATED) + + +wns max -531.20 +tns max -19315.02 +Repair timing output passed/skipped equivalence test diff --git a/src/rmp/test/emap_gcd_asap7.tcl b/src/rmp/test/emap_gcd_asap7.tcl new file mode 100644 index 00000000000..2ce9ef4a53c --- /dev/null +++ b/src/rmp/test/emap_gcd_asap7.tcl @@ -0,0 +1,69 @@ +source "helpers.tcl" + +set test_name emap_gcd_asap7 + +define_corners fast slow +set lib_files_slow {\ + ./asap7/asap7sc7p5t_AO_RVT_SS_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_INVBUF_RVT_SS_nldm_220122.lib.gz \ + ./asap7/asap7sc7p5t_OA_RVT_SS_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_SEQ_RVT_SS_nldm_220123.lib \ + ./asap7/asap7sc7p5t_SIMPLE_RVT_SS_nldm_211120.lib.gz\ +} +set lib_files_fast {\ + ./asap7/asap7sc7p5t_AO_RVT_FF_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_INVBUF_RVT_FF_nldm_220122.lib.gz \ + ./asap7/asap7sc7p5t_OA_RVT_FF_nldm_211120.lib.gz \ + ./asap7/asap7sc7p5t_SEQ_RVT_FF_nldm_220123.lib \ + ./asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz\ +} + +foreach lib_file $lib_files_slow { + read_liberty -corner slow $lib_file +} +foreach lib_file $lib_files_fast { + read_liberty -corner fast $lib_file +} + +read_lef ./asap7/asap7_tech_1x_201209.lef +read_lef ./asap7/asap7sc7p5t_28_R_1x_220121a.lef +read_verilog ./gcd_asap7.v +link_design gcd +read_sdc ./gcd_asap7.sdc + +source asap7/setRC.tcl + +puts "-- Before --\n" +report_cell_usage +report_timing_histogram +report_checks +report_wns +report_tns + +write_verilog_for_eqy $test_name before "None" + +puts "-- After emap --\n" + +resynth_emap -scene fast \ + -map_multioutput + +report_cell_usage +report_timing_histogram +report_checks +report_wns +report_tns + +puts "-- After repair --\n" + +repair_timing + +report_cell_usage +report_timing_histogram +report_checks +report_wns +report_tns + +set liberty_files [concat $lib_files_slow $lib_files_fast] +run_equivalence_test $test_name \ + -liberty_files $liberty_files \ + -remove_cells "None" diff --git a/src/rmp/test/gcd_annealing1.tcl b/src/rmp/test/gcd_annealing1.tcl index 4c7b32be5c9..2737c36dc50 100644 --- a/src/rmp/test/gcd_annealing1.tcl +++ b/src/rmp/test/gcd_annealing1.tcl @@ -42,7 +42,7 @@ write_verilog_for_eqy $test_name before "None" puts "-- After --\n" -resynth_annealing -corner fast -revert_after 5 -seed 55 +resynth_annealing -scene fast -revert_after 5 -seed 55 report_timing_histogram report_cell_usage report_checks diff --git a/src/rmp/test/gcd_annealing2.tcl b/src/rmp/test/gcd_annealing2.tcl index 80158cdc7c1..0554212dbaf 100644 --- a/src/rmp/test/gcd_annealing2.tcl +++ b/src/rmp/test/gcd_annealing2.tcl @@ -42,7 +42,7 @@ write_verilog_for_eqy $test_name before "None" puts "-- After --\n" -resynth_annealing -corner fast -temp 1e-10 -seed 66 +resynth_annealing -scene fast -temp 1e-10 -seed 66 report_timing_histogram report_cell_usage report_checks diff --git a/src/rmp/test/gcd_asap7.tcl b/src/rmp/test/gcd_asap7.tcl index 600a8de041a..4f81578300d 100644 --- a/src/rmp/test/gcd_asap7.tcl +++ b/src/rmp/test/gcd_asap7.tcl @@ -28,14 +28,14 @@ report_tns puts "-- After --\n" -resynth -corner fast +resynth -scene fast report_timing_histogram report_cell_usage report_checks report_wns report_tns -resynth -corner fast +resynth -scene fast report_timing_histogram report_cell_usage report_checks diff --git a/src/rmp/test/gcd_genetic.tcl b/src/rmp/test/gcd_genetic.tcl index 6a4adae6495..be658da27f3 100644 --- a/src/rmp/test/gcd_genetic.tcl +++ b/src/rmp/test/gcd_genetic.tcl @@ -42,7 +42,7 @@ write_verilog_for_eqy $test_name before "None" puts "-- After --\n" resynth_genetic \ - -corner slow \ + -scene slow \ -initial_ops 5 \ -iters 15 \ -population_size 100 \ diff --git a/src/rmp/test/rmp_readme_msgs_check.ok b/src/rmp/test/rmp_readme_msgs_check.ok index e4ee21f5d66..9d209c723cd 100644 --- a/src/rmp/test/rmp_readme_msgs_check.ok +++ b/src/rmp/test/rmp_readme_msgs_check.ok @@ -1,5 +1,5 @@ README.md -Names: 4, Desc: 4, Syn: 4, Options: 4, Args: 4 +Names: 5, Desc: 5, Syn: 5, Options: 5, Args: 5 Global Examples: None Global See Also: None Man2 successfully compiled. diff --git a/test/helpers.tcl b/test/helpers.tcl index cf4fbed4606..463208e4f97 100644 --- a/test/helpers.tcl +++ b/test/helpers.tcl @@ -151,7 +151,7 @@ proc run_equivalence_test { test args } { if { [info exists ::env(EQUIVALENCE_CHECK)] } { exec rm -rf $run_dir - catch { exec eqy -d $run_dir $test_script > /dev/null } + catch { exec eqy -j [cpu_count] -d $run_dir $test_script > /dev/null } set count 0 catch { set count [exec grep -c "Successfully proved designs equivalent" \ diff --git a/third-party/BUILD b/third-party/BUILD new file mode 100644 index 00000000000..4f47371a718 --- /dev/null +++ b/third-party/BUILD @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2026-2026, The OpenROAD Authors + +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +cc_library( + name = "mockturtle", + hdrs = glob(["mockturtle/include/mockturtle/**/*.hpp"]) + glob([ + "mockturtle/lib/kitty/**/*.hpp", + ]) + glob([ + "mockturtle/lib/lorina/**/*.hpp", + ]) + glob([ + "mockturtle/lib/lorina/**/*.inc", + ]), + includes = [ + "mockturtle/include/", + "mockturtle/lib/kitty/", + "mockturtle/lib/lorina/", + ], + visibility = ["//visibility:public"], + deps = [ + "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/container:flat_hash_map", + "@abseil-cpp//absl/random:random", + "@spdlog", + ], +) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index a934c2e0ffd..1cccb9f6330 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -57,5 +57,25 @@ add_compile_options( endif() +add_library(mockturtle INTERFACE) + +find_package(spdlog REQUIRED) + +target_link_libraries(mockturtle + INTERFACE + spdlog::spdlog +) + +target_include_directories(mockturtle + INTERFACE + ./mockturtle/include/ + ./mockturtle/lib/kitty/ + ./mockturtle/lib/lorina/ +) + +# Mockturtle uses abseil, but we don't add it here to avoid conflicts +# with or-tools vendored version. +set(MOCKTURTLE_LIBRARY "mockturtle" "absl::flat_hash_map" "absl::random_random" CACHE STRING "") + add_subdirectory(lodepng) add_subdirectory(stb_truetype) diff --git a/third-party/mockturtle/LICENSE b/third-party/mockturtle/LICENSE new file mode 100644 index 00000000000..4b575c201a2 --- /dev/null +++ b/third-party/mockturtle/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2019 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cleanup.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cleanup.hpp new file mode 100644 index 00000000000..766d67764fd --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cleanup.hpp @@ -0,0 +1,657 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cleanup.hpp + \brief Cleans up networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/crossed.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +#include + +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +void cleanup_dangling_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, node_map, NtkSrc>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto child, auto ) { + const auto f = old_to_new[child]; + if ( ntk.is_complemented( child ) ) + { + children.push_back( dest.create_not( f ) ); + } + else + { + children.push_back( f ); + } + } ); + + /* clone node */ + if constexpr ( std::is_same_v ) + { + old_to_new[node] = dest.clone_node( ntk, node, children ); + } + else + { + do + { + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v, "NtkDest cannot create AND gates" ); + if ( ntk.is_and( node ) ) + { + old_to_new[node] = dest.create_and( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_or_v ) + { + static_assert( has_create_or_v, "NtkDest cannot create OR gates" ); + if ( ntk.is_or( node ) ) + { + old_to_new[node] = dest.create_or( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v, "NtkDest cannot create XOR gates" ); + if ( ntk.is_xor( node ) ) + { + old_to_new[node] = dest.create_xor( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v, "NtkDest cannot create MAJ gates" ); + if ( ntk.is_maj( node ) ) + { + old_to_new[node] = dest.create_maj( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_ite_v ) + { + static_assert( has_create_ite_v, "NtkDest cannot create ITE gates" ); + if ( ntk.is_ite( node ) ) + { + old_to_new[node] = dest.create_ite( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v, "NtkDest cannot create XOR3 gates" ); + if ( ntk.is_xor3( node ) ) + { + old_to_new[node] = dest.create_xor3( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_nary_and_v ) + { + static_assert( has_create_nary_and_v, "NtkDest cannot create n-ary AND gates" ); + if ( ntk.is_nary_and( node ) ) + { + old_to_new[node] = dest.create_nary_and( children ); + break; + } + } + if constexpr ( has_is_nary_or_v ) + { + static_assert( has_create_nary_or_v, "NtkDest cannot create n-ary OR gates" ); + if ( ntk.is_nary_or( node ) ) + { + old_to_new[node] = dest.create_nary_or( children ); + break; + } + } + if constexpr ( has_is_nary_xor_v ) + { + static_assert( has_create_nary_xor_v, "NtkDest cannot create n-ary XOR gates" ); + if ( ntk.is_nary_xor( node ) ) + { + old_to_new[node] = dest.create_nary_xor( children ); + break; + } + } + if constexpr ( has_is_not_v ) + { + static_assert( has_create_not_v, "NtkDest cannot create NOT gates" ); + if ( ntk.is_not( node ) ) + { + old_to_new[node] = dest.create_not( children[0] ); + break; + } + } + if constexpr ( has_is_buf_v ) + { + static_assert( has_create_buf_v, "NtkDest cannot create buffers" ); + if ( ntk.is_buf( node ) ) + { + old_to_new[node] = dest.create_buf( children[0] ); + break; + } + } + if constexpr ( has_is_function_v ) + { + static_assert( has_create_node_v, "NtkDest cannot create arbitrary function gates" ); + old_to_new[node] = dest.create_node( children, ntk.node_function( node ) ); + break; + } + std::cerr << "[e] something went wrong, could not copy node " << ntk.node_to_index( node ) << "\n"; + } while ( false ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( node ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[node], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[node], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void cleanup_dangling_with_crossings_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, node_map, NtkSrc>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto const& f ) { + if ( ntk.is_crossing( ntk.get_node( f ) ) ) + children.push_back( ntk.is_second( f ) ? dest.make_second( old_to_new[f] ) : old_to_new[f] ); + else + children.push_back( old_to_new[f] ); + } ); + + /* clone node */ + if ( ntk.is_crossing( node ) ) + { + assert( children.size() == 2 ); + old_to_new[node] = dest.create_crossing( children[0], children[1] ).first; + } + else + { + old_to_new[node] = dest.clone_node( ntk, node, children ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( node ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[node], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[node], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void cleanup_luts_impl( Ntk const& ntk, Ntk& dest, LeavesIterator begin, LeavesIterator end, node_map, Ntk>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* iterate through nodes */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + return; /* continue */ + + auto func = ntk.node_function( n ); + + /* constant propagation */ + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( dest.is_constant( old_to_new[f] ) ) + { + if ( dest.constant_value( old_to_new[f] ) != ntk.is_complemented( f ) ) + { + kitty::cofactor1_inplace( func, i ); + } + else + { + kitty::cofactor0_inplace( func, i ); + } + } + } ); + + const auto support = kitty::min_base_inplace( func ); + auto new_func = kitty::shrink_to( func, static_cast( support.size() ) ); + + std::vector> children; + if ( auto var = support.begin(); var != support.end() ) + { + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( *var == i ) + { + auto const& new_f = old_to_new[f]; + children.push_back( ntk.is_complemented( f ) ? dest.create_not( new_f ) : new_f ); + if ( ++var == support.end() ) + { + return false; + } + } + return true; + } ); + } + + if ( new_func.num_vars() == 0u ) + { + old_to_new[n] = dest.get_constant( !kitty::is_const0( new_func ) ); + } + else if ( new_func.num_vars() == 1u ) + { + old_to_new[n] = *( new_func.begin() ) == 0b10 ? children.front() : dest.create_not( children.front() ); + } + else + { + old_to_new[n] = dest.create_node( children, new_func ); + } + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[n], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[n], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void clone_inputs( NtkSrc const& ntk, NtkDest& dest, std::vector>& cis, bool remove_dangling_PIs = false ) +{ + /* network name */ + if constexpr ( has_get_network_name_v && has_set_network_name_v ) + { + dest.set_network_name( ntk.get_network_name() ); + } + + /* PIs & PI names */ + ntk.foreach_pi( [&]( auto n ) { + if ( remove_dangling_PIs && ntk.fanout_size( n ) == 0 ) + { + cis.push_back( dest.get_constant( false ) ); + } + else + { + cis.push_back( dest.create_pi() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + } + } ); + + /* ROs & RO names & register information */ + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + ntk.foreach_ro( [&]( auto const& n, auto i ) { + cis.push_back( dest.create_ro() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + dest.set_register( i, ntk.register_at( i ) ); + } ); + } +} + +template +void clone_outputs( NtkSrc const& ntk, NtkDest& dest, node_map, NtkSrc> const& old_to_new, bool remove_redundant_POs = false ) +{ + /* POs */ + ntk.foreach_po( [&]( auto const& po ) { + auto const f = old_to_new[po]; + auto const n = dest.get_node( f ); + if ( remove_redundant_POs && ( dest.is_pi( n ) || dest.is_constant( n ) ) ) + { + return; + } + dest.create_po( ntk.is_complemented( po ) ? dest.create_not( f ) : f ); + } ); + + /* RIs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + dest.create_ri( ntk.is_complemented( f ) ? dest.create_not( old_to_new[f] ) : old_to_new[f] ); + } ); + } + + /* CO names */ + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + ntk.foreach_co( [&]( auto co, auto index ) { + (void)co; + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } ); + } +} + +} // namespace detail + +template +std::vector> cleanup_dangling( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "NtkSrc does not implement the is_complemented method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + + node_map, NtkSrc> old_to_new( ntk ); + detail::cleanup_dangling_impl( ntk, dest, begin, end, old_to_new ); + std::vector> fs; + + /* create outputs in the same order */ + ntk.foreach_po( [&]( auto po ) { + const auto f = old_to_new[po]; + fs.push_back( ntk.is_complemented( po ) ? dest.create_not( f ) : f ); + } ); + if constexpr ( has_foreach_ri_v ) + { + ntk.foreach_ri( [&]( auto ri ) { + const auto f = old_to_new[ri]; + fs.push_back( ntk.is_complemented( ri ) ? dest.create_not( f ) : f ); + } ); + } + + return fs; +} + +/*! \brief Cleans up dangling nodes. + * + * This method reconstructs a network and omits all dangling nodes. If the flag + * `remove_dangling_PIs` is true, dangling PIs are also omitted. If the flag + * `remove_redundant_POs` is true, redundant POs, i.e. POs connected to a PI or + * constant, are also omitted. The network types of the source and destination + * network are the same. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + */ +template +[[nodiscard]] NtkDest cleanup_dangling( NtkSrc const& ntk, bool remove_dangling_PIs = false, bool remove_redundant_POs = false ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_node_to_index_v, "NtkSrc does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_is_complemented_v, "NtkDest does not implement the is_complemented method" ); + + NtkDest dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis, remove_dangling_PIs ); + + node_map, NtkSrc> old_to_new( ntk ); + if constexpr ( is_crossed_network_type_v ) + { + detail::cleanup_dangling_with_crossings_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + } + else + { + detail::cleanup_dangling_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + } + + detail::clone_outputs( ntk, dest, old_to_new, remove_redundant_POs ); + + return dest; +} + +/*! \brief Cleans up LUT nodes. + * + * This method reconstructs a LUT network and optimizes LUTs when they do not + * depend on all their fanin, or when some of the fanin are constant inputs. + * + * Constant gate inputs will be propagated. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `get_constant` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_node` + * - `foreach_fanin` + * - `create_pi` + * - `create_po` + * - `create_node` + * - `create_not` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `node_function` + */ +template +[[nodiscard]] Ntk cleanup_luts( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_node_v, "Ntk does not implement the create_node method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + Ntk dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis ); + + node_map, Ntk> old_to_new( ntk ); + detail::cleanup_luts_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + + detail::clone_outputs( ntk, dest, old_to_new ); + + return dest; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration.hpp b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration.hpp new file mode 100644 index 00000000000..f3651991900 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/cut_enumeration.hpp @@ -0,0 +1,1838 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cut_enumeration.hpp + \brief Cut enumeration + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Sahand Kashani-Akhavan + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/cuts.hpp" +#include "../utils/mixed_radix.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/truth_table_cache.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for cut_enumeration. + * + * The data structure `cut_enumeration_params` holds configurable parameters + * with default arguments for `cut_enumeration`. + */ +struct cut_enumeration_params +{ + /*! \brief Maximum number of leaves for a cut. */ + uint32_t cut_size{ 4u }; + + /*! \brief Maximum number of cuts for a node. */ + uint32_t cut_limit{ 25u }; + + /*! \brief Maximum number of fan-ins for a node. */ + uint32_t fanin_limit{ 10u }; + + /*! \brief Prune cuts by removing don't cares. */ + bool minimize_truth_table{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose. */ + bool very_verbose{ false }; +}; + +struct cut_enumeration_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for truth table computation. */ + stopwatch<>::duration time_truth_table{ 0 }; + + /*! \brief Prints report. */ + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] truth table time = {:>5.2f} secs\n", to_seconds( time_truth_table ) ); + } +}; + +static constexpr uint32_t max_cut_size = 16; + +template +struct cut_data; + +template +struct cut_data +{ + uint32_t func_id; + T data; +}; + +template +struct cut_data +{ + T data; +}; + +template +using cut_type = cut>; + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct network_cuts; + +template +network_cuts cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps = {}, cut_enumeration_stats* pst = nullptr ); + +/* function to update a cut */ +template +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + (void)cut; + (void)cuts; + (void)ntk; + (void)n; + } +}; + +namespace detail +{ +template +class cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Cut database for a network. + * + * The function `cut_enumeration` returns an instance of type `network_cuts` + * which contains a cut database and can be queried to return all cuts of a + * node, or the function of a cut (if it was computed). + * + * An instance of type `network_cuts` can only be constructed from the + * `cut_enumeration` algorithm. + */ +template +struct network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 26; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +private: + explicit network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::dynamic_truth_table zero( 0u ), proj( 1u ); + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) { return _cuts[node_index]; } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const { return _cuts[node_index]; } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::dynamic_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::cut_enumeration_impl; + + template + friend network_cuts<_Ntk, _ComputeTruth, _CutData> cut_enumeration( _Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ); + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + +private: + /* compressed representation of cuts */ + std::vector _cuts; + + /* cut truth tables */ + truth_table_cache _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class cut_enumeration_impl +{ +public: + using cut_t = typename network_cuts::cut_t; + using cut_set_t = typename network_cuts::cut_set_t; + + explicit cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + +private: + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = kitty::extend_to( cuts._truth_tables[( *cut )->func_id], res.size() ); + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + auto tt_res_shrink = shrink_to( tt_res, static_cast( support.size() ) ); + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + return cuts._truth_tables.insert( tt_res_shrink ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, ps.cut_size ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, ps.cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, ps.cut_size ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Cut enumeration. + * + * This function implements the cut enumeration algorithm. The algorithm + * traverses all nodes in topological order and computes a node's cuts based + * on its fanins' cuts. Dominated cuts are filtered and are not added to the + * cut set. For each node a unit cut is added to the end of each cut set. + * + * The template parameter `ComputeTruth` controls whether truth tables should + * be computed for each cut. Computing truth tables slows down the execution + * time of the algorithm. + * + * The number of computed cuts is controlled via the `cut_limit` parameter. + * To decide which cuts are collected in each node's cut set, cuts are sorted. + * Unit cuts do not participate in the sorting and are always added to the end + * of each cut set. + * + * The algorithm can be configured by specifying the template argument `CutData` + * which holds the application specific data assigned to each cut. Examples + * on how to specify custom cost functions for sorting cuts based on the + * application specific cut data can be found in the files contained in the + * directory `include/mockturtle/algorithms/cut_enumeration`. + * + * **Required network functions:** + * - `is_constant` + * - `is_ci` + * - `size` + * - `get_node` + * - `node_to_index` + * - `foreach_node` + * - `foreach_fanin` + * - `compute` for `kitty::dynamic_truth_table` (if `ComputeTruth` is true) + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes + one can wrap the network parameter in a ``topo_view`` view. + + .. note:: + + The implementation of this algorithm was heavily inspired buy cut + enumeration implementations in ABC. + \endverbatim + */ +template +network_cuts cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( !ComputeTruth || has_compute_v, "Ntk does not implement the compute method for kitty::dynamic_truth_table" ); + + cut_enumeration_stats st; + network_cuts res( ntk.size() ); + detail::cut_enumeration_impl p( ntk, ps, st, res ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return res; +} + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct fast_network_cuts; + +template +fast_network_cuts fast_cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps = {}, cut_enumeration_stats* pst = nullptr ); + +namespace detail +{ +template +class fast_cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Cut database for a network. + * + * The function `cut_enumeration` returns an instance of type `fast_network_cuts` + * which contains a cut database and can be queried to return all cuts of a + * node, or the function of a cut (if it was computed). + * + * Comparing to `network_cuts`, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * An instance of type `fast_network_cuts` can only be constructed from the + * `fast_cut_enumeration` algorithm. + */ +template +struct fast_network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 50; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +private: + explicit fast_network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::static_truth_table zero, proj; + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) { return _cuts[node_index]; } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const { return _cuts[node_index]; } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::static_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::fast_cut_enumeration_impl; + + template + friend fast_network_cuts<_Ntk, _NumVars, _ComputeTruth, _CutData> fast_cut_enumeration( _Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ); + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + +private: + /* compressed representation of cuts */ + std::vector _cuts; + + /* cut truth tables */ + truth_table_cache> _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class fast_cut_enumeration_impl +{ +public: + using cut_t = typename fast_network_cuts::cut_t; + using cut_set_t = typename fast_network_cuts::cut_set_t; + + explicit fast_cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, fast_network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + +private: + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector> tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = cuts._truth_tables[( *cut )->func_id]; + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, NumVars ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, NumVars ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, NumVars ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + fast_network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Fast cut enumeration. + * + * This function implements the cut enumeration algorithm. The algorithm + * traverses all nodes in topological order and computes a node's cuts based + * on its fanins' cuts. Dominated cuts are filtered and are not added to the + * cut set. For each node a unit cut is added to the end of each cut set. + * + * The template parameter `ComputeTruth` controls whether truth tables should + * be computed for each cut. Computing truth tables slows down the execution + * time of the algorithm. + * + * The cut size is controlled using the template parameter `NumVars` instead + * of the `cut_size` parameter as in `cut_enumeration`. + * + * Comparing to `cut_enumeration`, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * The number of computed cuts is controlled via the `cut_limit` parameter. + * To decide which cuts are collected in each node's cut set, cuts are sorted. + * Unit cuts do not participate in the sorting and are always added to the end + * of each cut set. + * + * The algorithm can be configured by specifying the template argument `CutData` + * which holds the application specific data assigned to each cut. Examples + * on how to specify custom cost functions for sorting cuts based on the + * application specific cut data can be found in the files contained in the + * directory `include/mockturtle/algorithms/cut_enumeration`. + * + * **Required network functions:** + * - `is_constant` + * - `is_ci` + * - `size` + * - `get_node` + * - `node_to_index` + * - `foreach_node` + * - `foreach_fanin` + * - `compute` for `kitty::static_truth_table` (if `ComputeTruth` is true) + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes + one can wrap the network parameter in a ``topo_view`` view. + + .. note:: + + The implementation of this algorithm was heavily inspired by cut + enumeration implementations in ABC. + \endverbatim + */ +template +fast_network_cuts fast_cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( !ComputeTruth || has_compute_v, "Ntk does not implement the compute method for kitty::dynamic_truth_table" ); + + cut_enumeration_stats st; + fast_network_cuts res( ntk.size() ); + detail::fast_cut_enumeration_impl p( ntk, ps, st, res ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return res; +} + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct dynamic_network_cuts; + +namespace detail +{ +template +class dynamic_cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Dynamic cut database for a network. + * + * Struct `dynamic_network_cuts` contains a cut database and can be queried + * to return all cuts of a node, or the function of a cut (if it was computed). + * + * Comparing to `network_cuts`, it supports dynamic allocation of cuts for + * networks in expansion. Moreover, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * An instance of type `dynamic_network_cuts` can only be constructed from the + * `dynamic_cut_enumeration_impl` algorithm. + */ +template +struct dynamic_network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 16u; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +public: + explicit dynamic_network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::static_truth_table zero, proj; + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) + { + if ( node_index >= _cuts.size() ) + _cuts.resize( node_index + 1 ); + + return _cuts[node_index]; + } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const + { + assert( node_index < _cuts.size() ); + return _cuts[node_index]; + } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::static_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::dynamic_cut_enumeration_impl; + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + + void clear_cut_set( uint32_t index ) + { + _cuts[index].clear(); + } + +private: + /* compressed representation of cuts */ + std::deque _cuts; + + /* cut truth tables */ + truth_table_cache> _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ +template +class dynamic_cut_enumeration_impl +{ +public: + using cut_t = typename dynamic_network_cuts::cut_t; + using cut_set_t = typename dynamic_network_cuts::cut_set_t; + + explicit dynamic_cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, dynamic_network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + + void compute_cuts( node const& n ) + { + const auto index = ntk.node_to_index( n ); + + if ( cuts.cuts( index ).size() > 0 ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_cuts( ntk.get_node( f ) ); + } ); + + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + + void init_cuts() + { + cuts.add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + cuts.add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( true ) ) ) ); + ntk.foreach_ci( [&]( auto const& n ) { + cuts.add_unit_cut( ntk.node_to_index( n ) ); + } ); + } + + void clear_cuts( node const& n ) + { + const auto index = ntk.node_to_index( n ); + if ( cuts.cuts( index ).size() == 0 ) + return; + + cuts.clear_cut_set( index ); + } + +private: + inline bool fast_support_minimization( kitty::static_truth_table const& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + } + + return true; + } + + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector> tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = cuts._truth_tables[( *cut )->func_id]; + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, NumVars ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, NumVars ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, NumVars ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + dynamic_network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +// This function expects to receive a network where nodes are sorted in +// topological order. Cuts are represented as a 64-bit bit vector where each bit +// determines whether a given node exists in the cut. + +/*! \brief Cut enumeration. + * + * This function implements a generic fast cut enumeration algorithm for graphs + * containing at most 64 nodes. It is generic as it supports graphs in which + * nodes can have variable fan-in. Speed and space-efficiency are achieved by + * representing cuts as 64-bit bit vectors, i.e. each bit represents whether or + * not a node is in a cut. Cut-set union and domination operations then + * transform into bitwise operations that can be performed in a few clock cycles + * each. + * + * Like the larger cut_enumeration algorithm, this algorithm traverses all nodes + * in topological order and computes a node's cuts based on its fanins' cuts. + * Dominated cuts are filtered and are not added to the cut set. For each node a + * unit cut is added to the end of each cut set. + * + * This function computes all cuts of the network (i.e. the number of generated + * cuts is not bounded). Though the number of cuts cannot be bounded, their size + * can be bound by passing a `cut_size` argument to the function. + * + * **Required network functions:** + * - `fanin_size` + * - `foreach_fanin` + * - `foreach_gate` + * - `foreach_ci` + * - `get_node` + * - `node_to_index` + * - `size` + * + * Note that this algorithm *only* works for graphs with at most 64 nodes. + * However, since we cannot know the size of a graph at compile-time, this + * function returns the results wrapped in an std::optional. + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes one + can wrap the network parameter in a ``topo_view`` view. + \endverbatim + */ +template +std::optional>> +fast_small_cut_enumeration( Ntk const& ntk, const uint8_t cut_size = 4 ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + // You cannot check whether a graph is topologically sorted at compile-time, + // but the is_topologically_sorted_v template is used inside the + // topo_view class to determine whether the input graph should just be copied + // as it is already topologically-sorted, or whether the graph's topological + // order is to be computed. + // static_assert( is_topologically_sorted_v, "Ntk is not a topologically-sorted network" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + // Max 64 nodes, so 8 bits are enough for indices. + using node_idx_t = uint8_t; + using cut_t = uint64_t; + using cut_set_t = std::vector; + using cut_sets_t = std::vector; + + // It is not possible to know the size of a network at compile-time, so I will + // return a boolean flag stating whether the cut-sets returned by this + // function are valid or not. + constexpr node_idx_t max_nodes = 64; + if ( ntk.size() > max_nodes ) + { + return std::nullopt; + } + + ////////////////////////////////////////////////////////////////////////////// + // Final cut-sets to be computed ///////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // size() returns the number of nodes including constants, PIs, and dead + // nodes, so no need to allocate +1 memory slots like in the lecture notes + // to explicitly represent constants. + // By definition of the vector constructor, each cut-set is initialized to {}. + cut_sets_t cut_sets( ntk.size() ); + + ////////////////////////////////////////////////////////////////////////////// + // Helper functions ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + auto set_bit = []( + node_idx_t idx ) { + return static_cast( 1 ) << idx; + }; + + // Algorithm for counting #1 bits in a uint64_t. It is efficient as it only + // iterates as many times as the bit count to avoid always performing 64 + // iterations. Inspired from the following threads: + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive + // https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation + auto bit_cnt = []( + cut_t n ) { + uint8_t count = 0; + + while ( n > 0 ) + { + count = count + 1; + n = n & ( n - 1 ); + } + + return count; + }; + + // Operation to perform on each n-tuple. In the cut generation algorithm this + // involves computing a new cut from the fan-in nodes' selected cuts, checking + // whether an existing cut dominates it, and if not, adding it to the cutset + // of the current node. + auto visit_n_tuple = [&cut_sets, &bit_cnt, &cut_size]( + // Node for which we are computing the cut. + node_idx_t node_idx, + // Fan-in of the current node. + std::vector const& fanin_idx, + // Index of the cut to choose from the given fan-in node. + std::vector const& cut_idx ) { + // Compute new cut. + cut_t C = 0; + for ( auto i = 0U; i < fanin_idx.size(); i++ ) + { + C |= cut_sets.at( fanin_idx[i] ).at( cut_idx[i] ); + } + + // Restrict cut sizes if too large. + if ( bit_cnt( C ) > cut_size ) + { + return; + } + + // Don't add new cut if existing cut dominates it. A cut C' dominates + // another cut C if C' is a subset of C. Being a subset means that C' has at + // most the same bits set as C, but no more bits. + // + // So if we AND their bitsets together, we can see what they have in common, + // then we can XOR this with C' original bits to see if C' has any bit + // active that C does not. + // + // C' = 0b 00110 + // C = 0b 01010 AND + // -------- + // 0b 00010 + // C' = 0b 00110 XOR + // -------- + // 0b 00100 => C' does NOT dominate C as it contains a node that C does not. + for ( auto C_prime : cut_sets.at( node_idx ) ) + { + cut_t shared_nodes = C_prime & C; + cut_t C_prime_extra_nodes = shared_nodes ^ C_prime; + + bool C_prime_dominates_C = C_prime_extra_nodes == 0; + if ( C_prime_dominates_C ) + { + return; + } + } + + cut_sets.at( node_idx ).push_back( C ); + }; + + // Enumerates cuts of a given node. The inputs of the node can have variable + // fan-in, so the cut-sets they have could have different sizes. We therefore + // cannot use N nested for-loops to perform a cross product of the fan-in cuts + // since we don't know the cut-set sizes in advance. We instead use the + // mixed-radix n-tuple generation algorithm in TAOCP, Vol 4A, algorithm M. + auto cut_enumeration_node = [&cut_sets, &visit_n_tuple]( + Ntk const& ntk, + node const& node ) { + node_idx_t node_idx = ntk.node_to_index( node ); + + // Index of the node's fan-ins. + std::vector fanin_idx; + + // Number of cuts of a given fan-in node ("radix" in TAOCP, Vol 4A, Algorithm M). + std::vector cut_set_size; // radix (m[n-1], ... , m[0]) in TAOCP + + // Index of a cut of a given fan-in node ("value" in TAOCP, Vol 4A, Algorithm M). + std::vector cut_idx; // value (a[n-1], ... , a[0]) in TAOCP + + // We start by initializing the indices of the fan-in nodes' cuts and their + // radix. Need to get the the index of the fan-in nodes for this so we can + // query their number of cuts. + ntk.foreach_fanin( + node, + [&]( auto sig ) { + auto fanin_node = ntk.get_node( sig ); + auto fanin_node_idx = ntk.node_to_index( fanin_node ); + + fanin_idx.push_back( fanin_node_idx ); + // Radix of the given fan-in is determined by the number of cuts it has. + cut_set_size.push_back( cut_sets.at( fanin_node_idx ).size() ); + // Start counting from (0, 0, ... , 0) + cut_idx.push_back( 0 ); + } ); + + // Fan-in = number of cut-sets we must perform a cross-product over. + auto const num_cut_sets = ntk.fanin_size( node ); + + uint8_t j = 0; + while ( j != num_cut_sets ) + { + visit_n_tuple( node_idx, fanin_idx, cut_idx ); + + // Mixed-radix n-tuple generation algorithm. Adding 1 to the n-tuple. + j = 0; + while ( ( j != num_cut_sets ) && ( cut_idx.at( j ) == cut_set_size.at( j ) - 1 ) ) + { + cut_idx.at( j ) = 0; + j += 1; + } + if ( j != num_cut_sets ) + { + cut_idx.at( j ) += 1; + } + } + }; + + ////////////////////////////////////////////////////////////////////////////// + // Main algorithm //////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // Primary inputs only have themselves as their cut-set. + ntk.foreach_ci( + [&]( auto node ) { + auto const idx = ntk.node_to_index( node ); + cut_sets.at( idx ) = { set_bit( idx ) }; + } ); + + // Going through remaining gates (excluding constants and primary inputs). + ntk.foreach_gate( + [&]( auto node ) { + // Technically don't need to do this as vectors are constructed with zero + // length, but we leave it for clarity. + auto const idx = ntk.node_to_index( node ); + cut_sets.at( idx ) = {}; + + // Internally uses TAOCP Vol 4A algorithm M, mixed-radix n-tuple + // generation, to enumerate the cross-product of the node's fan-in + // cut-sets. + cut_enumeration_node( ntk, node ); + + cut_sets.at( idx ).push_back( set_bit( idx ) ); + } ); + + return cut_sets; +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/decomposition.hpp b/third-party/mockturtle/include/mockturtle/algorithms/decomposition.hpp new file mode 100644 index 00000000000..541de0b0e22 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/decomposition.hpp @@ -0,0 +1,301 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file decomposition.hpp + \brief Shannon and Davio decomposition + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../traits.hpp" +#include "node_resynthesis/null.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class shannon_decomposition_impl +{ +public: + shannon_decomposition_impl( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn ) + : ntk_( ntk ), + func_( func ), + vars_( vars ), + pis_( children ), + resyn_( resyn ) + { + cache_.insert( { func.construct(), ntk_.get_constant( false ) } ); + + for ( auto i = 0u; i < pis_.size(); ++i ) + { + auto var = func.construct(); + kitty::create_nth_var( var, i ); + cache_.insert( { func.construct(), pis_[i] } ); + } + } + + signal run() + { + return decompose( 0u, func_ ); + } + +private: + signal decompose( uint32_t var_index, kitty::dynamic_truth_table const& func ) + { + /* cache lookup... */ + auto it = cache_.find( func ); + if ( it != cache_.end() ) + { + return it->second; + } + + /* ...and for the complement */ + it = cache_.find( ~func ); + if ( it != cache_.end() ) + { + return ntk_.create_not( it->second ); + } + + signal f; + if ( var_index == vars_.size() ) + { + auto copy = func; + const auto support = kitty::min_base_inplace( copy ); + const auto small_func = kitty::shrink_to( copy, static_cast( support.size() ) ); + std::vector> small_pis( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + small_pis[i] = pis_[support[i]]; + } + resyn_( ntk_, small_func, small_pis.begin(), small_pis.end(), [&]( auto const& _f ) { + f = _f; + return false; + } ); + } + else + { + /* decompose */ + const auto f0 = decompose( var_index + 1, kitty::cofactor0( func, vars_[var_index] ) ); + const auto f1 = decompose( var_index + 1, kitty::cofactor1( func, vars_[var_index] ) ); + f = ntk_.create_ite( pis_[vars_[var_index]], f1, f0 ); + } + cache_.insert( { func, f } ); + return f; + } + +private: + Ntk& ntk_; + kitty::dynamic_truth_table func_; + std::vector vars_; + std::vector> const& pis_; + SynthesisFn const& resyn_; + std::unordered_map, kitty::hash> cache_; +}; + +template +class davio_decomposition_impl +{ +public: + davio_decomposition_impl( Ntk& ntk, bool polarity, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn ) + : ntk_( ntk ), + polarity_( polarity ), + func_( func ), + pis_( children ), + vars_( vars ), + resyn_( resyn ) + { + cache_.insert( { func.construct(), ntk_.get_constant( false ) } ); + + for ( auto i = 0u; i < pis_.size(); ++i ) + { + auto var = func.construct(); + kitty::create_nth_var( var, i ); + cache_.insert( { func.construct(), pis_[i] } ); + } + } + + signal run() + { + return decompose( 0u, func_ ); + } + +private: + signal decompose( uint32_t var_index, kitty::dynamic_truth_table const& func ) + { + /* cache lookup... */ + auto it = cache_.find( func ); + if ( it != cache_.end() ) + { + return it->second; + } + + /* ...and for the complement */ + it = cache_.find( ~func ); + if ( it != cache_.end() ) + { + return ntk_.create_not( it->second ); + } + + signal f; + if ( var_index == vars_.size() ) + { + auto copy = func; + const auto support = kitty::min_base_inplace( copy ); + const auto small_func = kitty::shrink_to( copy, static_cast( support.size() ) ); + std::vector> small_pis( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + small_pis[i] = pis_[support[i]]; + } + resyn_( ntk_, small_func, small_pis.begin(), small_pis.end(), [&]( auto const& _f ) { + f = _f; + return false; + } ); + } + else + { + /* decompose */ + const auto f0 = decompose( var_index + 1, kitty::cofactor0( func, vars_[var_index] ) ); + const auto f1 = decompose( var_index + 1, kitty::cofactor1( func, vars_[var_index] ) ); + + if ( polarity_ ) + { + f = ntk_.create_xor( f0, ntk_.create_and( pis_[vars_[var_index]], ntk_.create_xor( f0, f1 ) ) ); + } + else + { + f = ntk_.create_xor( f1, ntk_.create_and( ntk_.create_not( pis_[vars_[var_index]] ), ntk_.create_xor( f0, f1 ) ) ); + } + } + cache_.insert( { func, f } ); + return f; + } + +private: + Ntk& ntk_; + bool polarity_; + kitty::dynamic_truth_table func_; + std::vector> const& pis_; + std::vector const& vars_; + SynthesisFn const& resyn_; + std::unordered_map, kitty::hash> cache_; +}; + +} // namespace detail + +/*! \brief Shannon decomposition + * + * This function applies Shannon decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_ite` + * - `get_constant` + */ +template> +signal shannon_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::shannon_decomposition_impl impl( ntk, func, vars, children, resyn ); + return impl.run(); +} + +/*! \brief Positive Davio decomposition + * + * This function applies positive Davio decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_xor` + * - `get_constant` + */ +template> +signal positive_davio_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_ite method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::davio_decomposition_impl impl( ntk, true, func, vars, children, resyn ); + return impl.run(); +} + +/*! \brief Negative Davio decomposition + * + * This function applies positive Davio decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_xor` + * - `get_constant` + */ +template> +signal negative_davio_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_ite method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::davio_decomposition_impl impl( ntk, false, func, vars, children, resyn ); + return impl.run(); +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/algorithms/detail/mffc_utils.hpp b/third-party/mockturtle/include/mockturtle/algorithms/detail/mffc_utils.hpp new file mode 100644 index 00000000000..94da0450d94 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/detail/mffc_utils.hpp @@ -0,0 +1,135 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mffc_utils.hpp + \brief Utility functions for DAG-aware reference counting and + MFFC-size computation + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../../traits.hpp" +#include "../../utils/cost_functions.hpp" + +namespace mockturtle::detail +{ + +template +void initialize_values_with_fanout( Ntk& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + ntk.clear_values(); + ntk.foreach_node( [&]( auto const& n ) { + ntk.set_value( n, ntk.fanout_size( n ) ); + } ); +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n, TermCond const& terminate ) +{ + /* terminate? */ + if ( terminate( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}( ntk, n ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_value( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref( ntk, ntk.get_node( s ), terminate ); + } + } ); + return value; +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n, TermCond const& terminate ) +{ + /* terminate? */ + if ( terminate( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}( ntk, n ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.incr_value( ntk.get_node( s ) ) == 0 ) + { + value += recursive_ref( ntk, ntk.get_node( s ), terminate ); + } + } ); + return value; +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n, LeavesIterator begin, LeavesIterator end ) +{ + const auto terminate = [&]( auto const& n ) { return std::find( begin, end, n ) != end; }; + return recursive_deref( ntk, n, terminate ); +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n, LeavesIterator begin, LeavesIterator end ) +{ + const auto terminate = [&]( auto const& n ) { return std::find( begin, end, n ) != end; }; + return recursive_ref( ntk, n, terminate ); +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n ) +{ + const auto terminate = [&]( auto const& n ) { return ntk.is_constant( n ) || ntk.is_pi( n ); }; + return recursive_deref( ntk, n, terminate ); +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n ) +{ + const auto terminate = [&]( auto const& n ) { return ntk.is_constant( n ) || ntk.is_pi( n ); }; + return recursive_ref( ntk, n, terminate ); +} + +template> +uint32_t mffc_size( Ntk const& ntk, node const& n ) +{ + auto v1 = recursive_deref( ntk, n ); + auto v2 = recursive_ref( ntk, n ); + assert( v1 == v2 ); + (void)v2; + return v1; +} + +} /* namespace mockturtle::detail */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/detail/switching_activity.hpp b/third-party/mockturtle/include/mockturtle/algorithms/detail/switching_activity.hpp new file mode 100644 index 00000000000..c5df29cba5a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/detail/switching_activity.hpp @@ -0,0 +1,70 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file switching_activity.hpp + \brief Utility to compute the switching activity + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include "../simulation.hpp" + +#include +#include + +namespace mockturtle::detail +{ + +/*! \brief Switching Activity. + * + * This function computes the switching activity for each node + * in the network by performing random simulation. + * + * \param ntk Network + * \param simulation_size Number of simulation bits + */ +template +std::vector switching_activity( Ntk const& ntk, unsigned simulation_size = 2048 ) +{ + std::vector sw_map( ntk.size() ); + partial_simulator sim( ntk.num_pis(), simulation_size ); + + auto tts = simulate_nodes( ntk, sim ); + + ntk.foreach_node( [&]( auto const& n ) { + float ones = static_cast( kitty::count_ones( tts[n] ) ); + float activity = 2.0 * ones / simulation_size * ( simulation_size - ones ) / simulation_size; + sw_map[ntk.node_to_index( n )] = activity; + } ); + + return sw_map; +} + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/emap.hpp b/third-party/mockturtle/include/mockturtle/algorithms/emap.hpp new file mode 100644 index 00000000000..44b257e1601 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/emap.hpp @@ -0,0 +1,6052 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2024 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file emap.hpp + \brief An extended technology mapper + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" + +#include "../networks/aig.hpp" +#include "../networks/block.hpp" +#include "../networks/klut.hpp" +#include "../utils/cuts.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/tech_library.hpp" +#include "../views/binding_view.hpp" +#include "../views/cell_view.hpp" +#include "../views/choice_view.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "detail/mffc_utils.hpp" +#include "detail/switching_activity.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for emap. + * + * The data structure `emap_params` holds configurable parameters + * with default arguments for `emap`. + */ +struct emap_params +{ + emap_params() + { + cut_enumeration_ps.cut_limit = 16; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 16. + * The maximum cut limit is 19. + * By default, truth table minimization + * is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Do area-oriented mapping. */ + bool area_oriented_mapping{ false }; + + /*! \brief Maps using multi-output gates */ + bool map_multioutput{ false }; + + /*! \brief Matching mode + * + * Boolean uses Boolean matching (up to 6-input cells), + * Structural uses pattern matching for fully-DSD cells, + * Hybrid combines the two. + */ + enum matching_mode_t + { + boolean, + structural, + hybrid + } matching_mode = hybrid; + + /*! \brief Target required time (for each PO). */ + double required_time{ 0.0f }; + + /*! \brief Required time relaxation in percentage (10 = 10%). */ + double relax_required{ 0.0f }; + + /*! \brief Custom input arrival times. */ + std::vector arrival_times{}; + + /*! \brief Custom output required times. */ + std::vector required_times{}; + + /*! \brief Number of rounds for area flow optimization. */ + uint32_t area_flow_rounds{ 3u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t ela_rounds{ 2u }; + + /*! \brief Number of rounds for exact switching power optimization. */ + uint32_t eswp_rounds{ 0u }; + + /*! \brief Number of patterns for switching activity computation. */ + uint32_t switching_activity_patterns{ 2048u }; + + /*! \brief Compute area-oriented alternative matches */ + bool use_match_alternatives{ true }; + + /*! \brief Remove the cuts that are contained in others */ + bool remove_dominated_cuts{ false }; + + /*! \brief Remove overlapping multi-output cuts */ + bool remove_overlapping_multicuts{ false }; + + bool create_po_buffers { false }; + + bool insert_buffers { false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for emap. + * + * The data structure `emap_stats` provides data collected by running + * `emap`. + */ +struct emap_stats +{ + /*! \brief Area result. */ + double area{ 0 }; + /*! \brief Worst delay result. */ + double delay{ 0 }; + /*! \brief Power result. */ + double power{ 0 }; + /*! \brief Power result. */ + uint32_t inverters{ 0 }; + + /*! \brief Mapped multi-output gates. */ + uint32_t multioutput_gates{ 0 }; + + /*! \brief Runtime for multi-output matching. */ + stopwatch<>::duration time_multioutput{ 0 }; + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Cut enumeration stats. */ + cut_enumeration_stats cut_enumeration_st{}; + + /*! \brief Delay and area stats for each round. */ + std::vector round_stats{}; + + /*! \brief Mapping error. */ + bool mapping_error{ false }; + + void report() const + { + for ( auto const& stat : round_stats ) + { + std::cout << stat; + } + std::cout << fmt::format( "[i] Area = {:>5.2f}; Delay = {:>5.2f};", area, delay ); + if ( power != 0 ) + std::cout << fmt::format( " Power = {:>5.2f};\n", power ); + else + std::cout << "\n"; + if ( multioutput_gates ) + { + std::cout << fmt::format( "[i] Multi-output gates = {:>5}\n", multioutput_gates ); + std::cout << fmt::format( "[i] Multi-output runtime = {:>5.2f} secs\n", to_seconds( time_multioutput ) ); + } + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +struct cut_enumeration_emap_cut +{ + /* stats */ + uint32_t delay; + float flow; + bool ignore; + + /* pattern index for structural matching*/ + uint32_t pattern_index; + + /* function */ + kitty::static_truth_table function; + + /* list of supergates matching the cut for positive and negative output phases */ + std::array> const*, 2> supergates; + /* input negations, 0: pos, 1: neg */ + std::array negations; +}; + +struct cut_enumeration_emap_multi_cut +{ + /* stats */ + uint64_t id{ 0 }; +}; + +enum class emap_cut_sort_type +{ + DELAY = 0, + DELAY2 = 1, + AREA = 2, + AREA2 = 3, + NONE = 4 +}; + +template +class emap_cut_set +{ +public: + /*! \brief Standard constructor. + */ + emap_cut_set() + { + clear(); + } + + /*! \brief Assignment operator. + */ + emap_cut_set& operator=( emap_cut_set const& other ) + { + if ( this != &other ) + { + _pcend = _pend = _pcuts.begin(); + _set_limit = other._set_limit; + + auto it = other.begin(); + while ( it != other.end() ) + { + **_pend++ = **it++; + ++_pcend; + } + } + + return *this; + } + + /*! \brief Clears a cut set. + */ + void clear() + { + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } + } + + /*! \brief Sets the cut limit. + */ + void set_cut_limit( uint32_t limit ) + { + _set_limit = std::min( MaxCuts, limit ); + } + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ) + { + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; + } + + /*! \brief Appends a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param cut Cut to insert + */ + void append_cut( CutType const& cut ) + { + assert( _pend != _pcuts.end() ); + + **_pend++ = cut; + ++_pcend; + } + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const + { + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; + } + + static bool sort_delay( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + return c1.size() < c2.size(); + } + + static bool sort_delay2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + return c1->flow < c2->flow - eps; + } + + static bool sort_area( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + return c1->delay < c2->delay - eps; + } + + static bool sort_area2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + return c1.size() < c2.size(); + } + + /*! \brief Compare two cuts using sorting functions. + * + * This method compares two cuts using a sorting function. + * + * \param cut1 first cut. + * \param cut2 second cut. + * \param sort sorting function. + */ + static bool compare( CutType const& cut1, CutType const& cut2, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + if ( sort == emap_cut_sort_type::DELAY ) + { + return sort_delay( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::DELAY2 ) + { + return sort_delay2( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::AREA ) + { + return sort_area( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::AREA2 ) + { + return sort_area2( cut1, cut2 ); + } + else + { + return false; + } + } + + static bool same_cut( CutType const& cut1, CutType const& cut2 ) + { + return cut1.size() == cut2.size() && cut1.signature() == cut2.signature() + && std::equal( cut1.begin(), cut1.end(), cut2.begin() ); + } + + /*! \brief Inserts a cut into a set without checking dominance. + * + * This method will insert a cut into a set and maintain an order. This + * method doesn't remove the cuts that are dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param sort Cut prioritization function. + */ + void simple_insert( CutType const& cut, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + /* insert cut in a sorted way */ + typename std::array::iterator ipos = _pcuts.begin(); + + bool limit_reached = std::distance( _pcuts.begin(), _pend ) >= _set_limit; + + /* do not insert if worst than set_limit */ + if ( limit_reached ) + { + if ( sort == emap_cut_sort_type::AREA && !sort_area( cut, **( ipos + _set_limit - 1 ) ) ) + { + return; + } + else if ( sort != emap_cut_sort_type::AREA ) + { + return; + } + } + + if ( sort == emap_cut_sort_type::NONE ) + { + ipos = _pend; + } + else /* AREA */ + { + ipos = std::upper_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area( *a, *b ); } ); + } + + /* check for redundant cut */ + typename std::array::iterator jpos = ipos; + if ( cut->ignore ) + { + while ( jpos != _pcuts.begin() ) + { + --jpos; + if ( ( *jpos )->size() < cut.size() ) + break; + if ( same_cut( cut, **jpos ) ) + return; + } + } + else if ( ipos != _pcuts.begin() ) + { + if ( same_cut( cut, **( ipos - 1 ) ) ) + { + return; + } + } + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() || limit_reached ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; + } + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. Variable `skip0` tell to skip the dominance check on + * cut zero. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param skip0 Skip dominance check on cut zero. + * \param sort Cut prioritization function. + */ + void insert( CutType const& cut, bool skip0 = false, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + auto begin = _pcuts.begin(); + + if ( skip0 && _pend != _pcuts.begin() ) + ++begin; + + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( begin, _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + simple_insert( cut, sort ); + } + + /*! \brief Replaces a cut of the set. + * + * This method replaces the cut at position `index` in the set by `cut` + * and maintains the cuts order. The function does not check whether + * index is in the valid range. + * + * \param index Index of the cut to replace. + * \param cut Cut to insert. + */ + void replace( uint32_t index, CutType const& cut ) + { + *_pcuts[index] = cut; + } + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto const& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ) + { + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; + } + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ) + { + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } + } + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, emap_cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + + /*! \brief Returns if the cut set contains already `cut`. */ + bool is_contained( CutType const& cut ) + { + typename std::array::iterator ipos = _pcuts.begin(); + + while ( ipos != _pend ) + { + if ( same_cut( cut, **ipos ) ) + return true; + ++ipos; + } + + return false; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; + uint32_t _set_limit{ MaxCuts }; +}; + +template +struct emap_triple_hash +{ + inline uint64_t operator()( const std::array& p ) const + { + uint64_t seed = hash_block( p[0] ); + + for ( uint32_t i = 1; i < max_multioutput_cut_size; ++i ) + { + hash_combine( seed, hash_block( p[i] ) ); + } + + return seed; + } +}; + +template +struct best_gate_emap +{ + supergate const* gate; + double arrival; + float area; + float flow; + unsigned phase : 16; + unsigned cut : 12; + unsigned size : 4; +}; + +template +struct node_match_emap +{ + /* best gate match for positive and negative output phases */ + supergate const* best_gate[2]; + /* alternative best gate for positibe and negative output phase */ + best_gate_emap best_alternative[2]; + /* fanin pin phases for both output phases */ + uint16_t phase[2]; + /* best cut index for both phases */ + uint16_t best_cut[2]; + /* node is mapped using only one phase */ + bool same_match; + /* node is mapped to a multi-output gate */ + bool multioutput_match[2]; + + /* arrival time at node output */ + double arrival[2]; + /* required time at node output */ + double required[2]; + /* area of the best matches */ + float area[2]; + + /* number of references in the cover 0: pos, 1: neg */ + uint32_t map_refs[2]; + /* references estimation */ + float est_refs[2]; + /* area flow */ + float flows[2]; +}; + +template +class emap_impl +{ +private: + union multi_match_data + { + uint64_t data{ 0 }; + struct + { + uint64_t in_tfi : 1; + uint64_t cut_index : 31; + uint64_t node_index : 32; + }; + }; + union multioutput_info + { + uint32_t data; + struct + { + unsigned index : 29; + unsigned lowest_index : 1; + unsigned highest_index : 1; + unsigned has_info : 1; + }; + }; + +public: + static constexpr float epsilon = 0.005; + static constexpr uint32_t max_cut_num = 20; + using cut_t = cut>; + using cut_set_t = emap_cut_set; + using cut_merge_t = typename std::array; + using fanin_cut_t = typename std::array; + using support_t = typename std::array; + using TT = kitty::static_truth_table; + using truth_compute_t = typename std::array; + using node_match_t = std::vector>; + using klut_map = std::unordered_map, 2>>; + using block_map = std::unordered_map, 2>>; + + static constexpr uint32_t max_multioutput_cut_size = 3; + static constexpr uint32_t max_multioutput_output_size = 2; + using multi_cuts_t = fast_network_cuts; + using multi_cut_t = typename multi_cuts_t::cut_t; + using multi_leaves_set_t = std::array; + using multi_output_set_t = std::vector; + using multi_hash_t = absl::flat_hash_map>; + using multi_match_t = std::array; + using multi_cut_set_t = std::vector>; + using multi_single_matches_t = std::vector; + using multi_matches_t = std::vector>; + + using clock = typename std::chrono::steady_clock; + using time_point = typename clock::time_point; + +public: + explicit emap_impl( Ntk const& ntk, tech_library const& library, emap_params const& ps, emap_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + node_tuple_match( ntk.size() ), + switch_activity( ps.eswp_rounds ? switching_activity( ntk, ps.switching_activity_patterns ) : std::vector( 0 ) ), + cuts( ntk.size() ) + { + std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() ); + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + tmp_visited.reserve( 100 ); + } + + explicit emap_impl( Ntk const& ntk, tech_library const& library, std::vector const& switch_activity, emap_params const& ps, emap_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + node_tuple_match( ntk.size() ), + switch_activity( switch_activity ), + cuts( ntk.size() ) + { + std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() ); + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + tmp_visited.reserve( 100 ); + } + + cell_view run_block() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_block_network(); + + /* multi-output initialization */ + if ( ps.map_multioutput && ps.matching_mode != emap_params::structural ) + { + compute_multioutput_match(); + } + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* search for large matches */ + if ( ps.matching_mode == emap_params::structural || CutSize > 6 ) + { + if ( !compute_struct_match() ) + { + return res; + } + } + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + if (ps.insert_buffers) + insert_buffers(); + + /* generate the output network */ + finalize_cover_block( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + + binding_view run_klut() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_map_network(); + + /* multi-output initialization */ + if ( ps.map_multioutput && ps.matching_mode != emap_params::structural ) + { + compute_multioutput_match(); + } + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* search for large matches */ + if ( ps.matching_mode == emap_params::structural || CutSize > 6 ) + { + if ( !compute_struct_match() ) + { + return res; + } + } + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + if (ps.insert_buffers) + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + + binding_view run_node_map() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_map_network(); + + /* [i] multi-output support is currently not implemented */ + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match_node() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match_node() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + if (ps.insert_buffers) + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + +private: + bool improve_mapping() + { + /* compute mapping using global area flow */ + uint32_t i = 0; + while ( i++ < ps.area_flow_rounds ) + { + if ( !compute_mapping() ) + { + return false; + } + } + + /* compute mapping using exact area */ + i = 0; + compute_required_time( true ); + while ( i++ < ps.ela_rounds ) + { + if ( !compute_mapping_exact_reversed() ) + { + return false; + } + } + + /* compute mapping using exact switching activity estimation */ + i = 0; + while ( i++ < ps.eswp_rounds ) + { + if ( !compute_mapping_exact_reversed() ) + { + return false; + } + } + + return true; + } + + template + bool compute_mapping_match() + { + bool warning_box = false; + + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + + if ( !compute_matches_node( n, warning_box ) ) + { + continue; + } + + /* load multi-output cuts and data */ + if ( ps.map_multioutput && node_tuple_match[index].has_info ) + { + match_multi_add_cuts( n ); + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* select alternative matches to use */ + select_alternatives( n ); + + /* try multi-output matches */ + if constexpr ( DO_AREA ) + { + if ( ps.map_multioutput && node_tuple_match[index].highest_index ) + { + if ( match_multioutput( n ) ) + multi_node_update( n ); + } + } + } + + double area_old = area; + bool success = set_mapping_refs_and_req(); + + if ( warning_box ) + { + std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n"; + } + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + inline bool compute_matches_node( node const& n, bool& warning_box ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.est_refs[0] = node_data.est_refs[1] = static_cast( ntk.fanout_size( n ) ); + node_data.map_refs[0] = node_data.map_refs[1] = 0; + node_data.required[0] = node_data.required[1] = std::numeric_limits::max(); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 0.0 */ + node_data.flows[0] = node_data.flows[1] = 0.0f; + node_data.best_alternative[0].flow = node_data.best_alternative[1].flow = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + node_data.best_alternative[0].arrival = node_data.best_alternative[1].arrival = 0.0f; + /* skip if cuts have been computed before */ + if ( cuts[index].size() == 0 ) + { + add_zero_cut( index ); + match_constants( index ); + } + return false; + } + else if ( ntk.is_pi( n ) ) + { + node_data.flows[0] = 0.0f; + node_data.best_alternative[0].flow = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.flows[1] = lib_inv_area / node_data.est_refs[1]; + node_data.best_alternative[1].flow = lib_inv_area / node_data.est_refs[1]; + /* skip if cuts have been computed before */ + if ( cuts[index].size() == 0 ) + { + add_unit_cut( index ); + } + return false; + } + + if ( ps.matching_mode == emap_params::structural ) + return true; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + warning_box |= initialize_box( n ); + return false; + } + } + + /* compute cuts for node */ + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( n ); + } + else + { + merge_cuts( n ); + } + + return true; + } + + template + void merge_cuts2( node const& n ) + { + static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize; + + auto index = ntk.node_to_index( n ); + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + + /* compute cuts */ + const auto fanin = 2; + ntk.foreach_fanin( ntk.index_to_node( index ), [this]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* move pre-computed structural cuts to a temporary cutset */ + bool reinsert_cuts = false; + if ( rcuts.size() ) + { + temp_cuts.clear(); + for ( auto& cut : rcuts ) + { + if ( ( *cut )->ignore ) + continue; + recompute_cut_data( *cut, n ); + temp_cuts.simple_insert( *cut ); + reinsert_cuts = true; + } + rcuts.clear(); + } + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + + cut_t new_cut; + new_cut->pattern_index = 0; + fanin_cut_t vcuts; + + for ( auto const& c1 : *lcuts[0] ) + { + /* skip cuts of pattern matching */ + if ( ( *c1 )->pattern_index > 1 ) + continue; + vcuts[0] = c1; + + for ( auto const& c2 : *lcuts[1] ) + { + /* skip cuts of pattern matching */ + if ( ( *c2 )->pattern_index > 1 ) + continue; + + if ( !c1->merge( *c2, new_cut, max_cut_size ) ) + { + continue; + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + continue; + } + + /* compute function */ + vcuts[1] = c2; + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + if ( reinsert_cuts ) + { + for ( auto const& cut : temp_cuts ) + { + rcuts.simple_insert( *cut, sort ); + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + } + + template + void merge_cuts( node const& n ) + { + static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + cut_t best_cut; + + /* compute cuts */ + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + } ); + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + fanin_cut_t vcuts; + + if ( fanin > 1 && fanin <= ps.cut_enumeration_ps.fanin_limit ) + { + cut_t new_cut, tmp_cut; + + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, max_cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, max_cut_size ) ) + { + return true; /* continue */ + } + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + else if ( fanin == 1 ) + { + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + vcuts[0] = cut; + + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + + cuts_total += rcuts.size(); + + add_unit_cut( index ); + } + + bool compute_struct_match() + { + if ( ps.matching_mode == emap_params::boolean ) + return true; + + /* compatible only with AIGs */ + if constexpr ( !is_aig_network_type_v ) + { + if ( ps.matching_mode == emap_params::structural ) + { + std::cerr << "[e] MAP ERROR: structural library works only with AIGs\n"; + return false; + } + return true; + } + + /* no large gates identified */ + if ( library.num_structural_gates() == 0 ) + { + if ( ps.matching_mode == emap_params::structural ) + { + std::cerr << "[e] MAP ERROR: structural library is empty\n"; + return false; + } + return true; + } + + bool warning_box = false; + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + + if ( ntk.is_constant( n ) ) + { + add_zero_cut( index ); + match_constants( index ); + continue; + } + if ( ntk.is_pi( n ) ) + { + add_unit_cut( index ); + continue; + } + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + add_unit_cut( index ); + continue; + } + } + + /* compute cuts for node */ + merge_cuts_structural( n ); + } + + if ( warning_box ) + { + std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n"; + } + + /* round stats */ + if ( ps.verbose ) + { + st.round_stats.push_back( fmt::format( "[i] SCuts : Cuts = {:>12d} Time = {:>12.2f}\n", cuts_total, to_seconds( clock::now() - time_begin ) ) ); + } + + return true; + } + + void merge_cuts_structural( node const& n ) + { + auto index = ntk.node_to_index( n ); + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + + /* compute cuts */ + const auto fanin = 2; + std::array children_phase{}; + ntk.foreach_fanin( ntk.index_to_node( index ), [&]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + children_phase[i] = ntk.is_complemented( child ) ? 1 : 0; + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + + cut_t new_cut; + std::vector vcuts( fanin ); + + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + /* filter large cuts */ + if ( c1->size() + c2->size() > CutSize || c1->size() + c2->size() > NInputs ) + continue; + /* filter cuts involving constants */ + if ( ( *c1 )->pattern_index == 0 || ( *c2 )->pattern_index == 0 ) + continue; + + vcuts[0] = c1; + vcuts[1] = c2; + uint32_t pattern_id1 = ( ( *c1 )->pattern_index << 1 ) | children_phase[0]; + uint32_t pattern_id2 = ( ( *c2 )->pattern_index << 1 ) | children_phase[1]; + if ( pattern_id1 > pattern_id2 ) + { + std::swap( vcuts[0], vcuts[1] ); + std::swap( pattern_id1, pattern_id2 ); + } + + uint32_t new_pattern = library.get_pattern_id( pattern_id1, pattern_id2 ); + + /* pattern not matched */ + if ( new_pattern == UINT32_MAX ) + continue; + + create_structural_cut( new_cut, vcuts, new_pattern, pattern_id1, pattern_id2 ); + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + continue; + + /* match cut and compute data */ + compute_cut_data_structural( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + } + + template + bool compute_mapping_match_node() + { + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.best_gate[0] = node_data.best_gate[1] = nullptr; + node_data.same_match = 0; + node_data.multioutput_match[0] = node_data.multioutput_match[1] = false; + node_data.required[0] = node_data.required[1] = std::numeric_limits::max(); + node_data.map_refs[0] = node_data.map_refs[1] = 0; + node_data.est_refs[0] = node_data.est_refs[1] = static_cast( ntk.fanout_size( n ) ); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 0 */ + node_data.flows[0] = node_data.flows[1] = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + add_zero_cut( index ); + match_constants( index ); + continue; + } + else if ( ntk.is_pi( n ) ) + { + /* all terminals have flow 0 */ + node_data.flows[0] = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.flows[1] = lib_inv_area / node_data.est_refs[1]; + add_unit_cut( index ); + continue; + } + + /* compute the node mapping */ + add_node_cut( n ); + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* select alternative matches to use */ + select_alternatives( n ); + } + double area_old = area; + bool success = set_mapping_refs_and_req(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + void add_node_cut( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto& rcuts = &cuts[index]; + + std::vector fanin_indexes; + fanin_indexes.reserve( Ntk::max_fanin_size ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + fanin_indexes.push_back( ntk.node_to_index( ntk.get_node( f ) ) ); + } ); + + assert( fanin_indexes.size() <= CutSize ); + + cut_t new_cut = rcuts.add_cut( fanin_indexes.begin(), fanin_indexes.end() ); + new_cut->function = kitty::extend_to( ntk.node_function( n ) ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + ++cuts_total; + } + + template + bool compute_mapping() + { + for ( auto const& n : topo_order ) + { + uint32_t index = ntk.node_to_index( n ); + + /* reset mapping */ + node_match[index].map_refs[0] = node_match[index].map_refs[1] = 0u; + node_match[index].arrival[0] = node_match[index].arrival[1] = 0.0; + node_match[index].required[0] = node_match[index].required[1] = std::numeric_limits::max(); + + if ( ntk.is_constant( n ) ) + continue; + if ( ntk.is_pi( n ) ) + { + node_match[index].flows[1] = lib_inv_area / node_match[index].est_refs[1]; + node_match[index].best_alternative[1].flow = lib_inv_area / node_match[index].est_refs[1]; + continue; + } + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + } + continue; + } + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* try a multi-output match */ + if constexpr ( DO_AREA ) + { + if ( ps.map_multioutput && node_tuple_match[index].highest_index ) + { + bool multi_success = match_multioutput( n ); + if ( multi_success ) + multi_node_update( n ); + } + } + + assert( node_match[index].arrival[0] < node_match[index].required[0] + epsilon ); + assert( node_match[index].arrival[1] < node_match[index].required[1] + epsilon ); + } + + double area_old = area; + bool success = set_mapping_refs_and_req(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + bool compute_mapping_exact_reversed() + { + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_constant( *it ) || ntk.is_pi( *it ) ) + continue; + + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip not mapped nodes */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_backward_white_box( n ); + } + continue; + } + } + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + double old_required = -1; + if ( node_data.same_match ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, *it, use_phase ); + + /* propagate required time over the output inverter if present */ + if ( node_data.map_refs[use_phase ^ 1] > 0 ) + { + old_required = node_data.required[use_phase]; + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[use_phase ^ 1] - lib_inv_delay ); + } + } + else if ( !node_data.map_refs[0] || !node_data.map_refs[1] ) + { + use_phase = node_data.map_refs[0] ? 0 : 1; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, *it, use_phase ); + node_data.same_match = true; + } + + /* match positive phase */ + match_phase_exact( *it, 0u ); + + /* match negative phase */ + match_phase_exact( *it, 1u ); + + /* restore required time */ + if ( old_required > 0 ) + { + node_data.required[use_phase] = old_required; + } + + /* try to drop one phase */ + match_drop_phase( *it ); + + /* try a multi-output match */ /* TODO: fix the required time*/ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index ) + { + bool mapped = match_multioutput_exact( *it, true ); + + /* propagate required time for the selected gates */ + if ( mapped ) + { + match_multioutput_propagate_required( *it ); + } + else + { + match_propagate_required( index ); + } + } + else + { + match_propagate_required( index ); + } + } + + double area_old = area; + + propagate_arrival_times(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + if constexpr ( SwitchActivity ) + stats << fmt::format( "[i] Switching: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + else + stats << fmt::format( "[i] Area Rev : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + st.round_stats.push_back( stats.str() ); + } + + return true; + } + + inline void match_propagate_required( uint32_t index ) + { + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_backward_white_box( n ); + } + return; + } + } + + auto& node_data = node_match[index]; + + /* propagate required time through the leaves */ + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + unsigned other_phase = use_phase ^ 1; + + assert( node_data.best_gate[0] != nullptr || node_data.best_gate[1] != nullptr ); + // assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay ); + } + + if ( node_data.map_refs[0] ) + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + if ( node_data.map_refs[1] ) + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + auto ctr = 0u; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto const& supergate = node_data.best_gate[use_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[use_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + + if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + auto ctr = 0u; + auto const& best_cut = cuts[index][node_data.best_cut[other_phase]]; + auto const& supergate = node_data.best_gate[other_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[other_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + } + + template + bool set_mapping_refs() + { + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + inv = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + { + std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_pi( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( *it ) ) + { + set_mapping_refs_dont_touch( *it ); + continue; + } + } + + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + + if ( node_data.best_gate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_data.map_refs[use_phase]; + } + area += lib_inv_area; + ++inv; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + } + + ++iteration; + + if constexpr ( ELA ) + { + return true; + } + + /* blend estimated references */ + float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) ); + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] ); + node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] ); + } + + return true; + } + + template + bool set_mapping_refs_and_req() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + set_output_required_time( iteration == 0 ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + inv = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + { + std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_pi( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( *it ) ) + { + set_mapping_refs_dont_touch( *it ); + continue; + } + } + + /* refine best matches with alternatives */ + if constexpr ( !DO_AREA ) + { + if ( ps.use_match_alternatives ) + refine_best_matches( *it ); + } + + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + if ( node_data.best_gate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_data.map_refs[use_phase]; + } + area += lib_inv_area; + ++inv; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + + if ( !ps.area_oriented_mapping ) + { + match_propagate_required( index ); + } + } + + ++iteration; + + if constexpr ( ELA ) + { + return true; + } + + /* blend estimated references */ + float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) ); + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] ); + node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] ); + } + + return true; + } + + template + inline void set_mapping_refs_dont_touch( node const& n ) + { + if constexpr ( !ELA ) + { + /* reference node */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + uint32_t leaf = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + node_match[leaf].map_refs[phase]++; + } ); + } + + const auto index = ntk.node_to_index( n ); + + if constexpr ( has_has_binding_v ) + { + /* increase area */ + area += node_match[index].area[0]; + if ( node_match[index].map_refs[1] ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_match[index].map_refs[0]; + } + area += lib_inv_area; + ++inv; + } + } + } + + void set_output_required_time( bool warning ) + { + double required = delay; + /* relax delay constraints */ + if ( iteration == 0 && ps.required_time == 0.0f && ps.required_times.empty() && ps.relax_required > 0.0f ) + { + required *= ( 100.0 + ps.relax_required ) / 100.0; + } + + /* Global target time constraint */ + if ( ps.required_times.empty() ) + { + if ( ps.required_time != 0.0f ) + { + if ( ps.required_time < delay - epsilon ) + { + if ( warning ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl; + } + else + { + required = ps.required_time; + } + } + + /* set the required time at POs */ + ntk.foreach_po( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = required; + else + node_match[index].required[0] = required; + } ); + + return; + } + + /* Output-specific target time constraint */ + ntk.foreach_po( [&]( auto const& s, uint32_t i ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + uint8_t phase = ntk.is_complemented( s ) ? 1 : 0; + if ( node_match[index].arrival[phase] > ps.required_times[i] + epsilon ) + { + /* maintain the same delay */ + node_match[index].required[phase] = node_match[index].arrival[phase]; + if ( warning ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f} at output {}", ps.required_times[i], i ) << std::endl; + } + else + { + node_match[index].required[phase] = ps.required_times[i]; + } + } ); + } + + void compute_required_time( bool exit_early = false ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* return if mapping is area oriented */ + if ( ps.area_oriented_mapping ) + return; + + set_output_required_time( iteration == 1 ); + + if ( exit_early ) + return; + + /* propagate required time to the PIs */ + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_pi( *it ) || ntk.is_constant( *it ) ) + break; + + const auto index = ntk.node_to_index( *it ); + + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + match_propagate_required( index ); + } + } + + void propagate_arrival_times() + { + area = 0.0f; + inv = 0; + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* measure area */ + if ( ntk.is_constant( n ) ) + { + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* reset required time */ + node_data.required[0] = std::numeric_limits::max(); + node_data.required[1] = std::numeric_limits::max(); + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) + area += node_data.area[0]; + if ( node_data.map_refs[1] ) + { + area += lib_inv_area; + ++inv; + } + } + continue; + } + } + + // guard against unmapped nodes: if neither phase has a best gate, skip + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + { + // no valid mapping stored for this node (e.g., not in cover); skip arrival/area. + continue; + } + + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + + /* compute arrival of use_phase */ + supergate const* best_gate = node_data.best_gate[use_phase]; + double worst_arrival = 0; + uint16_t best_phase = node_data.phase[use_phase]; + auto ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + + /* compute area */ + if ( node_data.map_refs[use_phase] > 0 || ( node_data.same_match && ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) ) ) + { + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + area += lib_inv_area; + ++inv; + } + } + + /* compute arrival of the other phase */ + use_phase ^= 1; + if ( node_data.same_match ) + { + node_data.arrival[use_phase] = worst_arrival + lib_inv_delay; + continue; + } + + assert( node_data.best_gate[use_phase] != nullptr ); + + best_gate = node_data.best_gate[use_phase]; + worst_arrival = 0; + best_phase = node_data.phase[use_phase]; + ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + + if ( node_data.map_refs[use_phase] > 0 ) + { + area += node_data.area[use_phase]; + } + } + + /* compute the current worst delay */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + } ); + + /* return if mapping is area oriented */ + ++iteration; + if ( ps.area_oriented_mapping ) + return; + + /* set the required time at POs */ + ntk.foreach_po( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = delay; + else + node_match[index].required[0] = delay; + } ); + } + + void propagate_arrival_node( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + + /* compute arrival of use_phase */ + supergate const* best_gate = node_data.best_gate[use_phase]; + double worst_arrival = 0; + uint16_t best_phase = node_data.phase[use_phase]; + auto ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + node_data.arrival[use_phase] = worst_arrival; + + /* compute arrival of the other phase */ + use_phase ^= 1; + if ( node_data.same_match ) + { + node_data.arrival[use_phase] = worst_arrival + lib_inv_delay; + return; + } + + assert( node_data.best_gate[0] != nullptr ); + + best_gate = node_data.best_gate[use_phase]; + worst_arrival = 0; + best_phase = node_data.phase[use_phase]; + ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + } + + template + void match_phase( node const& n, uint8_t phase ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + uint32_t cut_index = 0u; + + node_data.best_gate[phase] = nullptr; + node_data.arrival[phase] = std::numeric_limits::max(); + node_data.flows[phase] = std::numeric_limits::max(); + node_data.area[phase] = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + + best_gate_emap& gA = node_data.best_alternative[phase]; + gA.gate = nullptr; + gA.arrival = std::numeric_limits::max(); + gA.flow = std::numeric_limits::max(); + uint32_t best_sizeA = UINT32_MAX; + + /* unmap multioutput */ + node_data.multioutput_match[phase] = false; + + /* foreach cut */ + for ( auto& cut : cuts[index] ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = ( *cut )->supergates; + auto const negation = ( *cut )->negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint16_t gate_polarity = gate.polarity ^ negation; + double worst_arrival = 0.0f; + double worst_arrivalA = 0.0f; + float area_local = gate.area; + float area_localA = gate.area; + + auto ctr = 0u; + for ( auto l : *cut ) + { + uint8_t leaf_phase = ( gate_polarity >> ctr ) & 1; + + double arrival_pinA = node_match[l].best_alternative[leaf_phase].arrival + gate.tdelay[ctr]; + worst_arrivalA = std::max( worst_arrivalA, arrival_pinA ); + + // if constexpr ( DO_AREA ) + // { + // if ( worst_arrivalA > node_data.required[phase] + epsilon || worst_arrivalA >= std::numeric_limits::max() ) + // break; + // } + + double arrival_pin = node_match[l].arrival[leaf_phase] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + + area_local += node_match[l].flows[leaf_phase]; + area_localA += node_match[l].best_alternative[leaf_phase].flow; + ++ctr; + } + + bool skip = false; + if constexpr ( DO_AREA ) + { + if ( ctr < cut->size() ) + continue; + if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits::max() ) + skip = true; + } + + if ( !skip && compare_map( worst_arrival, node_data.arrival[phase], area_local, node_data.flows[phase], cut->size(), best_size ) ) + { + node_data.best_gate[phase] = &gate; + node_data.arrival[phase] = worst_arrival; + node_data.flows[phase] = area_local; + node_data.best_cut[phase] = cut_index; + node_data.area[phase] = gate.area; + node_data.phase[phase] = gate_polarity; + best_size = cut->size(); + } + + /* compute the alternative */ + if ( compare_map( worst_arrivalA, gA.arrival, area_localA, gA.flow, cut->size(), best_sizeA ) ) + { + gA.gate = &gate; + gA.arrival = worst_arrivalA; + gA.area = gate.area; + gA.flow = area_localA; + gA.phase = gate_polarity; + gA.cut = cut_index; + best_sizeA = cut->size(); + gA.size = cut->size(); + } + } + + ++cut_index; + } + } + + template + void match_phase_exact( node const& n, uint8_t phase ) + { + double best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint16_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + supergate const* best_gate = node_data.best_gate[phase]; + + /* unmap multioutput */ + if ( node_data.multioutput_match[phase] ) + { + /* dereference multi-output */ + if ( !node_data.same_match && best_gate != nullptr && node_data.map_refs[phase] ) + { + auto const& cut = multi_cut_set[node_data.best_cut[phase]][0]; + cut_deref( cut, n, phase ); + } + best_gate = nullptr; + node_data.multioutput_match[phase] = false; + } + + /* recompute best match info */ + if ( best_gate != nullptr ) + { + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + auto const& cut = cuts[index][node_data.best_cut[phase]]; + cut_deref( cut, n, phase ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts[index] ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = ( *cut )->supergates; + auto const negation = ( *cut )->negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint16_t gate_polarity = gate.polarity ^ negation; + double worst_arrival = 0.0f; + + auto ctr = 0u; + for ( auto l : *cut ) + { + double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits::max() ) + continue; + + node_data.phase[phase] = gate_polarity; + node_data.area[phase] = gate.area; + float area_exact = cut_measure_mffc( *cut, n, phase ); + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = gate.area; + best_size = cut->size(); + best_cut = cut_index; + best_phase = gate_polarity; + best_gate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_gate[phase] = best_gate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_ref( cuts[index][best_cut], n, phase ); + } + } + + template + void match_drop_phase( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* compute arrival adding an inverter to the other match phase */ + double worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + double worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + + /* only one phase is matched */ + if ( node_data.best_gate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + return; + } + else if ( node_data.best_gate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + return; + } + + /* try to use only one match to cover both phases */ + if constexpr ( !DO_AREA ) + { + /* if arrival improves matching the other phase and inserting an inverter */ + if ( worst_arrival_npos < node_data.arrival[0] + epsilon ) + { + use_one = true; + } + if ( worst_arrival_nneg < node_data.arrival[1] + epsilon ) + { + use_zero = true; + } + } + else + { + /* check if both phases + inverter meet the required time */ + use_zero = worst_arrival_nneg < ( node_data.required[1] + epsilon ); + use_one = worst_arrival_npos < ( node_data.required[0] + epsilon ); + } + + /* condition on not used phases, evaluate a substitution during exact area recovery */ + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] == 0 || node_data.map_refs[1] == 0 ) + { + /* select the used match */ + auto phase = 0; + auto nphase = 0; + if ( node_data.map_refs[0] == 0 ) + { + phase = 1; + use_one = true; + use_zero = false; + } + else + { + nphase = 1; + use_one = false; + use_zero = true; + } + /* select the not used match instead if it leads to area improvement and doesn't violate the required time */ + if ( node_data.arrival[nphase] + lib_inv_delay < node_data.required[phase] + epsilon ) + { + auto size_phase = cuts[index][node_data.best_cut[phase]].size(); + auto size_nphase = cuts[index][node_data.best_cut[nphase]].size(); + + if ( compare_map( node_data.arrival[nphase] + lib_inv_delay, node_data.arrival[phase], node_data.flows[nphase] + lib_inv_area, node_data.flows[phase], size_nphase, size_phase ) ) + { + /* invert the choice */ + use_zero = !use_zero; + use_one = !use_one; + } + } + } + } + + if ( ( !use_zero && !use_one ) ) + { + /* use both phases */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.same_match = false; + return; + } + + /* use area flow as a tiebreaker */ + if ( use_zero && use_one ) + { + auto size_zero = cuts[index][node_data.best_cut[0]].size(); + auto size_one = cuts[index][node_data.best_cut[1]].size(); + + if constexpr ( ELA ) + { + if ( !node_data.same_match ) + { + /* both phases were implemented --> evaluate substitution */ + cut_deref( cuts[index][node_data.best_cut[0]], n, 0 ); + node_data.flows[1] = cut_deref( cuts[index][node_data.best_cut[1]], n, 1 ); + node_data.flows[0] = cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + /* evaluate based on inverter cost */ + if constexpr ( !SwitchActivity ) + { + use_zero = lib_inv_area < node_data.flows[1] + epsilon; + use_one = lib_inv_area < node_data.flows[0] + epsilon; + } + + if ( use_one && use_zero ) + { + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + else if ( !use_one && !use_zero && node_data.same_match ) + { + node_data.same_match = false; + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + return; + } + } + else + { + /* compare flows by looking at the most convinient and referenced */ + if ( node_data.flows[0] / node_data.est_refs[0] + lib_inv_area < node_data.flows[1] / node_data.est_refs[1] + epsilon ) + { + use_one = false; + } + else if ( node_data.flows[1] / node_data.est_refs[1] + lib_inv_area < node_data.flows[0] / node_data.est_refs[0] + epsilon ) + { + use_zero = false; + } + else + { + /* delay the decision on what to keep --> wait for better estimations */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.same_match = false; + return; + } + } + } + + if ( use_zero ) + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the negative phase cut if in use */ + if ( node_data.map_refs[1] > 0 ) + cut_deref( cuts[index][node_data.best_cut[1]], n, 1 ); + /* reference the positive cut if not in use before */ + if ( node_data.map_refs[0] == 0 && node_data.map_refs[1] > 0 ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + else if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + } + else + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the positive phase cut if in use */ + if ( node_data.map_refs[0] > 0 ) + cut_deref( cuts[index][node_data.best_cut[0]], n, 0 ); + /* reference the negative cut if not in use before */ + if ( node_data.map_refs[1] == 0 && node_data.map_refs[0] > 0 ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + else if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + } + } + + inline void set_match_complemented_phase( uint32_t index, uint8_t phase, double worst_arrival_n ) + { + auto& node_data = node_match[index]; + auto phase_n = phase ^ 1; + node_data.same_match = true; + node_data.best_gate[phase_n] = nullptr; + node_data.best_cut[phase_n] = node_data.best_cut[phase]; + node_data.phase[phase_n] = node_data.phase[phase]; + node_data.arrival[phase_n] = worst_arrival_n; + node_data.area[phase_n] = node_data.area[phase]; + node_data.flows[phase_n] = ( node_data.flows[phase] + lib_inv_area ) / node_data.est_refs[phase_n]; + node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[phase]; + } + + template + inline void select_alternatives( node const& n ) + { + if constexpr ( DO_AREA ) + return; + + if ( !ps.use_match_alternatives ) + return; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + best_gate_emap& g0 = node_data.best_alternative[0]; + best_gate_emap& g1 = node_data.best_alternative[1]; + float g0flow = g0.flow / node_data.est_refs[0]; + float g1flow = g1.flow / node_data.est_refs[1]; + + /* process for best area */ /* removed check on required since this is executed only during a delay pass */ + if ( g0.gate != nullptr && g0flow + lib_inv_area < g1flow + epsilon ) + { + g1 = g0; + g1.gate = nullptr; + g1.arrival += lib_inv_delay; + g1.flow = ( g1.flow + lib_inv_area ) / node_data.est_refs[1]; + g0.flow = g0flow; + return; + } + else if ( g1.gate != nullptr && g1flow + lib_inv_area < g0flow + epsilon ) + { + g0 = g1; + g0.gate = nullptr; + g0.arrival += lib_inv_delay; + g0.flow = ( g0.flow + lib_inv_area ) / node_data.est_refs[0]; + g1.flow = g1flow; + return; + } + + g0.flow = g0flow; + g1.flow = g1flow; + } + + inline void refine_best_matches( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* evaluate to change the best matches with the best alternative */ + best_gate_emap& g0 = node_data.best_alternative[0]; + best_gate_emap& g1 = node_data.best_alternative[1]; + + if ( node_data.map_refs[0] && node_data.map_refs[1] ) + { + if ( node_data.same_match ) + { + /* pick best implementation between the two alternatives */ + unsigned use_phase = g0.gate == nullptr ? 1 : 0; + if ( g0.gate != nullptr && g1.gate != nullptr ) + { + if ( g0.arrival > node_data.required[0] + epsilon || g1.arrival > node_data.required[1] + epsilon ) + return; + + refine_best_matches_copy_refinement( n, 0, false ); + refine_best_matches_copy_refinement( n, 1, false ); + node_data.same_match = false; + return; + } + else + { + best_gate_emap& gUse = node_data.best_alternative[use_phase]; + if ( gUse.arrival > node_data.required[use_phase] + epsilon || gUse.arrival + lib_inv_delay > node_data.required[use_phase ^ 1] + epsilon ) + { + return; + } + refine_best_matches_copy_refinement( n, use_phase, true ); + return; + } + } + else + { + /* not same match: evaluate both zero and one phase */ + if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 0, g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon ); + } + if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 1, g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon ); + } + } + } + else if ( node_data.map_refs[0] ) + { + if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 0, false ); + } + else if ( g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon ) + { + refine_best_matches_copy_refinement( n, 1, true ); + } + } + else + { + if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 1, false ); + } + else if ( g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon ) + { + refine_best_matches_copy_refinement( n, 0, true ); + } + } + } + + inline void refine_best_matches_copy_refinement( node const& n, unsigned phase, bool both_phases ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + best_gate_emap& bg = node_data.best_alternative[phase]; + + node_data.best_gate[phase] = bg.gate; + node_data.phase[phase] = bg.phase; + node_data.best_cut[phase] = bg.cut; + node_data.arrival[phase] = bg.arrival; + node_data.area[phase] = bg.area; + node_data.flows[phase] = bg.flow; + + if ( !both_phases ) + return; + + node_data.same_match = true; + phase ^= 1; + node_data.best_gate[phase] = nullptr; + node_data.phase[phase] = bg.phase; + node_data.best_cut[phase] = bg.cut; + node_data.arrival[phase] = bg.arrival + lib_inv_delay; + node_data.area[phase] = bg.area; + node_data.flows[phase] = ( bg.flow * node_data.est_refs[phase ^ 1] + lib_inv_area ) / node_data.est_refs[phase]; + } + + bool initialize_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( cuts[index].size() == 0 ) + add_unit_cut( index ); + + auto& node_data = node_match[index]; + node_data.same_match = true; + + /* if it has mapping data propagate the delays and measure the data */ + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + return false; + } + + /* consider as a black box */ + node_data.flows[0] = 0.0f; + node_data.flows[1] = lib_inv_area / node_data.est_ref[1]; + node_data.arrival[0] = 0.0f; + node_data.arrival[1] = lib_inv_delay; + node_data.area[0] = node_data.area[1] = 0; + + return true; + } + + void propagate_data_forward_white_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto const& gate = ntk.get_binding( n ); + + /* propagate arrival time */ + double arrival = 0; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay ); + arrival = std::max( arrival, node_match[f_index].arrival[phase] + propagation_delay ); + } ); + + /* set data */ + node_data.arrival[0] = arrival; + node_data.arrival[1] = arrival + lib_inv_delay; + node_data.area[0] = node_data.area[1] = gate.area; + node_data.flows[1] = ( node_data.flows[0] + lib_inv_area ) / node_data.est_refs[1]; + node_data.flows[0] = node_data.area[0] / node_data.est_refs[0]; + } + + void propagate_data_backward_white_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto const& gate = ntk.get_binding( n ); + + assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.map_refs[1] > 0 ) + { + node_data.required[0] = std::min( node_data.required[0], node_data.required[1] - lib_inv_delay ); + } + + if ( node_data.map_refs[0] ) + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + if ( node_data.map_refs[1] ) + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay ); + node_match[f_index].required[phase] = std::min( node_match[f_index].required[phase], node_data.required[0] - propagation_delay ); + } ); + } + + void match_constants( uint32_t index ) + { + auto& node_data = node_match[index]; + + kitty::static_truth_table zero_tt; + auto const supergates_zero = library.get_supergates( zero_tt ); + auto const supergates_one = library.get_supergates( ~zero_tt ); + + /* Not available in the library */ + if ( supergates_zero == nullptr && supergates_one == nullptr ) + { + return; + } + /* if only one is available, the other is obtained using an inverter */ + if ( supergates_zero != nullptr ) + { + node_data.best_gate[0] = &( ( *supergates_zero )[0] ); + node_data.arrival[0] = node_data.best_gate[0]->tdelay[0]; + node_data.area[0] = node_data.best_gate[0]->area; + node_data.phase[0] = 0; + } + if ( supergates_one != nullptr ) + { + node_data.best_gate[1] = &( ( *supergates_one )[0] ); + node_data.arrival[1] = node_data.best_gate[1]->tdelay[0]; + node_data.area[1] = node_data.best_gate[1]->area; + node_data.phase[1] = 0; + } + else + { + node_data.same_match = true; + node_data.arrival[1] = node_data.arrival[0] + lib_inv_delay; + node_data.area[1] = node_data.area[0] + lib_inv_area; + node_data.phase[1] = 1; + } + if ( supergates_zero == nullptr ) + { + node_data.same_match = true; + node_data.arrival[0] = node_data.arrival[1] + lib_inv_delay; + node_data.area[0] = node_data.area[1] + lib_inv_area; + node_data.phase[0] = 1; + } + } + + template + bool match_multioutput( node const& n ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + /* get the cut */ + auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index]; + + /* local values storage */ + std::array arrival; + std::array area_flow; + std::array area; + std::array phase; + std::array pin_phase; + std::array est_refs; + std::array cut_index; + bool mapped_multioutput = false; + + uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0; + + /* iterate for each possible match */ + for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i ) + { + /* store local validity and comparison info */ + bool valid = true; + bool is_best = true; + bool respects_required = true; + double old_flow_sum = 0; + + /* iterate for each output of the multi-output gate */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + cut_index[j] = tuple_data[j].cut_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + /* protection on complicated duplicated nodes to remap to multioutput */ + if ( !node_data.same_match ) + return false; + + /* get the output phase */ + pin_phase[j] = gate.polarity; + phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted; + + /* compute arrival */ + arrival[j] = 0.0; + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr]; + arrival[j] = std::max( arrival[j], arrival_pin ); + ++ctr; + } + + /* check required time: same_match is true */ + if constexpr ( DO_AREA ) + { + if ( arrival[j] > node_data.required[phase[j]] + epsilon ) + { + valid = false; + break; + } + if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon ) + { + valid = false; + break; + } + } + + /* check required time of the current solution */ + if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] ) + respects_required = false; + if ( node_data.same_match && node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] ) + respects_required = false; + + /* compute area flow */ + if ( j == 0 || !node_data.multioutput_match[0] ) + { + uint8_t current_phase = node_data.best_gate[0] == nullptr ? 1 : 0; + old_flow_sum += node_data.flows[current_phase]; + } + uint8_t old_phase = node_data.phase[phase[j]]; + node_data.phase[phase[j]] = gate.polarity; + area[j] = gate.area; + area_flow[j] = gate.area + cut_leaves_flow( cut, n, phase[j] ); + node_data.phase[phase[j]] = old_phase; + + /* current version may lead to delay increase */ + est_refs[j] = node_data.est_refs[phase[j]]; + } + + /* not better than individual gates */ + if ( !valid ) + continue; + + if constexpr ( !DO_AREA ) + { + if ( !is_best ) + continue; + } + + /* combine evaluation for precise area flow estimantion */ + /* compute equation AF(n) = ( Area(G) + |roots| * SUM_{l in leaves} AF(l) ) / SUM_{p in roots} est_refs( p ) */ + float flow_sum_pos = 0, flow_sum_neg; + float combined_est_refs = 0; + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + flow_sum_pos += area_flow[j]; + combined_est_refs += est_refs[j]; + } + flow_sum_neg = flow_sum_pos; + flow_sum_pos /= combined_est_refs; + + /* not better than individual gates */ + if ( respects_required && ( flow_sum_pos > old_flow_sum + epsilon ) ) + continue; + + mapped_multioutput = true; + flow_sum_neg = ( flow_sum_neg + lib_inv_area ) / combined_est_refs; + + /* commit multi-output gate */ + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + uint8_t mapped_phase = phase[j]; + node_data.multioutput_match[mapped_phase] = true; + + node_data.best_gate[mapped_phase] = &gate; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j]; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = flow_sum_pos; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + + /* select opposite phase */ + mapped_phase ^= 1; + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = nullptr; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = flow_sum_neg; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + } + } + + return mapped_multioutput; + } + + template + bool match_multioutput_exact( node const& n, bool last_round ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + /* local values storage */ + std::array best_exact_area; + + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + /* protection on complicated duplicated nodes to remap to multioutput */ + if ( !node_match[tuple_data[j].node_index].same_match ) + return false; + } + + /* if one of the outputs is not referenced, do not use multi-output gate */ + if ( last_round ) + { + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + if ( !node_match[node_index].map_refs[0] && !node_match[node_index].map_refs[1] ) + { + return false; + } + } + } + + /* if "same match" and used in the cover dereference the leaves (reverse topo order) */ + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + uint32_t node_index = tuple_data[j].node_index; + uint8_t selected_phase = node_match[node_index].best_gate[0] == nullptr ? 1 : 0; + + if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] ) + { + /* match is always single output here */ + auto const& cut = cuts[node_index][node_match[node_index].best_cut[0]]; + uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + best_exact_area[j] = cut_deref( cut, ntk.index_to_node( node_index ), use_phase ); + + /* mapping a non referenced phase */ + if ( node_match[node_index].map_refs[selected_phase] == 0 ) + best_exact_area[j] += lib_inv_area; + } + } + + /* perform mapping */ + bool mapped_multioutput = false; + mapped_multioutput = match_multioutput_exact_core( tuple_data, best_exact_area ); + + /* if "same match" and used in the cover reference the leaves (topo order) */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + + if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] ) + { + uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + auto const& best_cut = cuts[node_index][node_match[node_index].best_cut[use_phase]]; + cut_ref( best_cut, ntk.index_to_node( node_index ), use_phase ); + } + } + + return mapped_multioutput; + } + + template + inline bool match_multioutput_exact_core( multi_match_t const& tuple_data, std::array& best_exact_area ) + { + /* get the cut representative */ + auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index]; + + /* local values storage */ + std::array arrival; + std::array area_exact; + std::array area; + std::array phase; + std::array pin_phase; + std::array cut_index; + + uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0; + + bool mapped_multioutput = false; + + /* iterate for each possible match */ + for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i ) + { + /* store local validity and comparison info */ + bool valid = true; + bool respects_required = true; + uint32_t it_counter = 0; + + /* iterate for each output of the multi-output gate (reverse topo order) */ + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + uint32_t node_index = tuple_data[j].node_index; + cut_index[j] = tuple_data[j].cut_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + ++it_counter; + + /* get the output phase and area */ + pin_phase[j] = gate.polarity; + phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted; + area[j] = gate.area; + + /* compute arrival */ + arrival[j] = 0.0; + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr]; + arrival[j] = std::max( arrival[j], arrival_pin ); + ++ctr; + } + + /* check required time */ + if ( arrival[j] > node_data.required[phase[j]] + epsilon ) + { + valid = false; + break; + } + if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon ) + { + valid = false; + break; + } + + /* check required time of current solution */ + if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] ) + respects_required = false; + if ( node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] ) + respects_required = false; + + /* compute exact area for match: needed only for the first node (leaves are shared) */ + if ( it_counter == 1 ) + { + auto old_phase = node_data.phase[phase[j]]; + auto old_area = node_data.area[phase[j]]; + node_data.phase[phase[j]] = pin_phase[j]; + node_data.area[phase[j]] = area[j]; + area_exact[j] = cut_measure_mffc( cut, ntk.index_to_node( node_index ), phase[j] ); + node_data.phase[phase[j]] = old_phase; + node_data.area[phase[j]] = old_area; + } + else + { + area_exact[j] = area[j]; + } + + /* Add output inverter cost if mapping a non referenced phase */ + if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 ) + { + area_exact[j] += lib_inv_area; + } + } + + /* check quality: TODO add output inverter in the cost if necessary */ + float best_exact_area_total = 0; + float area_exact_total = 0; + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + best_exact_area_total += best_exact_area[j]; + area_exact_total += area_exact[j]; + } + + /* not better than individual gates */ + if ( !valid || ( area_exact_total > best_exact_area_total - epsilon && respects_required ) ) + { + continue; + } + + mapped_multioutput = true; + + /* commit multi-output gate (topo order) */ + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + uint8_t mapped_phase = phase[j]; + best_exact_area[j] = area_exact[j]; + + if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 ) + { + best_exact_area[j] += lib_inv_area; + } + + /* write data */ + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = &gate; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j]; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + + node_data.flows[mapped_phase] = area_exact[j]; /* partial exact area contribution */ + /* select opposite phase */ + mapped_phase ^= 1; + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = nullptr; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = area_exact[j]; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + } + } + + return mapped_multioutput; + } + + template + void multi_node_update( node const& n ) + { + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0]; + uint64_t signature = 0; + + /* check if a node is in TFI: there is a path of length > 1 */ + bool in_tfi = false; + node min_node = n; + for ( auto j = 0; j < max_multioutput_output_size - 1; ++j ) + { + if ( tuple_data[j].in_tfi ) + { + min_node = ntk.index_to_node( tuple_data[j].node_index ); + in_tfi = true; + signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f ); + } + } + + if ( !in_tfi ) + return; + + /* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */ + ntk.incr_trav_id(); + ntk.foreach_fanin( n, [&]( auto const& f ) { + /* TODO: this recursion works as it is for a maximum multioutput value of 2 */ + multi_node_update_rec( ntk.get_node( f ), min_node + 1, signature ); + } ); + } + + template + void multi_node_update_rec( node const& n, uint32_t min_index, uint64_t& signature ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( index < min_index ) + return; + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_node_update_rec( ntk.get_node( f ), min_index, signature ); + } ); + + /* update the node if uses an updated leaf */ + auto& node_data = node_match[index]; + bool leaf_used = multi_node_update_cut_check( index, signature, 0 ); + + if ( !node_data.same_match ) + leaf_used |= multi_node_update_cut_check( index, signature, 1 ); + + if ( !leaf_used ) + return; + + signature |= UINT64_C( 1 ) << ( index & 0x3f ); + + /* avoid cycles by recomputing arrival times for multi-output gates or decomposing them */ + if ( node_data.same_match && node_data.multioutput_match[0] ) + { + propagate_arrival_node( n ); + /* check required time */ + if ( node_data.arrival[0] < node_data.required[0] + epsilon && node_data.arrival[1] < node_data.required[1] + epsilon ) + return; + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + } + + template + void multi_node_update_exact( node const& n ) + { + uint32_t check_index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0]; + uint64_t signature = 0; + + /* check if a node is in TFI: there is a path of length > 1 */ + bool in_tfi = false; + node min_node = n; + for ( auto j = 0; j < max_multioutput_output_size - 1; ++j ) + { + if ( tuple_data[j].in_tfi ) + { + min_node = ntk.index_to_node( tuple_data[j].node_index ); + in_tfi = true; + signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f ); + } + } + + if ( !in_tfi ) + return; + + /* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */ + ntk.incr_trav_id(); + ntk.foreach_fanin( n, [&]( auto const& f ) { + /* TODO: this recursion works as it is for a maximum multioutput value of 2 */ + multi_node_update_exact_rec( ntk.get_node( f ), min_node + 1, signature ); + } ); + } + + template + void multi_node_update_exact_rec( node const& n, uint32_t min_index, uint64_t& signature ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( index < min_index ) + return; + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_node_update_exact_rec( ntk.get_node( f ), min_index, signature ); + } ); + + /* update the node if uses an updated leaf */ + auto& node_data = node_match[index]; + bool leaf_used = multi_node_update_cut_check( index, signature, 0 ); + + if ( !node_data.same_match ) + leaf_used |= multi_node_update_cut_check( index, signature, 1 ); + + if ( !leaf_used ) + return; + + signature |= UINT64_C( 1 ) << ( index & 0x3f ); + + assert( !node_data.multioutput_match[0] ); + assert( !node_data.multioutput_match[1] ); + + if ( node_data.same_match && ( node_data.map_refs[0] || node_data.map_refs[1] ) ) + { + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, n, use_phase ); + } + + /* match positive phase */ + match_phase_exact( n, 0u ); + + /* match negative phase */ + match_phase_exact( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + assert( node_data.arrival[0] < std::numeric_limits::max() ); + assert( node_data.arrival[1] < std::numeric_limits::max() ); + } + + inline void match_multioutput_propagate_required( node const& n ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + const auto node_index = tuple_data[j].node_index; + match_propagate_required( node_index ); + } + } + + void match_multi_add_cuts( node const& n ) + { + /* assume a single cut (current version) */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t& matches = multi_node_match[node_tuple_match[index].index][0]; + + /* find the corresponding cut */ + uint32_t cut_p = 0; + while ( matches[cut_p].node_index != index ) + ++cut_p; + + assert( cut_p < matches.size() ); + uint32_t cut_index = matches[cut_p].cut_index; + auto& cut = multi_cut_set[cut_index][cut_p]; + auto single_cut = multi_cut_set[cut_index][cut_p]; + auto& rcuts = cuts[index]; + + /* not enough space in the data structure: abort */ + if ( rcuts.size() == max_cut_num ) + { + match_multi_add_cuts_remove_entry( matches ); + return; + } + + /* insert single cut variation if unique (for delay preservation) */ + if ( !rcuts.is_contained( single_cut ) ) + { + single_cut->pattern_index = 0; + compute_cut_data( single_cut, ntk.index_to_node( index ) ); + rcuts.append_cut( single_cut ); + + /* not enough space in the data structure: abort */ + if ( rcuts.size() == max_cut_num ) + { + rcuts.limit( rcuts.size() - 1 ); + match_multi_add_cuts_remove_entry( matches ); + return; + } + } + + /* add multi-output cut */ + uint32_t num_cuts_pre = rcuts.size(); + cut->ignore = true; + rcuts.append_cut( cut ); + + assert( rcuts.size() == num_cuts_pre + 1 ); + + rcuts.limit( num_cuts_pre ); + + /* update tuple data */ + matches[cut_p].cut_index = num_cuts_pre; + } + + inline void match_multi_add_cuts_remove_entry( multi_match_t const& matches ) + { + /* reset matches */ + for ( multi_match_data const& entry : matches ) + { + node_tuple_match[entry.node_index].data = 0; + } + } + + inline bool multi_node_update_cut_check( uint32_t index, uint64_t signature, uint8_t phase ) + { + auto const& cut = cuts[index][node_match[index].best_cut[phase]]; + + if ( ( signature & cut.signature() ) > 0 ) + return true; + + return false; + } + + inline double cut_leaves_flow( cut_t const& cut, node const& n, uint8_t phase ) + { + double flow{ 0.0f }; + auto const& node_data = node_match[ntk.node_to_index( n )]; + + uint8_t ctr = 0u; + for ( auto leaf : cut ) + { + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + flow += node_match[leaf].flows[leaf_phase]; + } + + return flow; + } + + template + float cut_ref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Recursive referencing if leaf was not referenced */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_deref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* dereference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( --node_match[leaf].map_refs[1] == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + --node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */ + if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + /* Recursive dereferencing */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_deref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + if ( --node_match[leaf].map_refs[leaf_phase] == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_deref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_measure_mffc( cut_t const& cut, node const& n, uint8_t phase ) + { + tmp_visited.clear(); + + float count = cut_ref_visit( cut, n, phase ); + + /* dereference visited */ + for ( auto s : tmp_visited ) + { + uint32_t leaf = s >> 1; + --node_match[leaf].map_refs[s & 1]; + } + + return count; + } + + template + float cut_ref_visit( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* add to visited */ + tmp_visited.push_back( ( static_cast( leaf ) << 1 ) | leaf_phase ); + + if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Recursive referencing if leaf was not referenced */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref_visit( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref_visit( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + void insert_buffers() + { + if ( lib_buf_id != UINT32_MAX ) + { + double area_old = area; + bool buffers = false; + + ntk.foreach_po( [&]( auto const& f ) { + auto const& n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) && ntk.is_pi( n ) && !ntk.is_complemented( f ) ) + { + area += lib_buf_area; + delay = std::max( delay, node_match[ntk.node_to_index( n )].arrival[0] + lib_inv_delay ); + buffers = true; + } + } ); + + /* round stats */ + if ( ps.verbose && buffers ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + area_gain = float( ( area_old - area ) / area_old * 100 ); + + stats << fmt::format( "[i] Buffering: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + st.round_stats.push_back( stats.str() ); + } + } + } + + std::pair, klut_map> initialize_map_network() + { + binding_view dest( library.get_gates() ); + klut_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + std::pair, block_map> initialize_block_network() + { + cell_view dest( library.get_cells() ); + block_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + void init_topo_order() + { + topo_order.reserve( ntk.size() ); + + if ( multi_node_match.size() > 0 ) + { + multi_init_topo_order(); + return; + } + + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + } + + bool init_arrivals() + { + if ( ps.required_times.size() && ps.required_times.size() != ntk.num_pos() ) + { + std::cerr << "[e] MAP ERROR: required time vector does not match the output size of the network" << std::endl; + st.mapping_error = true; + return false; + } + + if ( ps.arrival_times.empty() ) + { + ntk.foreach_pi( [&]( auto const& n ) { + auto& node_data = node_match[ntk.node_to_index( n )]; + node_data.arrival[0] = node_data.best_alternative[0].arrival = 0; + node_data.arrival[1] = node_data.best_alternative[1].arrival = lib_inv_delay; + } ); + return true; + } + + if ( ps.arrival_times.size() != ntk.num_pis() ) + { + std::cerr << "[e] MAP ERROR: arrival time vector does not match the input size of the network" << std::endl; + st.mapping_error = true; + return false; + } + + ntk.foreach_pi( [&]( auto const& n, uint32_t i ) { + auto& node_data = node_match[ntk.node_to_index( n )]; + node_data.arrival[0] = node_data.best_alternative[0].arrival = ps.arrival_times[i]; + node_data.arrival[1] = node_data.best_alternative[1].arrival = ps.arrival_times[i] + lib_inv_delay; + } ); + + return true; + } + + void finalize_cover( binding_view& res, klut_map& old2new ) + { + uint32_t multioutput_count = 0; + + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[index][0] ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + clone_box( res, old2new, index ); + continue; + } + } + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_binding( res.get_node( old2new[index][phase ^ 1] ), lib_inv_id ); + } + + /* count multioutput gates */ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] ) + { + ++multioutput_count; + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* count multioutput gates */ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] ) + { + ++multioutput_count; + } + } + + st.multioutput_gates = multioutput_count; + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX && ps.create_po_buffers) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_binding( res.get_node( buf ), lib_buf_id ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + /* write final results */ + st.area = area; + st.delay = delay; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + void finalize_cover_block( cell_view& res, block_map& old2new ) + { + uint32_t multioutput_count = 0; + + /* get standard cells */ + std::vector const& lib = res.get_library(); + + /* get translation ID from GENLIB to STD_CELL */ + std::vector genlib_to_cell( library.get_gates().size() ); + for ( standard_cell const& cell : lib ) + { + for ( gate const& g : cell.gates ) + { + genlib_to_cell[g.id] = cell.id; + } + } + + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[index][0] ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell[lib_inv_id] ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + clone_box2( res, old2new, index, genlib_to_cell ); + continue; + } + } + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + /* create multioutput gates */ + if ( ps.map_multioutput && node_data.multioutput_match[phase] ) + { + assert( node_data.same_match == true ); + + if ( node_tuple_match[index].has_info && node_tuple_match[index].lowest_index ) + { + ++multioutput_count; + create_block_for_gate( res, old2new, index, phase, genlib_to_cell ); + } + continue; + } + + create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_cell( res.get_node( old2new[index][phase ^ 1] ), genlib_to_cell[lib_inv_id] ); + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + assert( !ps.map_multioutput || !node_data.multioutput_match[phase] ); + create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell ); + } + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX && ps.create_po_buffers) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_cell( res.get_node( buf ), genlib_to_cell[lib_buf_id] ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + /* write final results */ + st.area = area; + st.delay = delay; + st.multioutput_gates = multioutput_count; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + void create_lut_for_gate( binding_view& res, klut_map& old2new, uint32_t index, unsigned phase ) + { + auto const& node_data = node_match[index]; + auto const& best_cut = cuts[index][node_data.best_cut[phase]]; + auto const& gate = node_data.best_gate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_binding( res.get_node( f ), gate->root->id ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate_rec( res, *gate, children ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + signal create_lut_for_gate_rec( binding_view& res, composed_gate const& gate, std::vector> const& children ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate_rec( res, *fanin, children ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_binding( res.get_node( f ), gate.root->id ); + return f; + } + + void create_lut_for_gate2( cell_view& res, block_map& old2new, uint32_t index, unsigned phase, std::vector const& genlib_to_cell ) + { + auto const& node_data = node_match[index]; + auto const& best_cut = cuts[index][node_data.best_cut[phase]]; + auto const& gate = node_data.best_gate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( gate->root->id ) ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate2_rec( res, *gate, children, genlib_to_cell ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + signal create_lut_for_gate2_rec( cell_view& res, composed_gate const& gate, std::vector> const& children, std::vector const& genlib_to_cell ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate2_rec( res, *fanin, children, genlib_to_cell ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( gate.root->id ) ); + return f; + } + + void create_block_for_gate( cell_view& res, block_map& old2new, uint32_t index, unsigned phase, std::vector const& genlib_to_cell ) + { + std::vector const& lib = res.get_library(); + composed_gate const* local_gate = node_match[index].best_gate[phase]->root; + standard_cell const& cell = lib[genlib_to_cell.at( local_gate->root->id )]; + + assert( !local_gate->is_super ); + auto const& best_cut = cuts[index][node_match[index].best_cut[phase]]; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( cell.gates.front().num_vars ); + + /* output negations have already been assigned by the mapper */ + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= local_gate->num_vars ) + break; + children[node_match[index].best_gate[phase]->permutation[ctr]] = old2new[l][( node_match[index].phase[phase] >> ctr ) & 1]; + ++ctr; + } + + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + std::vector outputs; + std::vector functions; + + /* re-order outputs to match the ones of the cell */ + for ( gate const& g : cell.gates ) + { + /* find the correct node */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + assert( node_match[node_index].same_match ); + uint8_t node_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + assert( node_match[node_index].multioutput_match[node_phase] ); + + gate const* node_gate = node_match[node_index].best_gate[node_phase]->root->root; + + /* wrong output */ + if ( node_gate->id != g.id ) + continue; + + outputs.push_back( node_index ); + functions.push_back( g.function ); + } + } + + assert( outputs.size() == cell.gates.size() ); + + /* create the block */ + auto f = res.create_node( children, functions ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( local_gate->root->id ) ); + + for ( uint32_t s : outputs ) + { + /* add inverted version if used */ + uint8_t node_phase = node_match[s].best_gate[0] != nullptr ? 0 : 1; + assert( node_match[s].same_match ); + + /* add the node in the data structure */ + old2new[s][node_phase] = f; + + if ( node_match[s].map_refs[node_phase ^ 1] > 0 ) + { + old2new[s][node_phase ^ 1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[s][node_phase ^ 1] ), genlib_to_cell.at( lib_inv_id ) ); + } + + f = res.next_output_pin( f ); + } + } + + void clone_box( binding_view& res, klut_map& old2new, uint32_t index ) + { + node n = ntk.index_to_node( index ); + std::vector> children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] ); + } ); + + /* create the node */ + auto const& tt = ntk.node_function( n ); + auto f = res.create_node( children, tt ); + + /* add the node in the data structure */ + old2new[index][0] = f; + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + + if constexpr ( has_has_binding_v ) + { + if ( ntk.has_binding( n ) ) + res.add_binding( res.get_node( f ), ntk.get_binding_index( n ) ); + } + } + + void clone_box2( cell_view& res, klut_map& old2new, uint32_t index, std::vector const& genlib_to_cell ) + { + node n = ntk.index_to_node( index ); + std::vector> children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] ); + } ); + + /* check if multi-output */ + std::vector const& lib = res.get_library(); + if constexpr ( has_has_binding_v ) + { + bool is_multioutput = false; + if ( ntk.has_binding( n ) ) + { + uint32_t cell_id = genlib_to_cell.at( ntk.get_binding_index( n ) ); + if ( lib.at( cell_id ).gates.size() > 1 ) + is_multioutput = true; + } + + /* create the multioutput node (partially dangling) */ + if ( is_multioutput ) + { + standard_cell const& cell = lib.at( genlib_to_cell.at( ntk.get_binding_index( n ) ) ); + std::vector functions; + for ( auto const& g : cell.gates ) + { + functions.push_back( g.function ); + } + + auto f = res.create_node( children, functions ); + + /* find and connect the correct pin */ + for ( auto const& g : cell.gates ) + { + if ( g.id == cell.id ) + break; + res.next_output_pin( f ); + } + + old2new[index][0] = f; + res.add_cell( res.get_node( f ), cell.id ); + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) ); + } + return; + } + } + + /* create the single-output node */ + auto const& tt = ntk.node_function( n ); + auto f = res.create_node( children, tt ); + + /* add the node in the data structure */ + old2new[index][0] = f; + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) ); + } + + if constexpr ( has_has_binding_v ) + { + if ( ntk.has_binding( n ) ) + res.add_cell( res.get_node( f ), genlib_to_cell.at( ntk.get_binding_index( n ) ) ); + } + } + + void compute_cut_data( cut_t& cut, node const& n ) + { + cut->delay = std::numeric_limits::max(); + cut->flow = std::numeric_limits::max(); + cut->ignore = false; + + if ( cut.size() > NInputs || cut.size() > 6 ) + { + /* Ignore cuts too big to be mapped using the library */ + cut->ignore = true; + return; + } + + const auto tt = cut->function; + const kitty::static_truth_table fe = kitty::extend_to( tt ); + auto fe_canon = fe; + + uint16_t negations_pos = 0; + uint16_t negations_neg = 0; + + /* match positive polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( fe, cut.size() ); + fe_canon = std::get<0>( canon ); + negations_pos = std::get<1>( canon ); + } + + auto const supergates_pos = library.get_supergates( fe_canon ); + + /* match negative polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( ~fe, cut.size() ); + fe_canon = std::get<0>( canon ); + negations_neg = std::get<1>( canon ); + } + else + { + fe_canon = ~fe; + } + + auto const supergates_neg = library.get_supergates( fe_canon ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut->supergates = { supergates_pos, supergates_neg }; + cut->negations = { negations_pos, negations_neg }; + } + else + { + /* Ignore not matched cuts */ + cut->ignore = true; + return; + } + + /* compute cut cost based on LUT area */ + recompute_cut_data( cut, n ); + } + + void compute_cut_data_structural( cut_t& cut, node const& n ) + { + cut->delay = std::numeric_limits::max(); + cut->flow = std::numeric_limits::max(); + cut->ignore = false; + + assert( cut.size() <= NInputs ); + + const auto supergates_pos = library.get_supergates_pattern( cut->pattern_index, false ); + const auto supergates_neg = library.get_supergates_pattern( cut->pattern_index, true ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut->supergates = { supergates_pos, supergates_neg }; + } + else + { + /* Ignore not matched cuts */ + cut->ignore = true; + return; + } + + /* compute cut cost based on LUT area */ + recompute_cut_data( cut, n ); + } + + void recompute_cut_data( cut_t& cut, node const& n ) + { + /* compute cut cost based on LUT area */ + uint32_t best_arrival = 0; + float best_area_flow = cut.size() > 1 ? cut.size() : 0; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + best_arrival = std::max( best_arrival, best_leaf_cut->delay ); + best_area_flow += best_leaf_cut->flow; + } + + cut->delay = best_arrival + ( cut.size() > 1 ) ? 1 : 0; + cut->flow = best_area_flow / ntk.fanout_size( n ); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + void compute_truth_table_support( cut_t const& sub, cut_t const& sup, TT& tt ) + { + size_t j = 0; + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + lsupport[j++] = static_cast( std::distance( sup.begin(), itp ) ); + } + + /* swap variables in the truth table */ + for ( int i = j - 1; i >= 0; --i ) + { + assert( i <= lsupport[i] ); + kitty::swap_inplace( tt, i, lsupport[i] ); + } + } + + void add_zero_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + cut->ignore = true; + cut->delay = 0; + cut->flow = 0; + cut->pattern_index = 0; + cut->negations[0] = cut->negations[1] = 0; + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index + 1 ); + + kitty::create_nth_var( cut->function, 0 ); + cut->ignore = true; + cut->delay = 0; + cut->flow = 0; + cut->pattern_index = 1; + cut->negations[0] = cut->negations[1] = 0; + } + + inline void create_structural_cut( cut_t& new_cut, std::vector const& vcuts, uint32_t new_pattern, uint32_t pattern_id1, uint32_t pattern_id2 ) + { + new_cut.set_leaves( *vcuts[0] ); + new_cut.add_leaves( vcuts[1]->begin(), vcuts[1]->end() ); + new_cut->pattern_index = new_pattern; + + /* get the polarity of the leaves of the new cut */ + uint16_t neg_l = 0, neg_r = 0; + if ( ( *vcuts[0] )->pattern_index == 1 ) + { + neg_r = static_cast( pattern_id1 & 1 ); + } + else + { + neg_r = ( *vcuts[0] )->negations[0]; + } + if ( ( *vcuts[1] )->pattern_index == 1 ) + { + neg_l = static_cast( pattern_id2 & 1 ); + } + else + { + neg_l = ( *vcuts[1] )->negations[0]; + } + + new_cut->negations[0] = ( neg_l << vcuts[0]->size() ) | neg_r; + new_cut->negations[1] = new_cut->negations[0]; + } + + inline bool fast_support_minimization( TT const& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + } + + return true; + } + + void compute_truth_table( uint32_t index, fanin_cut_t const& vcuts, uint32_t fanin, cut_t& res ) + { + for ( uint32_t i = 0; i < fanin; ++i ) + { + cut_t const* cut = vcuts[i]; + ltruth[i] = ( *cut )->function; + compute_truth_table_support( *cut, res, ltruth[i] ); + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), ltruth.begin(), ltruth.begin() + fanin ); + + if ( ps.cut_enumeration_ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + + res->function = tt_res; + } + + template + inline bool compare_map( double arrival, double best_arrival, float area_flow, float best_area_flow, uint32_t size, uint32_t best_size ) + { + if constexpr ( DO_AREA ) + { + if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + else if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + return size < best_size; + } + else + { + if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + else if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + return size < best_size; + } + } + + double compute_switching_power() + { + double power = 0.0f; + + for ( auto const& n : topo_order ) + { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + } + + phase = phase ^ 1; + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + } + } + + return power; + } + + /* Experimental code */ + void compute_multioutput_match() + { + stopwatch t( st.time_multioutput ); + + if ( library.num_multioutput_gates() == 0 ) + return; + + /* compute cuts: first simple method without proper matching */ + cut_enumeration_params multi_ps; + multi_ps.minimize_truth_table = false; + multi_cuts_t multi_cuts = fast_cut_enumeration( ntk, multi_ps ); + + /* cuts leaves classes */ + multi_hash_t multi_cuts_classes; + multi_cuts_classes.reserve( 2000 ); + + /* Multi-output matching */ + multi_enumerate_matches( multi_cuts, multi_cuts_classes ); + + multi_single_matches_t multi_node_match_local; + multi_node_match_local.reserve( multi_cuts_classes.size() ); + + multi_compute_matches( multi_cuts, multi_cuts_classes, multi_node_match_local ); + + if ( ps.remove_overlapping_multicuts ) + multi_filter_and_match( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */ + else + multi_filter_and_match( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */ + } + + void multi_init_topo_order() + { + bool topo_ok = true; + /* create and initialize a choice view to store the tuples */ + choice_view choice_ntk{ ntk }; + multi_add_choices( choice_ntk ); + + ntk.incr_trav_id(); + ntk.incr_trav_id(); + + /* add constants and CIs */ + const auto c0 = ntk.get_node( ntk.get_constant( false ) ); + topo_order.push_back( c0 ); + ntk.set_visited( c0, ntk.trav_id() ); + + if ( const auto c1 = ntk.get_node( ntk.get_constant( true ) ); ntk.visited( c1 ) != ntk.trav_id() ) + { + topo_order.push_back( c1 ); + ntk.set_visited( c1, ntk.trav_id() ); + } + + ntk.foreach_ci( [&]( auto const& n ) { + if ( ntk.visited( n ) != ntk.trav_id() ) + { + topo_order.push_back( n ); + ntk.set_visited( n, ntk.trav_id() ); + } + } ); + + /* sort topologically */ + ntk.foreach_co( [&]( auto const& f ) { + if (!topo_ok) + return; + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + return; + topo_ok = multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + } ); + + if (!topo_ok) { + std::cerr << "[i] WARNING: failed to compute topological order for multi-output matching, falling back to non-topological order\n"; + topo_order.clear(); + multi_node_match.clear(); + std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() ); + for ( auto& data : node_match ) + { + data.multioutput_match[0] = false; + data.multioutput_match[1] = false; + } + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + } + } + + /* Experimental code resticted to only half adders and full adders */ + void multi_enumerate_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes ) + { + static_assert( max_multioutput_cut_size > 1 && max_multioutput_cut_size < 7 ); + + multi_leaves_set_t leaves = { 0 }; + + ntk.foreach_gate( [&]( auto const& n ) { + uint32_t cut_index = 0; + for ( auto& cut : multi_cuts.cuts( ntk.node_to_index( n ) ) ) + { + kitty::static_truth_table tt = multi_cuts.truth_table( *cut ); + /* reduce support for matching ID */ + uint64_t tt_id = ( cut->size() < 3 ) ? ( tt._bits & 0xF ) : tt._bits; + uint64_t id = library.get_multi_function_id( tt_id ); + + if ( !id ) + { + ++cut_index; + continue; + } + + ( *cut )->data.id = id; + + multi_match_data data; + data.node_index = ntk.node_to_index( n ); + data.cut_index = cut_index; + leaves[2] = 0; + uint32_t i = 0; + for ( auto l : *cut ) + leaves[i++] = l; + + /* add to hash table */ + multi_cuts_classes[leaves].push_back( data ); + + ++cut_index; + } + } ); + } + + /* Experimental code */ + void multi_compute_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes, multi_single_matches_t& multi_node_match_local ) + { + ntk.clear_values(); + + /* copy set and sort by gate size: improve, too slow */ + std::vector> class_list; + class_list.reserve( multi_cuts_classes.size() ); + for ( auto& it : multi_cuts_classes ) + { + /* insert multiple occurring cuts */ + if ( it.second.size() > 1 ) + class_list.push_back( it ); + } + + std::stable_sort( class_list.begin(), class_list.end(), [&]( auto const& a, auto const& b ) { + if (a.first == b.first) { + for (std::size_t i = 0; i < std::min(a.second.size(), b.second.size()); ++i) { + if (a.second[i].data > b.second[i].data) return true; + } + return a.second.size() > b.second.size(); + } + return a.first > b.first; + } ); + + /* combine and match: specific code for 2-output cells */ + for ( auto it : class_list ) + { + for ( uint32_t i = 0; i < it.second.size() - 1; ++i ) + { + multi_match_data data_i = it.second[i]; + uint32_t index_i = data_i.node_index; + uint32_t cut_index_i = data_i.cut_index; + auto const& cut_i = multi_cuts.cuts( index_i )[cut_index_i]; + + for ( uint32_t j = i + 1; j < it.second.size(); ++j ) + { + multi_match_data data_j = it.second[j]; + uint32_t index_j = data_j.node_index; + uint32_t cut_index_j = data_j.cut_index; + auto const& cut_j = multi_cuts.cuts( index_j )[cut_index_j]; + + /* not compatible -> TODO: change */ + if ( cut_i->data.id == cut_j->data.id ) + continue; + + /* check compatibility */ + if ( !multi_check_partially_dangling( index_i, index_j, cut_i ) ) + continue; + + multi_node_match_local.push_back( { data_i, data_j } ); + } + } + } + } + + bool node_in_tfi( uint32_t dst_idx, uint32_t src_idx ) + { + if ( dst_idx == src_idx ) + return true; + + ntk.incr_trav_id(); + const auto mark = ntk.trav_id(); + + bool found = false; + + std::function const&)> rec = [&]( node const& n ) { + if ( found ) + return; + + if ( ntk.visited( n ) == mark ) + return; + ntk.set_visited( n, mark ); + + if ( ntk.node_to_index( n ) == dst_idx ) + { + found = true; + return; + } + + ntk.foreach_fanin( n, [&]( auto const& f ) { + rec( ntk.get_node( f ) ); + } ); + }; + + rec( ntk.index_to_node( src_idx ) ); + return found; + } + + /* Experimental code */ + template + void multi_filter_and_match( multi_cuts_t const& multi_cuts, multi_single_matches_t const& multi_node_match_local ) + { + multi_cut_set.clear(); + multi_node_match.clear(); + multi_cut_set.reserve( multi_node_match_local.size() ); + multi_node_match.reserve( multi_node_match_local.size() ); + + for ( auto const& pair : multi_node_match_local ) + { + uint32_t index1 = pair[0].node_index; + uint32_t index2 = pair[1].node_index; + uint32_t cut_index1 = pair[0].cut_index; + uint32_t cut_index2 = pair[1].cut_index; + + assert( index1 < index2 ); + + multi_cut_t const& cut1 = multi_cuts.cuts( index1 )[cut_index1]; + multi_cut_t const& cut2 = multi_cuts.cuts( index2 )[cut_index2]; + + /* Reject structurally dependent pairs. */ + const bool index1_in_tfi_of_index2 = node_in_tfi( index1, index2 ); + const bool index2_in_tfi_of_index1 = node_in_tfi( index2, index1 ); + if ( index1_in_tfi_of_index2 || index2_in_tfi_of_index1 ) + { + continue; + } + + /* remove incompatible multi-output cuts */ + bool is_new = true; + uint32_t insertion_index = multi_node_match.size(); + + if constexpr ( OverlapFilter ) + { + if ( multi_gate_check_overlapping( index1, index2, cut1 ) ) + continue; + } + else + { + if ( multi_gate_check_incompatible( index1, index2, is_new, insertion_index ) ) + continue; + } + + /* copy cuts */ + cut_t new_cut1, new_cut2; + new_cut1.set_leaves( cut1.begin(), cut1.end() ); + new_cut2.set_leaves( cut2.begin(), cut2.end() ); + new_cut1->function = kitty::extend_to( multi_cuts.truth_table( cut1 ) ); + new_cut2->function = kitty::extend_to( multi_cuts.truth_table( cut2 ) ); + + /* Multi-output Boolean matching, continue if no match */ + std::array cut_pair = { new_cut1, new_cut2 }; + if ( !multi_compute_cut_data( cut_pair ) ) + continue; + + /* mark multioutput gate */ + if constexpr ( OverlapFilter ) + { + multi_gate_mark_visited( index1, index2, cut1 ); + node_tuple_match[index1].has_info = 1; + node_tuple_match[index1].lowest_index = 1; + node_tuple_match[index1].index = multi_node_match.size(); + node_tuple_match[index2].has_info = 1; + node_tuple_match[index2].highest_index = 1; + node_tuple_match[index2].index = multi_node_match.size(); + } + else + { + multi_gate_mark_compatibility( index1, index2, insertion_index ); + } + + /* add cut */ + multi_cut_set.push_back( cut_pair ); + + /* re-index data */ + multi_match_data new_data1{}; + multi_match_data new_data2{}; + + new_data1.node_index = index1; + new_data1.cut_index = static_cast( multi_cut_set.size() - 1 ); + new_data1.in_tfi = index1_in_tfi_of_index2 ? 1 : 0; + + new_data2.node_index = index2; + new_data2.cut_index = static_cast( multi_cut_set.size() - 1 ); + new_data2.in_tfi = index2_in_tfi_of_index1 ? 1 : 0; + + multi_match_t p = { new_data1, new_data2 }; + + /* add cuts to the correct bucket */ + if ( is_new ) + { + multi_node_match.push_back( { p } ); + } + else + { + multi_node_match[insertion_index].push_back( p ); + } + } + } + + bool multi_compute_cut_data( std::array& cut_tuple ) + { + std::array, max_multioutput_output_size> tts; + std::array, max_multioutput_output_size> tts_order; + std::array order = {}; + std::array phase = { 0 }; + std::array phase_order; + + std::iota( order.begin(), order.end(), 0 ); + + for ( auto i = 0; i < max_multioutput_output_size; ++i ) + { + tts[i] = kitty::extend_to( cut_tuple[i]->function ); + if constexpr (NInputs <= 6) { + if ( ( tts[i]._bits & 1 ) == 1 ) + { + tts[i] = ~tts[i]; + phase[i] = 1; + } + } else { + if ( ( tts[i]._bits[0] & 1 ) == 1 ) + { + tts[i] = ~tts[i]; + phase[i] = 1; + } + } + } + + std::stable_sort( order.begin(), order.end(), [&]( size_t a, size_t b ) { + return tts[a] < tts[b]; + } ); + + std::transform( order.begin(), order.end(), tts_order.begin(), [&]( size_t a ) { + return tts[a]; + } ); + + std::transform( order.begin(), order.end(), phase_order.begin(), [&]( uint8_t a ) { + return phase[a]; + } ); + + auto const multigates_match = library.get_multi_supergates( tts_order ); + + /* Ignore not matched cuts */ + if ( multigates_match == nullptr ) + return false; + + /* add cut matches */ + for ( auto i = 0; i < max_multioutput_output_size; ++i ) + { + cut_tuple[order[i]]->supergates[0] = nullptr; + cut_tuple[order[i]]->supergates[1] = nullptr; + cut_tuple[order[i]]->ignore = false; + std::vector> const* multigate = &( ( *multigates_match )[i] ); + cut_tuple[order[i]]->supergates[phase_order[i]] = multigate; + } + + return true; + } + + inline bool multi_check_partially_dangling( uint32_t index1, uint32_t index2, multi_cut_t const& cut1 ) + { + bool valid = true; + + /* check containment of cut1 in cut2 and viceversa */ + if ( index1 > index2 ) + { + std::swap( index1, index2 ); + } + + ntk.foreach_fanin( ntk.index_to_node( index2 ), [&]( auto const& f ) { + auto g = ntk.get_node( f ); + if ( ntk.node_to_index( g ) == index1 && ntk.fanout_size( g ) == 1 ) + { + valid = false; + } + return valid; + } ); + + if ( !valid ) + return false; + + if ( !is_contained_mffc( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut1 ) ) + return false; + + return true; + } + + inline bool multi_gate_check_overlapping( uint32_t index1, uint32_t index2, multi_cut_t const& cut ) + { + bool contained = false; + + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + contained = multi_mark_visited_rec( ntk.index_to_node( index1 ) ); + contained |= multi_mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + inline bool multi_gate_check_incompatible( uint32_t index1, uint32_t index2, bool& is_new, uint32_t& data_index ) + { + /* check cut assigned cut outputs, specialized code for 2 outputs */ + if ( !node_tuple_match[index1].has_info && !node_tuple_match[index2].has_info ) + return false; + + if ( node_tuple_match[index1].has_info && node_tuple_match[index2].has_info ) + { + uint32_t current_assignment = node_tuple_match[index1].index; + if ( current_assignment != node_tuple_match[index2].index ) + return true; + is_new = false; + data_index = current_assignment; + return false; + } + + return true; + } + + inline void multi_gate_mark_compatibility( uint32_t index1, uint32_t index2, uint32_t mark_value ) + { + node_tuple_match[index1].has_info = 1; + node_tuple_match[index1].lowest_index = 1; + node_tuple_match[index1].index = mark_value; + node_tuple_match[index2].has_info = 1; + node_tuple_match[index2].highest_index = 1; + node_tuple_match[index2].index = mark_value; + } + + inline void multi_gate_mark_visited( uint32_t index1, uint32_t index2, multi_cut_t const& cut ) + { + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + /* mark */ + multi_mark_visited_rec( ntk.index_to_node( index1 ) ); + multi_mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + } + + template + bool multi_mark_visited_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return false; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + if constexpr ( MARK ) + { + ntk.set_visited( n, ntk.trav_id() ); + } + + bool contained = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + contained |= multi_mark_visited_rec( ntk.get_node( f ) ); + + if constexpr ( !MARK ) + { + if ( contained ) + return false; + } + + return true; + } ); + + return contained; + } + + bool is_contained_mffc( node root, node n, multi_cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + bool valid = true; + tmp_visited.clear(); + dereference_node_rec( root ); + + if ( ntk.fanout_size( n ) == 0 ) + valid = false; + + for ( uint64_t g : tmp_visited ) + ntk.incr_fanout_size( ntk.index_to_node( g ) ); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return valid; + } + + void dereference_node_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + node g = ntk.get_node( f ); + if ( ntk.decr_fanout_size( g ) == 0 ) + { + dereference_node_rec( g ); + } + tmp_visited.push_back( ntk.node_to_index( g ) ); + } ); + } + + void multi_add_choices( choice_view& choice_ntk ) + { + for ( auto& field : multi_node_match ) + { + auto& pair = field.front(); + uint32_t index1 = pair[0].node_index; + uint32_t index2 = pair[1].node_index; + uint32_t cut_index1 = pair[0].cut_index; + cut_t const& cut = multi_cut_set[cut_index1][0]; + + /* don't add choice if in TFI, set TFI bit */ + if ( multi_is_in_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ) ) + { + /* if there is a path of length > 1 linking node 1 and 2, save as TFI node */ + uint32_t in_tfi = multi_is_in_direct_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ) ) ? 0 : 1; + for ( auto& match : field ) + match[0].in_tfi = in_tfi; + /* add a TFI dependency */ + ntk.set_value( ntk.index_to_node( index1 ), index2 ); + // multi_set_tfi_dependency( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ); + continue; + } + + choice_ntk.add_choice( ntk.index_to_node( index1 ), ntk.index_to_node( index2 ) ); + + assert( choice_ntk.count_choices( ntk.index_to_node( index1 ) ) == 2 ); + } + } + + bool multi_topo_sort_rec( choice_view& choice_ntk, node const& n ) + { + /* is permanently marked? */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + /* loop detected: backtrack to remove the cause */ + if ( ntk.visited( n ) == ntk.trav_id() - 1 ) + return false; + + /* get the representative (smallest index) */ + node repr = choice_ntk.get_choice_representative( n ); + + /* loop detected: backtrack to remove the cause */ + if ( ntk.visited( repr ) == ntk.trav_id() - 1 ) + return false; + + /* solve the TFI dependency first */ + node dependency_node = ntk.index_to_node( ntk.value( n ) ); + if ( dependency_node > 0 && ntk.visited( dependency_node ) != ntk.trav_id() - 1 ) + { + if ( !multi_topo_sort_rec( choice_ntk, dependency_node ) ) + return false; + assert( ntk.visited( n ) == ntk.trav_id() ); + return true; + } + + /* for all the choices */ + uint32_t i = 0; + bool check = true; + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited or temporarily marked */ + assert( ntk.visited( g ) != ntk.trav_id() ); + assert( ntk.visited( g ) != ntk.trav_id() - 1 ); + + /* mark node temporarily */ + ntk.set_visited( g, ntk.trav_id() - 1 ); + + /* mark children */ + ntk.foreach_fanin( g, [&]( auto const& f ) { + check = multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + return check; + } ); + + /* cycle detected: backtrack to the last choice jump */ + if ( !check ) + { + /* revert visited */ + ntk.set_visited( g, ntk.trav_id() - 2 ); + if ( i > 0 && n == repr ) + { + /* fix cycle: remove multi-output match */ + choice_ntk.foreach_choice( repr, [&]( auto const& p ) { + auto idx = ntk.node_to_index( p ); + node_tuple_match[idx].data = 0; + node_match[idx].multioutput_match[0] = false; + node_match[idx].multioutput_match[1] = false; + return true; + } ); + choice_ntk.remove_choice( g ); + check = true; + } + return false; + } + + ++i; + return true; + } ); + + if ( !check ) + { + return false; + } + + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited */ + assert( ntk.visited( g ) != ntk.trav_id() ); + + /* mark node n permanently */ + ntk.set_visited( g, ntk.trav_id() ); + + /* visit node */ + topo_order.push_back( g ); + + return true; + } ); + + return true; + } + + inline bool multi_is_in_tfi( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + multi_mark_visited_rec( root ); + bool contained = ntk.visited( n ) == ntk.trav_id(); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + inline bool multi_is_in_direct_tfi( node const& root, node const& n ) + { + bool contained = false; + + ntk.foreach_fanin( root, [&]( auto const& f ) { + if ( ntk.get_node( f ) == n ) + contained = true; + } ); + + return contained; + } + + inline void multi_set_tfi_dependency( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + + /* add a TFI dependencies */ + ntk.set_value( n, ntk.node_to_index( root ) ); + ntk.set_visited( n, ntk.trav_id() ); + multi_set_tfi_dependency_rec( root, ntk.node_to_index( root ) ); + + /* reset root's dependency info */ + ntk.set_value( root, 0 ); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + } + + void multi_set_tfi_dependency_rec( node const& n, uint32_t const dependency_info ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.set_value( n, dependency_info ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_set_tfi_dependency_rec( ntk.get_node( f ), dependency_info ); + } ); + } + +private: + Ntk const& ntk; + tech_library const& library; + emap_params const& ps; + emap_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + double delay{ 0.0f }; /* current delay of the mapping */ + double area{ 0.0f }; /* current area of the mapping */ + uint32_t inv{ 0 }; /* current inverter count */ + + /* lib inverter info */ + float lib_inv_area; + float lib_inv_delay; + uint32_t lib_inv_id; + + /* lib buffer info */ + float lib_buf_area; + float lib_buf_delay; + uint32_t lib_buf_id; + + std::vector> topo_order; + node_match_t node_match; + std::vector node_tuple_match; + std::vector switch_activity; + std::vector tmp_visited; + + /* cut computation */ + std::vector cuts; /* compressed representation of cuts */ + cut_merge_t lcuts; /* cut merger container */ + cut_set_t temp_cuts; /* temporary cut set container */ + truth_compute_t ltruth; /* truth table merger container */ + support_t lsupport; /* support merger container */ + uint32_t cuts_total{ 0 }; /* current computed cuts */ + + /* multi-output matching */ + multi_cut_set_t multi_cut_set; /* set of multi-output cuts */ + multi_matches_t multi_node_match; /* matched multi-output gates */ + + time_point time_begin; +}; + +} /* namespace detail */ + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a block network that supports multi-output cells. + * + * The novelties of this mapper are contained in 2 publications: + * - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023. + * - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +cell_view emap( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_block(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a k-LUT network. Each LUT abstacts a gate of the technology library. + * + * The novelties of this mapper are contained in 2 publications: + * - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023. + * - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view emap_klut( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_klut(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology node mapping. + * + * This function implements a simple technology mapping algorithm. + * The algorithm maps each node to the best implementation in the technology library. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_binding` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view emap_node_map( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_node_map(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology node mapping. + * + * This function implements a simple technology mapping algorithm. + * The algorithm maps each node to the first implementation in the technology library. + * + * The input must be a binding_view with the gates correctly loaded. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_binding` + * + * \param ntk Network + * + */ +template +void emap_load_mapping( Ntk& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + + /* build the library map */ + using lib_t = std::unordered_map>; + lib_t tt_to_gate; + + for ( auto const& g : ntk.get_library() ) + { + tt_to_gate[g.function] = g.id; + } + + ntk.foreach_gate( [&]( auto const& n ) { + if ( auto it = tt_to_gate.find( ntk.node_function( n ) ); it != tt_to_gate.end() ) + { + ntk.add_binding( n, it->second ); + } + else + { + std::cout << fmt::format( "[e] node mapping for node {} failed: no match in the tech library\n", ntk.node_to_index( n ) ); + } + } ); +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/null.hpp b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/null.hpp new file mode 100644 index 00000000000..8a71dd0e315 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/node_resynthesis/null.hpp @@ -0,0 +1,57 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file null.hpp + \brief No resynthesis (as default synthesis engine) + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../../traits.hpp" +#include + +namespace mockturtle +{ + +template +class null_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + (void)ntk; + (void)function; + (void)begin; + (void)end; + (void)fn; + } +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/algorithms/simulation.hpp b/third-party/mockturtle/include/mockturtle/algorithms/simulation.hpp new file mode 100644 index 00000000000..119c96adaf3 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/algorithms/simulation.hpp @@ -0,0 +1,982 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file simulation.hpp + \brief Simulate networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Abstract template class for simulation. */ +template +class default_simulator +{ +public: + default_simulator() = delete; +}; + +/*! \brief Simulates Boolean assignments. + * + * This simulator simulates Boolean values. A vector with assignments for each + * primary input must be passed to the constructor. + */ +template<> +class default_simulator +{ +public: + default_simulator() = delete; + default_simulator( std::vector const& assignments ) : assignments( assignments ) {} + + bool compute_constant( bool value ) const { return value; } + bool compute_pi( uint32_t index ) const { return assignments[index]; } + bool compute_not( bool value ) const { return !value; } + +private: + std::vector assignments; +}; + +/*! \brief Simulates Boolean assignments with input word. + * + * This simulator simulates Boolean values. A bitstring with assignments for + * each primary input must be passed to the constructor. Because this + * bitstring can have at most 64 bits, this simulator is not suitable for + * logic networks with more than 64 primary inputs. + */ +class input_word_simulator +{ +public: + input_word_simulator( uint64_t word ) : word( word ) {} + + bool compute_constant( bool value ) const { return value; } + bool compute_pi( uint32_t index ) const { return ( word >> index ) & 1; } + bool compute_not( bool value ) const { return !value; } + +private: + uint64_t word; +}; + +/*! \brief Simulates truth tables. + * + * This simulator simulates truth tables. Each primary input is assigned the + * projection function according to the index. The number of variables be + * passed to the constructor of the simulator. + */ +template<> +class default_simulator +{ +public: + default_simulator() = delete; + default_simulator( unsigned num_vars ) : num_vars( num_vars ) {} + + kitty::dynamic_truth_table compute_constant( bool value ) const + { + kitty::dynamic_truth_table tt( num_vars ); + return value ? ~tt : tt; + } + + kitty::dynamic_truth_table compute_pi( uint32_t index ) const + { + kitty::dynamic_truth_table tt( num_vars ); + kitty::create_nth_var( tt, index ); + return tt; + } + + kitty::dynamic_truth_table compute_not( kitty::dynamic_truth_table const& value ) const + { + return ~value; + } + +private: + unsigned num_vars; +}; + +/*! \brief Simulates truth tables. + * + * This simulator simulates truth tables. Each primary input is assigned the + * projection function according to the index. The number of variables must be + * known at compile time. + */ +template +class default_simulator> +{ +public: + kitty::static_truth_table compute_constant( bool value ) const + { + kitty::static_truth_table tt; + return value ? ~tt : tt; + } + + kitty::static_truth_table compute_pi( uint32_t index ) const + { + kitty::static_truth_table tt; + kitty::create_nth_var( tt, index ); + return tt; + } + + kitty::static_truth_table compute_not( kitty::static_truth_table const& value ) const + { + return ~value; + } +}; + +/*! \brief Simulates partial truth tables. + * + * This simulator simulates partial truth tables, whose length is flexible + * and new simulation patterns can be added. + */ +class partial_simulator +{ + friend class bit_packed_simulator; + +public: + partial_simulator() {} + + /*! \brief Create a `partial_simulator` with random simulation patterns. + * + * \param num_pis Number of primary inputs, which is the same as the length of a simulation pattern. + * \param num_patterns Number of initial random simulation patterns. + */ + partial_simulator( uint32_t num_pis, uint32_t num_patterns, std::default_random_engine::result_type seed = 1 ) + : num_patterns( num_patterns ) + { + assert( num_pis > 0u ); + + for ( auto i = 0u; i < num_pis; ++i ) + { + patterns.emplace_back( num_patterns ); + kitty::create_random( patterns.back(), seed + i ); + } + } + + /* copy constructors */ + partial_simulator( partial_simulator const& sim ) = default; + partial_simulator& operator=( partial_simulator const& sim ) = default; + + /*! \brief Create a `partial_simulator` with given simulation patterns. + * + * \param initial_patterns Initial simulation patterns. + */ + partial_simulator( std::vector const& initial_patterns ) + : patterns( initial_patterns ), num_patterns( patterns.at( 0 ).num_bits() ) + {} + + /*! \brief Create a `partial_simulator` with simulation patterns read from a file. + * + * The simulation pattern file should contain `num_pis` lines of the same length. + * Each line is the simulation signature of a primary input, represented in hexadecimal. + * + * \param filename Name of the simulation pattern file. + * \param length Number of simulation patterns to keep. Should not be greater than 4 times + * the length of a line in the file. Setting this parameter to 0 means to keep all patterns in the file. + */ + partial_simulator( const std::string& filename, uint32_t length = 0u ) + { + std::ifstream in( filename, std::ifstream::in ); + std::string line; + + while ( getline( in, line ) ) + { + patterns.emplace_back( line.length() * 4 ); + kitty::create_from_hex_string( patterns.back(), line ); + if ( length != 0u ) + { + patterns.back().resize( length ); + } + } + + in.close(); + + assert( patterns.size() > 0 ); + num_patterns = patterns[0].num_bits(); + } + + kitty::partial_truth_table compute_constant( bool value ) const + { + kitty::partial_truth_table zero( num_patterns ); + return value ? ~zero : zero; + } + + kitty::partial_truth_table compute_pi( uint32_t index ) const + { + return patterns.at( index ); + } + + kitty::partial_truth_table compute_not( kitty::partial_truth_table const& value ) const + { + return ~value; + } + + /*! \brief Get the current number of simulation patterns. */ + uint32_t num_bits() const + { + return num_patterns; + } + + /*! \brief Add a pattern (primary input assignment) into the pattern set. + * + * \param pattern The pattern. Length should be the same as number of PIs. + */ + void add_pattern( std::vector const& pattern ) + { + assert( pattern.size() == patterns.size() ); + + for ( auto i = 0u; i < pattern.size(); ++i ) + { + patterns.at( i ).add_bit( pattern.at( i ) ); + } + ++num_patterns; + } + + /*! \brief Get the simulation patterns. + * + * \return A vector of `num_pis()` patterns stored in `kitty::partial_truth_table`s. + */ + std::vector get_patterns() const + { + return patterns; + } + + template, typename = std::enable_if_t> + void remove_CDC_patterns( Ntk const& ntk ) + { + std::vector pattern( patterns.size() ); + for ( int i = 0; i < (int)num_patterns; ++i ) + { + for ( auto j = 0u; j < patterns.size(); ++j ) + { + pattern[j] = kitty::get_bit( patterns[j], i ); + } + if ( ntk.pattern_is_EXCDC( pattern ) ) + { + for ( auto j = 0u; j < patterns.size(); ++j ) + { + kitty::copy_bit( patterns[j], num_patterns - 1, patterns[j], i ); + } + --num_patterns; + --i; + } + } + for ( auto j = 0u; j < patterns.size(); ++j ) + { + patterns[j].resize( num_patterns ); + } + } + +private: + std::vector patterns; + uint32_t num_patterns; +}; + +/*! \brief Simulates partial truth tables, and performs bit packing when requested. + * + * This class has the same interfaces as `partial_simulator`, except that + * (1) care bits should be provided as the second argument of `add_pattern`; and + * (2) `pack_bits` can be called to reduce the size of pattern set. + */ +class bit_packed_simulator : public partial_simulator +{ +public: + using partial_simulator::compute_constant; + using partial_simulator::compute_not; + using partial_simulator::compute_pi; + using partial_simulator::get_patterns; + using partial_simulator::num_bits; + + bit_packed_simulator() {} + + bit_packed_simulator( uint32_t num_pis, uint32_t num_patterns, std::default_random_engine::result_type seed = 1 ) + : partial_simulator( num_pis, num_patterns, seed ), packed_patterns( num_patterns ) + { + fill_cares( num_pis ); + } + + /* copy constructors */ + bit_packed_simulator( bit_packed_simulator const& sim ) = default; + bit_packed_simulator& operator=( bit_packed_simulator const& sim ) = default; + + /* copy constructor from `partial_simulator` */ + bit_packed_simulator( partial_simulator const& sim ) + : partial_simulator( sim ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + bit_packed_simulator( std::vector const& initial_patterns ) + : partial_simulator( initial_patterns ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + bit_packed_simulator( const std::string& filename, uint32_t length = 0u ) + : partial_simulator( filename, length ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + /*! \brief Add a pattern (primary input assignment) into the pattern set. + * + * \param pattern The pattern. Length should be the same as number of PIs. + * \param care_bits Care bits of the pattern. Length should be the same as `pattern`. + */ + void add_pattern( std::vector const& pattern, std::vector const& care_bits ) + { + assert( pattern.size() == care_bits.size() ); + assert( pattern.size() == patterns.size() ); + + for ( auto i = 0u; i < pattern.size(); ++i ) + { + patterns.at( i ).add_bit( pattern.at( i ) ); + care.at( i ).add_bit( care_bits.at( i ) ); + } + ++num_patterns; + } + + /*! \brief Try to pack the newly added patterns (since the last call) into preceding patterns. + * + * \return `true` when some patterns are packed (so that update of simulated truth tables is needed) + */ + bool pack_bits() + { + if ( num_patterns == 0u ) + { + return false; + } + if ( num_patterns == packed_patterns ) + { + return false; + } + assert( num_patterns > packed_patterns ); + + std::vector empty_slots; + /* for each unpacked pattern (at `p`), try to pack it into one of the patterns before it (at `pos` in block `block`). */ + for ( int64_t p = num_patterns - 1; p >= (int64_t)packed_patterns; --p ) + { + for ( auto block = p < 1024 ? 0 : std::rand() % ( p >> 6 ); block <= ( p >> 6 ); ++block ) + { + uint64_t unavailable = 0u; + /* check each PI */ + for ( auto i = 0u; i < patterns.size(); ++i ) + { + if ( !kitty::get_bit( care[i], p ) ) + { + continue; + } /* only check for the cared PIs of p */ + unavailable |= care[i]._bits[block]; + } + auto pos = kitty::find_first_bit_in_word( ~unavailable ); + if ( pos != -1 && ( block < ( p >> 6 ) || pos < ( p % 64 ) ) ) + { + move_pattern( p, pos + ( block << 6 ) ); + empty_slots.emplace_back( p ); + break; + } + } + } + + if ( empty_slots.size() > 0u ) + { + /* fill the empty slots (from smaller values; `empty_slots` should be reversely sorted) */ + /* `empty_slots[j]` is the smallest position where larger positions are all empty */ + int64_t j = 0; + for ( int64_t i = empty_slots.size() - 1; i >= 0; --i ) + { + while ( j <= i && empty_slots[j] >= num_patterns - 1 ) + { + if ( empty_slots[j] == num_patterns - 1 ) + { + --num_patterns; + } + ++j; + if ( j == (int64_t)empty_slots.size() ) + { + break; + } + } + if ( j > i ) + { + break; + } + move_pattern( num_patterns - 1, empty_slots[i] ); + --num_patterns; + } + assert( patterns[0].num_bits() - num_patterns == empty_slots.size() ); + for ( auto i = 0u; i < patterns.size(); ++i ) + { + patterns[i].resize( num_patterns ); + care[i].resize( num_patterns ); + } + packed_patterns = num_patterns; + return true; + } + packed_patterns = num_patterns; + return false; + } + + void randomize_dont_care_bits( std::default_random_engine::result_type seed = 1 ) + { + for ( auto i = 0u; i < patterns.size(); ++i ) + { + kitty::partial_truth_table tt( num_patterns ); + kitty::create_random( tt, std::default_random_engine::result_type( seed + patterns.size() + i ) ); + patterns.at( i ) = ( patterns.at( i ) & care.at( i ) ) | ( tt & ~care.at( i ) ); + } + } + +private: + /* all bits in patterns generated before construction are care bits */ + void fill_cares( uint64_t const num_pis ) + { + for ( auto i = 0u; i < num_pis; ++i ) + { + care.emplace_back( num_patterns ); + care.back() = ~care.back(); + } + } + + /* move the pattern at position `from` to position `to`. */ + void move_pattern( uint64_t const from, uint64_t const to ) + { + for ( auto i = 0u; i < patterns.size(); ++i ) + { + if ( !kitty::get_bit( care[i], from ) ) + { + continue; + } + assert( !kitty::get_bit( care[i], to ) ); + kitty::copy_bit( patterns[i], from, patterns[i], to ); + kitty::set_bit( care[i], to ); + kitty::clear_bit( care[i], from ); + } + } + +private: + std::vector care; + uint32_t packed_patterns; +}; + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a map that maps each node to its computed simulation + * value. + * + * **Required network functions:** + * - `foreach_po` + * - `get_constant` + * - `constant_value` + * - `get_node` + * - `foreach_pi` + * - `foreach_gate` + * - `fanin_size` + * - `num_pos` + * - `compute` + * + * \param ntk Network + * \param sim Simulator, which implements the simulator interface + */ +template> +node_map simulate_nodes( Ntk const& ntk, Simulator const& sim = Simulator() ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for SimulationType" ); + + node_map node_to_value( ntk ); + + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); + + ntk.foreach_gate( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[f]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } ); + + return node_to_value; +} + +namespace detail +{ + +template +void simulate_nodes_with_node_map( Ntk const& ntk, Container& node_to_value, Simulator const& sim ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for SimulationType" ); + + /* constants */ + if ( !node_to_value.has( ntk.get_node( ntk.get_constant( false ) ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + } + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + if ( !node_to_value.has( ntk.get_node( ntk.get_constant( true ) ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + } + + /* pis */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + if ( !node_to_value.has( n ) ) + { + node_to_value[n] = sim.compute_pi( i ); + } + } ); + + /* gates */ + ntk.foreach_gate( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + if ( !node_to_value.has( n ) ) + { + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + } ); +} + +} // namespace detail + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a map that maps each node to its computed simulation + * value. + * + * **Required network functions:** + * - `foreach_po` + * - `get_constant` + * - `constant_value` + * - `get_node` + * - `foreach_pi` + * - `foreach_gate` + * - `fanin_size` + * - `num_pos` + * - `compute` + * + * \param ntk Network + * \param node_to_value A map from nodes to values + * \param sim Simulator, which implements the simulator interface + */ +template> +void simulate_nodes( Ntk const& ntk, unordered_node_map& node_to_value, Simulator const& sim = Simulator() ) +{ + detail::simulate_nodes_with_node_map>( ntk, node_to_value, sim ); +} + +template> +void simulate_nodes( Ntk const& ntk, incomplete_node_map& node_to_value, Simulator const& sim = Simulator() ) +{ + detail::simulate_nodes_with_node_map>( ntk, node_to_value, sim ); +} + +namespace detail +{ +/* Forward declaration */ +template +void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ); + +template +void simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + else if ( node_to_value[ntk.get_node( f )].num_bits() != sim.num_bits() ) + { + re_simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); +} + +template +void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + else if ( node_to_value[ntk.get_node( f )].num_bits() != sim.num_bits() ) + { + re_simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + ntk.compute( n, node_to_value[n], fanin_values.begin(), fanin_values.end() ); +} + +template +void update_const_pi( Ntk const& ntk, Container& node_to_value, Simulator const& sim ) +{ + /* constants */ + node_to_value[ntk.get_constant( false )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_constant( true )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + + /* pis */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); +} + +} // namespace detail + +/*! \brief (Re-)simulate `n` and its transitive fanin cone. + * + * Note that re-simulation (when `node_to_value.has( n ) == true`) is only done + * for the last block, no matter how many bits are used in this block. + * Hence, it is advised to call `simulate_nodes` with `simulate_whole_tt = false` + * whenever `sim.num_bits() % 64 == 0`. + * + */ +template> +void simulate_node( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_compute_v, "Ntk does not implement the compute specialization for kitty::partial_truth_table" ); + static_assert( has_compute_inplace_v, "Ntk does not implement the in-place compute specialization for kitty::partial_truth_table" ); + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + if ( node_to_value[ntk.get_node( ntk.get_constant( false ) )].num_bits() != sim.num_bits() ) + { + detail::update_const_pi( ntk, node_to_value, sim ); + } + + if ( !node_to_value.has( n ) ) + { + detail::simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + else if ( node_to_value[n].num_bits() != sim.num_bits() ) + { + detail::re_simulate_fanin_cone( ntk, n, node_to_value, sim ); + } +} + +/*! \brief Simulates a network with `partial_simulator` (or `bit_packed_simulator`). + * + * This is the specialization for `partial_truth_table`. + * This function simulates every node in the circuit. + * + * \param simulate_whole_tt When this parameter is true, it is assumed that `node_to_value.has( n )` is false for every node. + * In contrast, when this parameter is false, only the last block of `partial_truth_table` will be re-computed, + * and it is assumed that `node_to_value.has( n )` is true for every node. + */ +template> +void simulate_nodes( Ntk const& ntk, Container& node_to_value, Simulator const& sim, bool simulate_whole_tt ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_compute_v, "Ntk does not implement the compute specialization for kitty::partial_truth_table" ); + static_assert( has_compute_inplace_v, "Ntk does not implement the in-place compute specialization for kitty::partial_truth_table" ); + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + detail::update_const_pi( ntk, node_to_value, sim ); + + /* gates */ + if ( simulate_whole_tt ) + { + ntk.foreach_gate( [&]( auto const& n ) { + if ( !node_to_value.has( n ) ) + { + detail::simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + } ); + } + else + { + ntk.foreach_gate( [&]( auto const& n ) { + assert( node_to_value.has( n ) ); + if ( node_to_value[n].num_bits() != sim.num_bits() ) + { + detail::re_simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + } ); + } +} + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a vector that maps each primary output (ordered by + * position) to it's simulation value (taking possible complemented attributes + * into account). + * + * **Required network functions:** + * - `foreach_po` + * - `is_complemented` + * - `compute` + * + * \param ntk Network + * \param sim Simulator, which implements the simulator interface + */ +template> +std::vector simulate( Ntk const& ntk, Simulator const& sim = Simulator() ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po function" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented function" ); + static_assert( has_compute_v, "Ntk does not implement the compute function for SimulationType" ); + + auto const node_to_value = simulate_nodes( ntk, sim ); + + std::vector po_values( ntk.num_pos() ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk.is_complemented( f ) ) + { + po_values[i] = sim.compute_not( node_to_value[f] ); + } + else + { + po_values[i] = node_to_value[f]; + } + } ); + return po_values; +} + +/*! \brief Simulates a buffered network + * + * The implementation is only slightly different from `simulate` by + * replacing `foreach_gate` with `foreach_node` and checking `fanin_size` + * because buffers are not counted as gates but still need to be simulated. + */ +template +std::vector> simulate_buffered( Ntk const& ntk ) +{ + static_assert( has_is_buf_v, "Ntk is not a buffered network type" ); + static_assert( has_compute_v>, "Ntk does not implement the compute function for static_truth_table" ); + assert( ntk.num_pis() == NumPIs ); + + default_simulator> sim; + node_map, Ntk> node_to_value( ntk ); + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); + ntk.foreach_node( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + if ( ntk.fanin_size( n ) > 0 ) + { + std::vector> fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[f]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + } ); + + std::vector> po_values( ntk.num_pos() ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk.is_complemented( f ) ) + { + po_values[i] = sim.compute_not( node_to_value[f] ); + } + else + { + po_values[i] = node_to_value[f]; + } + } ); + return po_values; +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/io/genlib_reader.hpp b/third-party/mockturtle/include/mockturtle/io/genlib_reader.hpp new file mode 100644 index 00000000000..51d4034e779 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/genlib_reader.hpp @@ -0,0 +1,168 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file genlib_reader.hpp + \brief Reader visitor for GENLIB files + + \author Alessandro Tempia Calvino + \author Heinz Riener +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +enum class phase_type : uint8_t +{ + INV = 0, + NONINV = 1, + UNKNOWN = 2, +}; + +struct pin +{ + std::string name; + phase_type phase; + double input_load; + double max_load; + double rise_block_delay; + double rise_fanout_delay; + double fall_block_delay; + double fall_fanout_delay; +}; /* pin */ + +struct gate +{ + unsigned int id; + std::string name; + std::string expression; + uint32_t num_vars; + kitty::dynamic_truth_table function; + double area; + std::vector pins; + std::string output_name; +}; /* gate */ + +/*! \brief lorina callbacks for GENLIB files. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + \endverbatim + */ +class genlib_reader : public lorina::genlib_reader +{ +public: + explicit genlib_reader( std::vector& gates ) + : gates( gates ) + {} + + virtual void on_gate( std::string const& name, std::string const& expression, double area, std::vector const& ps, std::string const& output_pin ) const override + { + std::vector pp; + std::vector pin_names; + + if ( ps.size() == 1 && ps[0].name == "*" ) + { + /* pins not defined, use names and appearance order of the expression */ + std::vector tokens; + std::string const delimitators{ " ()!\'*&+|^\t\r\n" }; + std::size_t prev = 0, pos; + while ( ( pos = expression.find_first_of( delimitators, prev ) ) != std::string::npos ) + { + if ( pos > prev ) + { + tokens.emplace_back( expression.substr( prev, pos - prev ) ); + } + prev = pos + 1; + } + if ( prev < expression.length() ) + { + tokens.emplace_back( expression.substr( prev, std::string::npos ) ); + } + for ( auto const& pin_name : tokens ) + { + if ( std::find( pin_names.begin(), pin_names.end(), pin_name ) == pin_names.end() ) + { + pp.emplace_back( pin{ pin_name, + phase_type( static_cast( ps[0].phase ) ), + ps[0].input_load, ps[0].max_load, + ps[0].rise_block_delay, ps[0].rise_fanout_delay, ps[0].fall_block_delay, ps[0].fall_fanout_delay } ); + pin_names.push_back( pin_name ); + } + } + } + else + { + for ( const auto& p : ps ) + { + pp.emplace_back( pin{ p.name, + phase_type( static_cast( p.phase ) ), + p.input_load, p.max_load, + p.rise_block_delay, p.rise_fanout_delay, p.fall_block_delay, p.fall_fanout_delay } ); + pin_names.push_back( p.name ); + } + } + + /* replace possible CONST0 or CONST1 by 0 and 1 */ + std::string formula( expression ); + std::size_t found = formula.find( "CONST" ); + if ( found != std::string::npos ) + { + formula.erase( found, found + 5 ); + } + + uint32_t num_vars = pin_names.size(); + + kitty::dynamic_truth_table tt{ num_vars }; + + if ( !kitty::create_from_formula( tt, formula, pin_names ) ) + { + /* formula error, skip gate */ + return; + } + + gates.emplace_back( gate{ static_cast( gates.size() ), name, + expression, num_vars, tt, area, pp, output_pin } ); + } + +protected: + std::vector& gates; +}; /* genlib_reader */ + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/io/super_reader.hpp b/third-party/mockturtle/include/mockturtle/io/super_reader.hpp new file mode 100644 index 00000000000..45bb082d483 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/io/super_reader.hpp @@ -0,0 +1,102 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file super_reader.hpp + \brief Reader visitor for SUPER files generated by ABC + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" + +#include + +namespace mockturtle +{ + +struct supergate_spec +{ + unsigned int id; + std::string name{}; + bool is_super{ false }; + std::vector fanin_id; +}; + +struct super_lib +{ + std::string genlib_name{}; + uint32_t max_num_vars{ 0u }; + uint32_t num_supergates{ 0u }; + uint32_t num_lines{ 0 }; + std::vector supergates{}; +}; + +/*! \brief lorina callbacks for SUPER files. + * + * SUPER files can be generated by ABC with the command `super`. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + super_lib supergates_spec; + lorina::read_super( "file.super", super_reader( supergates_spec ) ); + \endverbatim + */ +class super_reader : public lorina::super_reader +{ +public: + explicit super_reader( super_lib& lib ) + : lib( lib ) + { + } + + virtual void on_super_info( std::string const& genlib_name, uint32_t max_num_vars, uint32_t max_superGates, uint32_t num_lines ) const override + { + lib.genlib_name = genlib_name; + lib.max_num_vars = max_num_vars; + lib.num_supergates = max_superGates; + lib.num_lines = num_lines; + } + + virtual void on_supergate( std::string const& name, bool const& is_super, std::vector const& fanins_id ) const override + { + + lib.supergates.emplace_back( supergate_spec{ static_cast( lib.supergates.size() ), + name, + is_super, + fanins_id } ); + } + +protected: + super_lib& lib; +}; /* super_reader */ + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/networks/aig.hpp b/third-party/mockturtle/include/mockturtle/networks/aig.hpp new file mode 100644 index 00000000000..6646ab3dbcf --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/aig.hpp @@ -0,0 +1,1235 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aig.hpp + \brief AIG logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Hash function for AIGs (from ABC) */ +template +struct aig_hash +{ + uint64_t operator()( Node const& n ) const + { + uint64_t seed = -2011; + seed += n.children[0].index * 7937; + seed += n.children[1].index * 2971; + seed += n.children[0].weight * 911; + seed += n.children[1].weight * 353; + return seed; + } +}; + +/*! \brief AIG storage container + + AIGs have nodes with fan-in 2. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) + `data[0].h2`: Application-specific value + `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) +*/ +using aig_storage = storage, + empty_storage_data, + aig_hash>>; + +class aig_network +{ +public: + static constexpr bool is_aig_network_type = true; + static constexpr auto min_fanin_size = 2u; + static constexpr auto max_fanin_size = 2u; + + using base_type = aig_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( aig_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator aig_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( aig_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + aig_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + aig_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + aig_network clone() const + { + return { std::make_shared( *_storage ) }; + } + + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0].data = node.children[1].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = _storage->outputs.size(); + _storage->outputs.emplace_back( f.index, f.complement ); + return static_cast( po_index ); + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } + + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } + + signal create_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement ? b : get_constant( false ); + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return { it->second, 0 }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return create_and( !a, !b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_xnor( signal const& a, signal const& b ) + { + return !create_xor( a, b ); + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + auto a1 = !create_and( !cond, f_else ); + auto a2 = !create_and( cond, f_then ); + return create_and( a1, a2 ) ^ !f_compl; + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + auto a1 = create_and( a, b ); + auto a2 = create_and( c, !create_and( !a, !b ) ); + return create_or( a1, a2 ); + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_xor( create_xor( a, b ), c ); + } + + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } + + signal clone_node( aig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 2u ); + return create_and( children[0u], children[1u] ); + } + + std::optional has_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return a.complement == b.complement ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement == false ? get_constant( false ) : b; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, 0 ); + } + + return {}; + } + + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return std::nullopt; + } + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + // check for trivial cases? + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + return std::make_pair( n, diff_pol ? get_constant( false ) : child1 ); + } + else if ( child0.index == 0 ) /* constant child */ + { + return std::make_pair( n, child0.complement ? child1 : get_constant( false ) ); + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return; + } + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into the hash table + node.children[0] = child0; + node.children[1] = child1; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + /* delete the node (ignoring its current fanout_size) */ + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + /* if the node has been deleted, then deref fanout_size of + fanins and try to take them out if their fanout_size become 0 */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + /* recursively reset old node */ + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } + + void substitute_nodes( std::list> substitutions ) + { + auto clean_substitutions = [&]( node const& n ) { + substitutions.erase( std::remove_if( std::begin( substitutions ), std::end( substitutions ), + [&]( auto const& s ) { + if ( s.first == n ) + { + node const nn = get_node( s.second ); + if ( is_dead( nn ) ) + return true; + + /* deref fanout_size of the node */ + if ( fanout_size( nn ) > 0 ) + { + decr_fanout_size( nn ); + } + /* remove the node if it's fanout_size becomes 0 */ + if ( fanout_size( nn ) == 0 ) + { + take_out_node( nn ); + } + /* remove substitution from list */ + return true; + } + return false; /* keep */ + } ), + std::end( substitutions ) ); + }; + + /* register event to delete substitutions if their right-hand side + nodes get deleted */ + auto clean_sub_event = _events->register_delete_event( clean_substitutions ); + + /* increment fanout_size of all signals to be used in + substitutions to ensure that they will not be deleted */ + for ( const auto& s : substitutions ) + { + incr_fanout_size( get_node( s.second ) ); + } + + while ( !substitutions.empty() ) + { + auto const [old_node, new_signal] = substitutions.front(); + substitutions.pop_front(); + + for ( auto index = 1u; index < _storage->nodes.size(); ++index ) + { + /* skip CIs and dead nodes */ + if ( is_ci( index ) || is_dead( index ) ) + continue; + + /* skip nodes that will be deleted */ + if ( std::find_if( std::begin( substitutions ), std::end( substitutions ), + [&index]( auto s ) { return s.first == index; } ) != std::end( substitutions ) ) + continue; + + /* replace in node */ + if ( const auto repl = replace_in_node( index, old_node, new_signal ); repl ) + { + incr_fanout_size( get_node( repl->second ) ); + substitutions.emplace_back( *repl ); + } + } + + /* replace in outputs */ + replace_in_outputs( old_node, new_signal ); + + /* replace in substitutions */ + for ( auto& s : substitutions ) + { + if ( get_node( s.second ) == old_node ) + { + s.second = is_complemented( s.second ) ? !new_signal : new_signal; + incr_fanout_size( get_node( new_signal ) ); + } + } + + /* finally remove the node: note that we never decrement the + fanout_size of the old_node. instead, we remove the node and + reset its fanout_size to 0 knowing that it must be 0 after + substituting all references. */ + assert( !is_dead( old_node ) ); + take_out_node( old_node ); + + /* decrement fanout_size when released from substitution list */ + decr_fanout_size( get_node( new_signal ) ); + } + + _events->release_delete_event( clean_sub_event ); + } + + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 2; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + return n > 0 && !is_ci( n ); + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } + + kitty::dynamic_truth_table node_function( const node& n ) const + { + (void)n; + kitty::dynamic_truth_table _and( 2 ); + _and._bits[0] = 0x8; + return _and; + } + + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + } + + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto v1 = *begin++; + auto v2 = *begin++; + + return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + result.mask_bits(); + } + + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } + + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } + + auto& events() const + { + return *_events; + } + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::aig_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std diff --git a/third-party/mockturtle/include/mockturtle/networks/block.hpp b/third-party/mockturtle/include/mockturtle/networks/block.hpp new file mode 100644 index 00000000000..bf6feb47f1f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/block.hpp @@ -0,0 +1,1046 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file block.hpp + \brief Block logic network implementation with multi-output support + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct block_storage_data +{ + truth_table_cache cache; +}; + +/*! \brief Block node + * + * `data[0].h1` : Application-specific value + * `data[1].h1` : Visited flags + * `data[1].h2` : Total fan-out size (we use MSB to indicate whether a node is dead) + * `data[2+i].h1`: Function literal in truth table cache for the fanout + * `data[2+i].h2`: Fan-out size + * + */ +struct block_storage_node : block_fanin_node<2> +{ + block_storage_node() + { + data = decltype( data )( 3 ); + } + + bool operator==( block_storage_node const& other ) const + { + if ( data.size() != other.data.size() ) + return false; + + for ( auto i = 2; i < data.size() + 2; ++i ) + if ( ( data[i].h1 != other.data[i].h1 ) || ( children != other.children ) ) + return false; + + return true; + } +}; + +/*! \brief Block storage container + + ... +*/ +using block_storage = storage_no_hash; + +class block_network +{ +public: + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + static constexpr auto min_gate_output_size = 1; + static constexpr auto max_gate_output_size = 2; + static constexpr auto output_signal_bits = 1; + + using base_type = block_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), output( 0 ), index( index ) + { + } + + signal( uint32_t index ) + : complement( 0 ), output( 0 ), index( index ) + { + } + + signal( uint64_t index, uint64_t complement, uint64_t output ) + : complement( complement ), output( output ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( block_storage::node_type::pointer_type const& p ) + : complement( p.weight & 1 ), output( p.weight >> 1 ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t output : output_signal_bits; + uint64_t index : 63 - output_signal_bits; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, output, 0 }; + } + + signal operator-() const + { + return { index, output, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator block_storage::node_type::pointer_type() const + { + return {index, (static_cast(output) << 1) | complement}; + } + + operator uint64_t() const + { + return data; + } + +#if __cplusplus > 201703L + bool operator==( block_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + block_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + block_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + + block_network clone() const + { + return { std::make_shared( *_storage ) }; + } + +protected: + inline void _init() + { + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[2].h1 = 0; + _storage->nodes[1].data[2].h1 = 1; + } + +public: + signal get_constant( bool value = false ) const + { + return value ? signal( 1, 0 ) : signal( 0, 0 ); + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[2].h1 = 2; + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[1].h2++; + _storage->nodes[f.index].data[2 + f.output].h2++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, ( f.output << 1 ) | f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_multioutput( node const& n ) const + { + return _storage->nodes[n].data.size() > 3; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return n > 1 && _storage->nodes[n].children.size() == 0u; + } + + bool is_pi( node const& n ) const + { + return n > 1 && _storage->nodes[n].children.size() == 0u; + } + + bool constant_value( node const& n ) const + { + return n != 0; + } + + signal create_buf( signal const& a ) + { + return _create_node( { a }, 2 ); + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } + + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } + + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } + + signal create_ha( signal a, signal b ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b }, { 4, 12 } ); + } + + signal create_hai( signal a, signal b ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b }, { 5, 13 } ); + } + + signal create_fa( signal a, signal b, signal c ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b, c }, { 14, 18 } ); + } + + signal create_fai( signal a, signal b, signal c ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b, c }, { 15, 19 } ); + } + + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } + + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[2].h1 = literal; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[1].h2++; /* TODO: increase fanout count for output */ + _storage->nodes[c.index].data[2 + c.output].h2++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal _create_node( std::vector const& children, std::vector const& literals ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + + node.data = decltype( node.data )( 2 + literals.size() ); + + for ( auto i = 0; i < literals.size(); ++i ) + node.data[2 + i].h1 = literals[i]; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[1].h2++; + _storage->nodes[c.index].data[2 + c.output].h2++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal create_node( std::vector const& children, std::vector const& functions ) + { + assert( functions.size() > 0 ); + + if ( children.size() == 0u ) + { + assert( functions[0].num_vars() == 0u ); + return get_constant( !kitty::is_const0( functions[0] ) ); + } + std::vector literals; + for ( auto const& tt : functions ) + literals.push_back( _storage->data.cache.insert( tt ) ); + + return _create_node( children, literals ); + } + + signal clone_node( block_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + if ( other.is_multioutput( source ) ) + { + std::vector tts; + for ( auto i = 2; i < other._storage->nodes[source].data.size(); ++i ) + tts.push_back( other._storage->data.cache[other._storage->nodes[source].data[i].h1] ); + return create_node( children, tts ); + } + else + { + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[2].h1]; + return create_node( children, tt ); + } + } + + void replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + bool in_fanin = false; + auto& nobj = _storage->nodes[n]; + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + in_fanin = true; + break; + } + } + + if ( !in_fanin ) + return; + + // remember before + std::vector old_children( nobj.children.size() ); + std::transform( nobj.children.begin(), nobj.children.end(), old_children.begin(), []( auto c ) { return signal{ c }; } ); + + /* replace in node */ + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + child = signal{ new_signal.data ^ ( child.data & 1 ) }; + // increment fan-out of new node + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + replace_in_node( n, old_node, new_signal ); + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output = signal{ new_signal.data ^ ( output.data & 1 ) }; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n < 2 || is_ci( n ) ) + return; + + /* delete the node */ + auto& nobj = _storage->nodes[n]; + nobj.data[1].h2 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + /* remove fanout count over output pins */ + for ( uint32_t i = 2; i < _storage->nodes[n].data.size(); ++i ) + { + nobj.data[i].h2 = 0; + } + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + /* if the node has been deleted, then deref fanout_size of + fanins and try to take them out if their fanout_size become 0 */ + for ( auto i = 0; i < nobj.children.size(); ++i ) + { + auto& child = nobj.children[i]; + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + + decr_fanout_size_pin( nobj.children[i].index, signal{ child }.output ); + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + assert( !is_dead( n ) ); + return; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto idx = 2u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + substitute_node( old_node, new_signal ); + } + + inline bool is_dead( node const& n ) const + { + /* A dead node is simply a dangling node */ + return ( _storage->nodes[n].data[1].h2 >> 31 ) & 1; + } + + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t num_outputs( node const& n ) const + { + return static_cast( _storage->nodes[n].data.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[1].h2++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return _storage->nodes[n].data[2 + pin_index].h2++; + } + + uint32_t decr_fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return --_storage->nodes[n].data[2 + pin_index].h2; + } + + uint32_t fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return _storage->nodes[n].data[2 + pin_index].h2; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } + + bool is_and( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 4; + } + + bool is_and( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 4; + } + + bool is_or( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 6; + } + + bool is_or( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 6; + } + + bool is_xor( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 12; + } + + bool is_xor( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 12; + } + + bool is_maj( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 14; + } + + bool is_maj( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 14; + } + + bool is_ite( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 16; + } + + bool is_ite( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 16; + } + + bool is_xor3( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 18; + } + + bool is_xor3( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 18; + } + + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[2].h1]; + } + + kitty::dynamic_truth_table node_function_pin( const node& n, uint32_t pin_index ) const + { + return _storage->data.cache[_storage->nodes[n].data[2 + pin_index].h1]; + } + + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return { n, 0 }; + } + + signal make_signal( node const& n, uint32_t output_pin ) const + { + return { n, 0, output_pin }; + } + + bool is_complemented( signal const& f ) const + { + return f.complement ? true : false; + } + + uint32_t get_output_pin( signal const& f ) const + { + return static_cast( f.output ); + } + + signal next_output_pin( signal const& f ) const + { + return { f.index, f.complement, static_cast(f.output) + 1 }; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto f ) { return signal( f ); }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto f ) { return signal( f ); }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return signal( f ); }, fn ); + } + + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + auto it = _storage->nodes[n].children.begin(); + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? ( ~( it->weight ) & 1 ) : ( ( it->weight ) & 1 ); + ++it; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[2].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector::value_type> tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* adjust polarities */ + for ( auto j = 0u; j < nfanin; ++j ) + { + if ( _storage->nodes[n].children[j].weight & 1 ) + tts[j] = ~tts[j]; + } + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[2].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } + + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h1 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h1 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h1++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h1 ); + } + + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } + + auto& events() const + { + return *_events; + } + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/crossed.hpp b/third-party/mockturtle/include/mockturtle/networks/crossed.hpp new file mode 100644 index 00000000000..6f1feed4fb0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/crossed.hpp @@ -0,0 +1,835 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file crossed.hpp + \brief Implements networks with crossing cells + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "klut.hpp" + +namespace mockturtle +{ + +/* Version 1: annotate information of crossing fanout ordereing with different signals + * Share the same `klut_storage_data` as regular k-LUT network, + * but define a different `crossed_klut_storage_node` with a weight field in the pointer. + */ + +/*! \brief k-LUT node + * + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Function literal in truth table cache + * `data[1].h2`: Visited flags + */ +struct crossed_klut_storage_node : mixed_fanin_node<2, 1> +{ + bool operator==( crossed_klut_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; +using crossed_klut_storage = storage; + +class crossed_klut_network +{ +public: + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + static constexpr bool is_crossed_network_type = true; + + using base_type = crossed_klut_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = crossed_klut_storage_node::pointer_type; + + crossed_klut_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + crossed_klut_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + +protected: + static constexpr uint32_t literal_crossing = 0xffffffff; + + inline void _init() + { + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } + +public: + signal get_constant( bool value = false ) const + { + return value ? signal( 1, 0 ) : signal( 0, 0 ); + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + return signal( index, 0 ); + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool constant_value( node const& n ) const + { + return n == 1; + } + + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } + + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_nor( signal a, signal b ) + { + return _create_node( { a, b }, 7 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_ge( signal a, signal b ) + { + return _create_node( { a, b }, 9 ); + } + + signal create_gt( signal a, signal b ) + { + return _create_node( { a, b }, 10 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } + + signal create_xnor( signal a, signal b ) + { + return _create_node( { a, b }, 13 ); + } + + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } + + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } + + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[0].h1++; + } + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return signal( index, 0 ); + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( crossed_klut_network const& other, node const& source, std::vector const& children ) + { + assert( !other.is_crossing( source ) ); + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } + + signal clone_node( klut_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } + + /*! \brief Create a crossing cell + * + * \return A pair `(out1, out2)` of two signals to be used as fanouts of the crossing, + * where `in1` connects to `out1` and `in2` connects to `out2`. + */ + std::pair create_crossing( signal const& in1, signal const& in2 ) + { + storage::element_type::node_type node; + node.children.emplace_back( in1 ); + node.children.emplace_back( in2 ); + node.data[1].h1 = literal_crossing; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + _storage->nodes[in1.index].data[0].h1++; + _storage->nodes[in2.index].data[0].h1++; + + /* TODO: not sure if this is wanted/needed? */ + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return std::make_pair( signal( index, 0 ), signal( index, 1 ) ); + } + + /*! \brief Insert a crossing cell on two wires + * + * After this operation, the network will not be in a topological order. + * + * \param in1 A fanin signal of the node `out1` + * \param in2 A fanin signal of the node `out2` + * \return The created crossing cell. No more fanouts should be added to it. + */ + node insert_crossing( signal const& in1, signal const& in2, node const& out1, node const& out2 ) + { + uint32_t fanin_index1 = std::numeric_limits::max(); + foreach_fanin( out1, [&]( auto const& f, auto i ) { + if ( f == in1 ) + { + fanin_index1 = i; + return false; + } + return true; + } ); + assert( fanin_index1 != std::numeric_limits::max() ); + + uint32_t fanin_index2 = std::numeric_limits::max(); + foreach_fanin( out2, [&]( auto const& f, auto i ) { + if ( f == in2 ) + { + fanin_index2 = i; + return false; + } + return true; + } ); + assert( fanin_index2 != std::numeric_limits::max() ); + + auto [fout1, fout2] = create_crossing( in1, in2 ); + _storage->nodes[out1].children[fanin_index1] = fout1; + _storage->nodes[out2].children[fanin_index2] = fout2; + + /* decrease ref-count to children (was increased in `create_crossing`) */ + _storage->nodes[in1.index].data[0].h1--; + _storage->nodes[in2.index].data[0].h1--; + + return get_node( fout1 ); + } + + /*! \brief Whether a node is a crossing cell + * + * \param n The node to be checked + * \return Whether this node is a crossing cell + */ + bool is_crossing( node const& n ) const + { + return _storage->nodes[n].data[1].h1 == literal_crossing; + } + + /*! \brief Whether a crossing's fanout signal is the second one + * + * \param f A signal pointing to a crossing cell + * \return Whether this fanout connects to the second fanin (return 1) or the first fanin (return 0) + */ + bool is_second( signal const& f ) const + { + assert( is_crossing( f.index ) ); + return f.weight; + } + + /*! \brief Take a crossing's first fanout signal and make it the second + * + * \param f A signal pointing to a crossing cell, referring to the fanout connecting to the first fanin + * \return The signal referring to the fanout connecting to the second fanin + */ + signal make_second( signal const& f ) const + { + assert( is_crossing( f.index ) ); + assert( !is_second( f ) ); + return signal( f.index, 1 ); + } + + /*! \brief Get the real fanin signal ignoring all crossings in between + * + * \param f A signal pointing to a crossing + * \return The corresponding signal pointing to a non-crossing node + */ + signal ignore_crossings( signal const& f ) const + { + if ( !is_crossing( get_node( f ) ) ) + return f; + return ignore_crossings( _storage->nodes[f.index].children[f.weight] ); + } + + /*! \brief Iterate through the real fanins of a node, ignoring all crossings in between */ + template + void foreach_fanin_ignore_crossings( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->nodes[n].children.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), [this]( auto f ) { return ignore_crossings( f ); }, fn ); + } + + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ) && !is_crossing( n ); + } + + kitty::dynamic_truth_table node_function( const node& n ) const + { + assert( !is_crossing( n ) ); + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } + + bool is_not( node const& n ) const + { + if ( !is_function( n ) ) + return false; + return _storage->nodes[n].children.size() == 1 && _storage->nodes[n].data[1].h1 == 3; + } + + /* AND-2 with any input negation, but not output negation */ + bool is_and( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 4 || node.data[1].h1 == 8 || node.data[1].h1 == 10 || node.data[1].h1 == 7; + } + + /* OR-2 with any input negation, but not output negation */ + bool is_or( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 5 || node.data[1].h1 == 9 || node.data[1].h1 == 11 || node.data[1].h1 == 6; + } + + /* XOR-2 or XNOR-2 */ + bool is_xor( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 12 || node.data[1].h1 == 13; + } + + /* XOR-3 or XNOR-3 */ + bool is_xor3( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 18 || node.data[1].h1 == 19; + } + + /* MAJ-3 or MINORITY-3 without non-symmetric input negation */ + bool is_maj( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 14 || node.data[1].h1 == 15; + } + + /* ITE (MUX2-1) without input negation; with or without output negation + * i.e., (x ? y :z) or !(x ? y : z) = x ? !y : !z */ + bool is_ite( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 16 || node.data[1].h1 == 17; + } + + std::vector get_fanin_negations( node const& n ) const + { + if ( is_crossing( n ) ) + return {false, false}; + if ( !is_function( n ) ) + return {}; + switch ( _storage->nodes[n].data[1].h1 ) + { + case 2: return {false}; // buf + case 3: return {true}; // not + case 4: return {false, false}; // and + case 5: return {true, true}; // x nand y = !x or !y + case 6: return {false, false}; // or + case 7: return {true, true}; // x nor y = !x and !y + case 8: return {true, false}; // !x and y + case 9: return {false, true}; // x or !y + case 10: return {false, true}; // x and !y + case 11: return {true, false}; // !x or y + case 12: return {false, false}; // xor + case 13: return {true, false}; // xnor (symmetric) + case 14: return {false, false, false}; // maj + case 15: return {true, true, true}; // minority + case 16: return {false, false, false}; // ite + case 17: return {false, true, true}; // !(x ? y : z) = x ? !y : !z + case 18: return {false, false, false}; // xor3 + case 19: return {true, false, false}; // xnor3 (symmetric) + } + return {}; + } + + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + /* Note: crossings are included */ + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + detail::foreach_element( _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), fn ); + } + + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( !is_crossing( n ) ); + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( !is_crossing( n ) ); + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } + + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } + + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } + + auto& events() const + { + return *_events; + } + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/detail/foreach.hpp b/third-party/mockturtle/include/mockturtle/networks/detail/foreach.hpp new file mode 100644 index 00000000000..7aadd5bbf2d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/detail/foreach.hpp @@ -0,0 +1,224 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file foreach.hpp + \brief For each functor utilities + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle::detail +{ + +template +inline constexpr bool is_callable_with_index_v = std::is_invocable_r_v; + +template +inline constexpr bool is_callable_without_index_v = std::is_invocable_r_v; + +template +Iterator foreach_element( Iterator begin, Iterator end, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !fn( *begin++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !fn( *begin++, index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + fn( *begin++ ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + fn( *begin++, index++ ); + } + return begin; + } +} + +template +Iterator foreach_element_if( Iterator begin, Iterator end, Pred&& pred, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + if ( !fn( *begin++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + if ( !fn( *begin++, index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + fn( *begin++ ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + fn( *begin++, index++ ); + } + return begin; + } +} + +template +Iterator foreach_element_transform( Iterator begin, Iterator end, Transform&& transform, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !fn( transform( *begin++ ) ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !fn( transform( *begin++ ), index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + fn( transform( *begin++ ) ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + fn( transform( *begin++ ), index++ ); + } + return begin; + } +} + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/events.hpp b/third-party/mockturtle/include/mockturtle/networks/events.hpp new file mode 100644 index 00000000000..25794d1f813 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/events.hpp @@ -0,0 +1,135 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file events.hpp + \brief Event API for updating a logic network. + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Network events. + * + * This data structure can be returned by a network. Clients can add functions + * to network events to call code whenever an event occurs. Events are adding + * a node, modifying a node, and deleting a node. + */ +template +class network_events +{ +public: + using add_event_type = std::function const& n )>; + using modified_event_type = std::function const& n, std::vector> const& previous_children )>; + using delete_event_type = std::function const& n )>; + +public: + std::shared_ptr register_add_event( add_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_add.emplace_back( pfn ); + return pfn; + } + + std::shared_ptr register_modified_event( modified_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_modified.emplace_back( pfn ); + return pfn; + } + + std::shared_ptr register_delete_event( delete_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_delete.emplace_back( pfn ); + return pfn; + } + + void release_add_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_add.erase( std::remove_if( std::begin( on_add ), std::end( on_add ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_add ) ); + } + + void release_modified_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_modified.erase( std::remove_if( std::begin( on_modified ), std::end( on_modified ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_modified ) ); + } + + void release_delete_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_delete.erase( std::remove_if( std::begin( on_delete ), std::end( on_delete ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_delete ) ); + } + +public: + /*! \brief Event when node `n` is added. */ + std::vector> on_add; + + /*! \brief Event when `n` is modified. + * + * The event also informs about the previous children. Note that the new + * children are already available at the time the event is triggered. + */ + std::vector> on_modified; + + /*! \brief Event when `n` is deleted. */ + std::vector> on_delete; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/networks/klut.hpp b/third-party/mockturtle/include/mockturtle/networks/klut.hpp new file mode 100644 index 00000000000..60b8d3dc8e7 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/klut.hpp @@ -0,0 +1,675 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file klut.hpp + \brief k-LUT logic network implementation + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct klut_storage_data +{ + truth_table_cache cache; +}; + +/*! \brief k-LUT node + * + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Function literal in truth table cache + * `data[1].h2`: Visited flags + */ +struct klut_storage_node : mixed_fanin_node<2> +{ + bool operator==( klut_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; + +/*! \brief k-LUT storage container + + ... +*/ +using klut_storage = storage; + +class klut_network +{ +public: + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + using base_type = klut_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = uint64_t; + + klut_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + klut_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + + klut_network clone() const + { + return { std::make_shared( *_storage ) }; + } + +protected: + inline void _init() + { + /* already initialized */ + if ( _storage->nodes.size() > 1 ) + return; + + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } + +public: + signal get_constant( bool value = false ) const + { + return value ? 1 : 0; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + return index; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool constant_value( node const& n ) const + { + return n == 1; + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } + + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } + + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } + + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } + + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c].data[0].h1++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return index; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( klut_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto i = 0u; i < _storage->nodes.size(); ++i ) + { + auto& n = _storage->nodes[i]; + for ( auto& child : n.children ) + { + if ( child == old_node ) + { + std::vector old_children( n.children.size() ); + std::transform( n.children.begin(), n.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( i, old_children ); + } + } + } + } + + /* check outputs */ + for ( auto& output : _storage->outputs ) + { + if ( output == old_node ) + { + output = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + } + } + + // reset fan-out of old node + _storage->nodes[old_node].data[0].h1 = 0; + } + + inline bool is_dead( node const& n ) const + { + return false; + } + + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++; + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } + + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } + + node get_node( signal const& f ) const + { + return f; + } + + signal make_signal( node const& n ) const + { + return n; + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return f.index; }, fn ); + } + + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector::value_type> tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } + + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } + + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } + + auto& events() const + { + return *_events; + } + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/networks/storage.hpp b/third-party/mockturtle/include/mockturtle/networks/storage.hpp new file mode 100644 index 00000000000..7bdb8c25063 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/networks/storage.hpp @@ -0,0 +1,259 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file storage.hpp + \brief Configurable storage container + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Bruno Schmitt + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" + +namespace mockturtle +{ + +template +struct node_pointer +{ +private: + static constexpr auto _len = sizeof( uint64_t ) * 8; + +public: + node_pointer() = default; + node_pointer( uint64_t index, uint64_t weight ) : weight( weight ), index( index ) {} + node_pointer( uint64_t data ) : data( data ) {} + + union + { + struct + { + uint64_t weight : PointerFieldSize; + uint64_t index : _len - PointerFieldSize; + }; + uint64_t data; + }; + + bool operator==( node_pointer const& other ) const + { + return data == other.data; + } + + bool operator!=( node_pointer const& other ) const + { + return data != other.data; + } +}; + +template<> +struct node_pointer<0> +{ +public: + node_pointer() = default; + node_pointer( uint64_t index ) : index( index ) {} + + union + { + uint64_t index; + uint64_t data; + }; + + bool operator==( node_pointer<0> const& other ) const + { + return data == other.data; + } +}; + +union cauint64_t +{ + uint64_t n{ 0 }; + struct + { + uint64_t h1 : 32; + uint64_t h2 : 32; + }; + struct + { + uint64_t q1 : 16; + uint64_t q2 : 16; + uint64_t q3 : 16; + uint64_t q4 : 16; + }; +}; + +template +struct regular_node +{ + using pointer_type = node_pointer; + + std::array children; + std::array data; + + bool operator==( regular_node const& other ) const + { + return children == other.children; + } +}; + +template +struct mixed_fanin_node +{ + using pointer_type = node_pointer; + + std::vector children; + std::array data; + + bool operator==( mixed_fanin_node const& other ) const + { + return children == other.children; + } +}; + +template +struct block_fanin_node +{ + using pointer_type = node_pointer; + + std::vector children; + std::vector data; + + bool operator==( block_fanin_node const& other ) const + { + return children == other.children; + } +}; + +/*! \brief Hash function for 64-bit word */ +inline uint64_t hash_block( uint64_t word ) +{ + /* from boost::hash_detail::hash_value_unsigned */ + return word ^ ( word + ( word << 6 ) + ( word >> 2 ) ); +} + +/*! \brief Combines two hash values */ +inline void hash_combine( uint64_t& seed, uint64_t other ) +{ + /* from boost::hash_detail::hash_combine_impl */ + const uint64_t m = UINT64_C( 0xc6a4a7935bd1e995 ); + const int r = 47; + + other *= m; + other ^= other >> r; + other *= m; + + seed ^= other; + seed *= m; + + seed += 0xe6546b64; +} + +template +struct node_hash +{ + uint64_t operator()( const Node& n ) const + { + if ( n.children.size() == 0 ) + return 0; + + auto it = std::begin( n.children ); + auto seed = hash_block( it->data ); + ++it; + + while ( it != std::end( n.children ) ) + { + hash_combine( seed, hash_block( it->data ) ); + ++it; + } + + return seed; + } +}; + +struct empty_storage_data +{ +}; + +template> +struct storage +{ + storage() + { + nodes.reserve( 10000u ); + hash.reserve( 10000u ); + + /* we generally reserve the first node for a constant */ + nodes.emplace_back(); + } + + using node_type = Node; + + uint32_t trav_id = 0u; + + std::vector nodes; + std::vector inputs; + std::vector outputs; + + absl::flat_hash_map hash; + + T data; +}; + +template +struct storage_no_hash +{ + storage_no_hash() + { + nodes.reserve( 10000u ); + + /* we generally reserve the first node for a constant */ + nodes.emplace_back(); + } + + using node_type = Node; + + uint32_t trav_id = 0u; + + std::vector nodes; + std::vector inputs; + std::vector outputs; + + T data; +}; + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/traits.hpp b/third-party/mockturtle/include/mockturtle/traits.hpp new file mode 100644 index 00000000000..bada5275f15 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/traits.hpp @@ -0,0 +1,2330 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file traits.hpp + \brief Type traits and checkers for the network interface + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +template +using signal = typename Ntk::signal; + +template +using node = typename Ntk::node; + +template +struct is_network_type : std::false_type +{ +}; + +template +struct is_network_type, node>, + std::void_t, + node, + typename Ntk::storage, + decltype( Ntk::max_fanin_size ), + decltype( Ntk::min_fanin_size )>>> : std::true_type +{ +}; + +template +inline constexpr bool is_network_type_v = is_network_type::value; + +template +struct is_aig_network_type : std::false_type +{ +}; + +template +struct is_aig_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_aig_network_type_v = is_aig_network_type::value; + +template +struct is_buffered_network_type : std::false_type +{ +}; + +template +struct is_buffered_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_buffered_network_type_v = is_buffered_network_type::value; + +template +struct is_crossed_network_type : std::false_type +{ +}; + +template +struct is_crossed_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_crossed_network_type_v = is_crossed_network_type::value; + +template +struct has_clone : std::false_type +{ +}; + +template +struct has_clone().clone() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clone_v = has_clone::value; + +template +struct is_topologically_sorted : std::false_type +{ +}; + +template +struct is_topologically_sorted>> : std::true_type +{ +}; + +template +inline constexpr bool is_topologically_sorted_v = is_topologically_sorted::value; + +template +struct has_get_constant : std::false_type +{ +}; + +template +struct has_get_constant().get_constant( bool() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_constant_v = has_get_constant::value; + +template +struct has_create_pi : std::false_type +{ +}; + +template +struct has_create_pi().create_pi() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_pi_v = has_create_pi::value; + +template +struct has_create_po : std::false_type +{ +}; + +template +struct has_create_po().create_po( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_po_v = has_create_po::value; + +template +struct has_create_ro : std::false_type +{ +}; + +template +struct has_create_ro().create_ro() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ro_v = has_create_ro::value; + +template +struct has_create_ri : std::false_type +{ +}; + +template +struct has_create_ri().create_ri( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ri_v = has_create_ri::value; + +template +struct has_is_combinational : std::false_type +{ +}; + +template +struct has_is_combinational().is_combinational() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_combinational_v = has_is_combinational::value; + +template +struct has_is_constant : std::false_type +{ +}; + +template +struct has_is_constant().is_constant( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_constant_v = has_is_constant::value; + +template +struct has_is_ci : std::false_type +{ +}; + +template +struct has_is_ci().is_ci( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ci_v = has_is_ci::value; + +template +struct has_is_pi : std::false_type +{ +}; + +template +struct has_is_pi().is_pi( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_pi_v = has_is_pi::value; + +template +struct has_is_ro : std::false_type +{ +}; + +template +struct has_is_ro().is_ro( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ro_v = has_is_ro::value; + +template +struct has_is_multioutput : std::false_type +{ +}; + +template +struct has_is_multioutput().is_multioutput( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_multioutput_v = has_is_multioutput::value; + +template +struct has_constant_value : std::false_type +{ +}; + +template +struct has_constant_value().constant_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_constant_value_v = has_constant_value::value; + +template +struct has_create_buf : std::false_type +{ +}; + +template +struct has_create_buf().create_buf( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_buf_v = has_create_buf::value; + +template +struct has_create_not : std::false_type +{ +}; + +template +struct has_create_not().create_not( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_not_v = has_create_not::value; + +template +struct has_create_and : std::false_type +{ +}; + +template +struct has_create_and().create_and( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_and_v = has_create_and::value; + +template +struct has_create_nand : std::false_type +{ +}; + +template +struct has_create_nand().create_nand( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nand_v = has_create_nand::value; + +template +struct has_create_or : std::false_type +{ +}; + +template +struct has_create_or().create_or( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_or_v = has_create_or::value; + +template +struct has_create_nor : std::false_type +{ +}; + +template +struct has_create_nor().create_nor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nor_v = has_create_nor::value; + +template +struct has_create_lt : std::false_type +{ +}; + +template +struct has_create_lt().create_lt( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_lt_v = has_create_lt::value; + +template +struct has_create_le : std::false_type +{ +}; + +template +struct has_create_le().create_le( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_le_v = has_create_le::value; + +template +struct has_create_gt : std::false_type +{ +}; + +template +struct has_create_gt().create_gt( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_gt_v = has_create_gt::value; + +template +struct has_create_ge : std::false_type +{ +}; + +template +struct has_create_ge().create_ge( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ge_v = has_create_ge::value; + +template +struct has_create_xor : std::false_type +{ +}; + +template +struct has_create_xor().create_xor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xor_v = has_create_xor::value; + +template +struct has_create_xnor : std::false_type +{ +}; + +template +struct has_create_xnor().create_xnor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xnor_v = has_create_xnor::value; + +template +struct has_create_maj : std::false_type +{ +}; + +template +struct has_create_maj().create_maj( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_maj_v = has_create_maj::value; + +template +struct has_create_maj_odd : std::false_type +{ +}; + +template +struct has_create_maj_odd().create_maj( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_maj_odd_v = has_create_maj_odd::value; + +template +struct has_create_ite : std::false_type +{ +}; + +template +struct has_create_ite().create_ite( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ite_v = has_create_ite::value; + +template +struct has_create_xor3 : std::false_type +{ +}; + +template +struct has_create_xor3().create_xor3( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xor3_v = has_create_xor3::value; + +template +struct has_create_nary_and : std::false_type +{ +}; + +template +struct has_create_nary_and().create_nary_and( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_and_v = has_create_nary_and::value; + +template +struct has_create_nary_or : std::false_type +{ +}; + +template +struct has_create_nary_or().create_nary_or( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_or_v = has_create_nary_or::value; + +template +struct has_create_nary_xor : std::false_type +{ +}; + +template +struct has_create_nary_xor().create_nary_xor( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_xor_v = has_create_nary_xor::value; + +template +struct has_create_node : std::false_type +{ +}; + +template +struct has_create_node().create_node( std::declval>>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_node_v = has_create_node::value; + +template +struct has_create_cover_node : std::false_type +{ +}; + +template +struct has_create_cover_node().create_cover_node( std::declval>>(), std::declval, bool>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_cover_node_v = has_create_cover_node::value; + +template +struct has_create_crossing : std::false_type +{ +}; + +template +struct has_create_crossing().create_crossing( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_crossing_v = has_create_crossing::value; + +template +struct has_insert_crossing : std::false_type +{ +}; + +template +struct has_insert_crossing().insert_crossing( std::declval>(), std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_insert_crossing_v = has_insert_crossing::value; + +template +struct has_merge_into_crossing : std::false_type +{ +}; + +template +struct has_merge_into_crossing().merge_into_crossing( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_merge_into_crossing_v = has_merge_into_crossing::value; + +template +struct has_clone_node : std::false_type +{ +}; + +template +struct has_clone_node().clone_node( std::declval(), std::declval>(), std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clone_node_v = has_clone_node::value; + +template +struct has_has_and : std::false_type +{ +}; + +template +struct has_has_and().has_and( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_and_v = has_has_and::value; + +template +struct has_has_xor : std::false_type +{ +}; + +template +struct has_has_xor().has_xor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_xor_v = has_has_xor::value; + +template +struct has_has_maj : std::false_type +{ +}; + +template +struct has_has_maj().has_maj( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_maj_v = has_has_maj::value; + +template +struct has_has_xor3 : std::false_type +{ +}; + +template +struct has_has_xor3().has_xor3( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_xor3_v = has_has_xor3::value; + +template +struct has_substitute_node : std::false_type +{ +}; + +template +struct has_substitute_node().substitute_node( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_substitute_node_v = has_substitute_node::value; + +template +struct has_substitute_nodes : std::false_type +{ +}; + +template +struct has_substitute_nodes().substitute_nodes( std::declval, signal>>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_substitute_nodes_v = has_substitute_nodes::value; + +template +struct has_replace_in_node : std::false_type +{ +}; + +template +struct has_replace_in_node().replace_in_node( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_replace_in_node_v = has_replace_in_node::value; + +template +struct has_replace_in_outputs : std::false_type +{ +}; + +template +struct has_replace_in_outputs().replace_in_outputs( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_replace_in_outputs_v = has_replace_in_outputs::value; + +template +struct has_take_out_node : std::false_type +{ +}; + +template +struct has_take_out_node().take_out_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_take_out_node_v = has_take_out_node::value; + +template +struct has_is_dead : std::false_type +{ +}; + +template +struct has_is_dead().is_dead( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_dead_v = has_is_dead::value; + +template +struct has_size : std::false_type +{ +}; + +template +struct has_size().size() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_size_v = has_size::value; + +template +struct has_num_cis : std::false_type +{ +}; + +template +struct has_num_cis().num_cis() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cis_v = has_num_cis::value; + +template +struct has_num_cos : std::false_type +{ +}; + +template +struct has_num_cos().num_cos() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cos_v = has_num_cos::value; + +template +struct has_num_pis : std::false_type +{ +}; + +template +struct has_num_pis().num_pis() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_pis_v = has_num_pis::value; + +template +struct has_num_pos : std::false_type +{ +}; + +template +struct has_num_pos().num_pos() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_pos_v = has_num_pos::value; + +template +struct has_num_gates : std::false_type +{ +}; + +template +struct has_num_gates().num_gates() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_gates_v = has_num_gates::value; + +template +struct has_num_registers : std::false_type +{ +}; + +template +struct has_num_registers().num_registers() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_registers_v = has_num_registers::value; + +template +struct has_fanin_size : std::false_type +{ +}; + +template +struct has_fanin_size().fanin_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_fanin_size_v = has_fanin_size::value; + +template +struct has_num_outputs : std::false_type +{ +}; + +template +struct has_num_outputs().num_outputs( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_outputs_v = has_num_outputs::value; + +template +struct has_fanout_size : std::false_type +{ +}; + +template +struct has_fanout_size().fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_fanout_size_v = has_fanout_size::value; + +template +struct has_incr_fanout_size : std::false_type +{ +}; + +template +struct has_incr_fanout_size().incr_fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_fanout_size_v = has_incr_fanout_size::value; + +template +struct has_decr_fanout_size : std::false_type +{ +}; + +template +struct has_decr_fanout_size().decr_fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_decr_fanout_size_v = has_decr_fanout_size::value; + +template +struct has_cost : std::false_type +{ +}; + +template +struct has_cost().get_cost() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_cost_v = has_cost::value; + +template +struct has_depth : std::false_type +{ +}; + +template +struct has_depth().depth() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_depth_v = has_depth::value; + +template +struct has_level : std::false_type +{ +}; + +template +struct has_level().level( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_level_v = has_level::value; + +template +struct has_update_levels : std::false_type +{ +}; + +template +struct has_update_levels().update_levels() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_levels_v = has_update_levels::value; + +template +struct has_rank_position : std::false_type +{ +}; + +template +struct has_rank_position().rank_position( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_rank_position_v = has_rank_position::value; + +template +struct has_at_rank_position : std::false_type +{ +}; + +template +struct has_at_rank_position().at_rank_position( std::declval(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_at_rank_position_v = has_at_rank_position::value; + +template +struct has_width : std::false_type +{ +}; + +template +struct has_width().width() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_width_v = has_width::value; + +template +struct has_swap : std::false_type +{ +}; + +template +struct has_swap().swap( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_swap_v = has_swap::value; + +template +struct has_sort_rank : std::false_type +{ +}; + +template +struct has_sort_rank().sort_rank( std::declval(), std::declval, node )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_sort_rank_v = has_sort_rank::value; + +template +struct has_foreach_node_in_rank : std::false_type +{ +}; + +template +struct has_foreach_node_in_rank().foreach_node_in_rank( std::declval(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_node_in_rank_v = has_foreach_node_in_rank::value; + +template +struct has_foreach_gate_in_rank : std::false_type +{ +}; + +template +struct has_foreach_gate_in_rank().foreach_gate_in_rank( std::declval(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_gate_in_rank_v = has_foreach_gate_in_rank::value; + +template +struct has_update_mffcs : std::false_type +{ +}; + +template +struct has_update_mffcs().update_mffcs() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_mffcs_v = has_update_mffcs::value; + +template +struct has_update_topo : std::false_type +{ +}; + +template +struct has_update_topo().update_topo() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_topo_v = has_update_topo::value; + +template +struct has_update_fanout : std::false_type +{ +}; + +template +struct has_update_fanout().update_fanout() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_fanout_v = has_update_fanout::value; + +template +struct has_is_on_critical_path : std::false_type +{ +}; + +template +struct has_is_on_critical_path().is_on_critical_path( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_on_critical_path_v = has_is_on_critical_path::value; + +template +struct has_is_buf : std::false_type +{ +}; + +template +struct has_is_buf().is_buf( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_buf_v = has_is_buf::value; + +template +struct has_is_not : std::false_type +{ +}; + +template +struct has_is_not().is_not( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_not_v = has_is_not::value; + +template +struct has_is_crossing : std::false_type +{ +}; + +template +struct has_is_crossing().is_crossing( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_crossing_v = has_is_crossing::value; + +template +struct has_is_and : std::false_type +{ +}; + +template +struct has_is_and().is_and( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_and_v = has_is_and::value; + +template +struct has_is_or : std::false_type +{ +}; + +template +struct has_is_or().is_or( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_or_v = has_is_or::value; + +template +struct has_is_xor : std::false_type +{ +}; + +template +struct has_is_xor().is_xor( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_xor_v = has_is_xor::value; + +template +struct has_is_maj : std::false_type +{ +}; + +template +struct has_is_maj().is_maj( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_maj_v = has_is_maj::value; + +template +struct has_is_ite : std::false_type +{ +}; + +template +struct has_is_ite().is_ite( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ite_v = has_is_ite::value; + +template +struct has_is_xor3 : std::false_type +{ +}; + +template +struct has_is_xor3().is_xor3( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_xor3_v = has_is_xor3::value; + +template +struct has_is_nary_and : std::false_type +{ +}; + +template +struct has_is_nary_and().is_nary_and( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_and_v = has_is_nary_and::value; + +template +struct has_is_nary_or : std::false_type +{ +}; + +template +struct has_is_nary_or().is_nary_or( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_or_v = has_is_nary_or::value; + +template +struct has_is_nary_xor : std::false_type +{ +}; + +template +struct has_is_nary_xor().is_nary_xor( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_xor_v = has_is_nary_xor::value; + +template +struct has_is_function : std::false_type +{ +}; + +template +struct has_is_function().is_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_function_v = has_is_function::value; + +template +struct has_node_function : std::false_type +{ +}; + +template +struct has_node_function().node_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_function_v = has_node_function::value; + +template +struct has_node_function_pin : std::false_type +{ +}; + +template +struct has_node_function_pin().node_function_pin( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_function_pin_v = has_node_function_pin::value; + +template +struct has_get_node : std::false_type +{ +}; + +template +struct has_get_node().get_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_node_v = has_get_node::value; + +template +struct has_make_signal : std::false_type +{ +}; + +template +struct has_make_signal().make_signal( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_make_signal_v = has_make_signal::value; + +template +struct has_get_output_pin : std::false_type +{ +}; + +template +struct has_get_output_pin().get_output_pin( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_output_pin_v = has_get_output_pin::value; + +template +struct has_is_complemented : std::false_type +{ +}; + +template +struct has_is_complemented().is_complemented( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_complemented_v = has_is_complemented::value; + +template +struct has_node_to_index : std::false_type +{ +}; + +template +struct has_node_to_index().node_to_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_to_index_v = has_node_to_index::value; + +template +struct has_index_to_node : std::false_type +{ +}; + +template +struct has_index_to_node().index_to_node( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_index_to_node_v = has_index_to_node::value; + +template +struct has_ci_at : std::false_type +{ +}; + +template +struct has_ci_at().ci_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ci_at_v = has_ci_at::value; + +template +struct has_co_at : std::false_type +{ +}; + +template +struct has_co_at().co_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_co_at_v = has_co_at::value; + +template +struct has_pi_at : std::false_type +{ +}; + +template +struct has_pi_at().pi_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_pi_at_v = has_pi_at::value; + +template +struct has_po_at : std::false_type +{ +}; + +template +struct has_po_at().po_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_po_at_v = has_po_at::value; + +template +struct has_ro_at : std::false_type +{ +}; + +template +struct has_ro_at().ro_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ro_at_v = has_ro_at::value; + +template +struct has_ri_at : std::false_type +{ +}; + +template +struct has_ri_at().ri_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ri_at_v = has_ri_at::value; + +template +struct ci_index : std::false_type +{ +}; + +template +struct ci_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ci_index_v = ci_index::value; + +template +struct co_index : std::false_type +{ +}; + +template +struct co_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool co_index_v = co_index::value; + +template +struct pi_index : std::false_type +{ +}; + +template +struct pi_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool pi_index_v = pi_index::value; + +template +struct po_index : std::false_type +{ +}; + +template +struct po_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool po_index_v = po_index::value; + +template +struct ro_index : std::false_type +{ +}; + +template +struct ro_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ro_index_v = ro_index::value; + +template +struct ri_index : std::false_type +{ +}; + +template +struct ri_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ri_index_v = ri_index::value; + +template +struct has_ro_to_ri : std::false_type +{ +}; + +template +struct has_ro_to_ri().ro_to_ri( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ro_to_ri_v = has_ro_to_ri::value; + +template +struct has_ri_to_ro : std::false_type +{ +}; + +template +struct has_ri_to_ro().ri_to_ro( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ri_to_ro_v = has_ri_to_ro::value; + +template +struct has_foreach_node : std::false_type +{ +}; + +template +struct has_foreach_node().foreach_node( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_node_v = has_foreach_node::value; + +template +struct has_foreach_ci : std::false_type +{ +}; + +template +struct has_foreach_ci().foreach_ci( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ci_v = has_foreach_ci::value; + +template +struct has_foreach_co : std::false_type +{ +}; + +template +struct has_foreach_co().foreach_co( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_co_v = has_foreach_co::value; + +template +struct has_foreach_pi : std::false_type +{ +}; + +template +struct has_foreach_pi().foreach_pi( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_pi_v = has_foreach_pi::value; + +template +struct has_foreach_po : std::false_type +{ +}; + +template +struct has_foreach_po().foreach_po( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_po_v = has_foreach_po::value; + +template +struct has_foreach_ro : std::false_type +{ +}; + +template +struct has_foreach_ro().foreach_ro( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ro_v = has_foreach_ro::value; + +template +struct has_foreach_ri : std::false_type +{ +}; + +template +struct has_foreach_ri().foreach_ri( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ri_v = has_foreach_ri::value; + +template +struct has_foreach_gate : std::false_type +{ +}; + +template +struct has_foreach_gate().foreach_gate( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_gate_v = has_foreach_gate::value; + +template +struct has_foreach_register : std::false_type +{ +}; + +template +struct has_foreach_register().foreach_register( std::declval, signal>, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_register_v = has_foreach_register::value; + +template +struct has_foreach_fanin : std::false_type +{ +}; + +template +struct has_foreach_fanin().foreach_fanin( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_fanin_v = has_foreach_fanin::value; + +template +struct has_foreach_fanout : std::false_type +{ +}; + +template +struct has_foreach_fanout().foreach_fanout( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_fanout_v = has_foreach_fanout::value; + +template +struct has_foreach_choice : std::false_type +{ +}; + +template +struct has_foreach_choice().foreach_choice( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_choice_v = has_foreach_choice::value; + +template +struct has_compute : std::false_type +{ +}; + +template +struct has_compute().compute( std::declval>(), std::begin( std::vector() ), std::end( std::vector() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_compute_v = has_compute::value; + +template +struct has_compute_inplace : std::false_type +{ +}; + +template +struct has_compute_inplace().compute( std::declval>(), std::declval(), std::begin( std::vector() ), std::end( std::vector() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_compute_inplace_v = has_compute_inplace::value; + +template +struct has_has_mapping : std::false_type +{ +}; + +template +struct has_has_mapping().has_mapping() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_mapping_v = has_has_mapping::value; + +template +struct has_is_cell_root : std::false_type +{ +}; + +template +struct has_is_cell_root().is_cell_root( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_cell_root_v = has_is_cell_root::value; + +template +struct has_clear_mapping : std::false_type +{ +}; + +template +struct has_clear_mapping().clear_mapping() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_mapping_v = has_clear_mapping::value; + +template +struct has_num_cells : std::false_type +{ +}; + +template +struct has_num_cells().num_cells() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cells_v = has_num_cells::value; + +template +struct has_add_to_mapping : std::false_type +{ +}; + +template +struct has_add_to_mapping().add_to_mapping( std::declval>(), std::begin( std::vector>() ), std::end( std::vector>() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_to_mapping_v = has_add_to_mapping::value; + +template +struct has_remove_from_mapping : std::false_type +{ +}; + +template +struct has_remove_from_mapping().remove_from_mapping( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_remove_from_mapping_v = has_remove_from_mapping::value; + +template +struct has_cell_function : std::false_type +{ +}; + +template +struct has_cell_function().cell_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_cell_function_v = has_cell_function::value; + +template +struct has_set_cell_function : std::false_type +{ +}; + +template +struct has_set_cell_function().set_cell_function( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_cell_function_v = has_set_cell_function::value; + +template +struct has_foreach_cell_fanin : std::false_type +{ +}; + +template +struct has_foreach_cell_fanin().foreach_cell_fanin( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_cell_fanin_v = has_foreach_cell_fanin::value; + +template +struct has_has_binding : std::false_type +{ +}; + +template +struct has_has_binding().has_binding( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_binding_v = has_has_binding::value; + +template +struct has_has_cell : std::false_type +{ +}; + +template +struct has_has_cell().has_cell( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_cell_v = has_has_cell::value; + +template +struct has_get_binding_index : std::false_type +{ +}; + +template +struct has_get_binding_index().get_binding_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_binding_index_v = has_get_binding_index::value; + +template +struct has_get_cell_index : std::false_type +{ +}; + +template +struct has_get_cell_index().get_cell_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_cell_index_v = has_get_cell_index::value; + +template +struct has_add_binding : std::false_type +{ +}; + +template +struct has_add_binding().add_binding( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_binding_v = has_add_binding::value; + +template +struct has_select_dont_touch : std::false_type +{ +}; + +template +struct has_select_dont_touch().select_dont_touch( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_select_dont_touch_v = has_select_dont_touch::value; + +template +struct has_is_dont_touch : std::false_type +{ +}; + +template +struct has_is_dont_touch().is_dont_touch( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_dont_touch_v = has_is_dont_touch::value; + +template +struct has_clear_values : std::false_type +{ +}; + +template +struct has_clear_values().clear_values() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_values_v = has_clear_values::value; + +template +struct has_value : std::false_type +{ +}; + +template +struct has_value().value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_value_v = has_value::value; + +template +struct has_set_value : std::false_type +{ +}; + +template +struct has_set_value().set_value( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_value_v = has_set_value::value; + +template +struct has_incr_value : std::false_type +{ +}; + +template +struct has_incr_value().incr_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_value_v = has_incr_value::value; + +template +struct has_decr_value : std::false_type +{ +}; + +template +struct has_decr_value().decr_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_decr_value_v = has_decr_value::value; + +template +struct has_get_fanin0 : std::false_type +{ +}; + +template +struct has_get_fanin0().get_fanin0( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_fanin0_v = has_get_fanin0::value; + +template +struct has_clear_visited : std::false_type +{ +}; + +template +struct has_clear_visited().clear_visited() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_visited_v = has_clear_visited::value; + +template +struct has_visited : std::false_type +{ +}; + +template +struct has_visited().visited( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_visited_v = has_visited::value; + +template +struct has_set_visited : std::false_type +{ +}; + +template +struct has_set_visited().set_visited( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_visited_v = has_set_visited::value; + +template +struct has_trav_id : std::false_type +{ +}; + +template +struct has_trav_id().trav_id() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_trav_id_v = has_trav_id::value; + +template +struct has_incr_trav_id : std::false_type +{ +}; + +template +struct has_incr_trav_id().incr_trav_id() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_trav_id_v = has_incr_trav_id::value; + +template +struct has_get_network_name : std::false_type +{ +}; + +template +struct has_get_network_name().get_network_name() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_network_name_v = has_get_network_name::value; + +template +struct has_set_network_name : std::false_type +{ +}; + +template +struct has_set_network_name().set_network_name( std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_network_name_v = has_set_network_name::value; + +template +struct has_get_name : std::false_type +{ +}; + +template +struct has_get_name().get_name( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_name_v = has_get_name::value; + +template +struct has_set_name : std::false_type +{ +}; + +template +struct has_set_name().set_name( std::declval>(), std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_name_v = has_set_name::value; + +template +struct has_has_name : std::false_type +{ +}; + +template +struct has_has_name().has_name( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_name_v = has_has_name::value; + +template +struct has_get_output_name : std::false_type +{ +}; + +template +struct has_get_output_name().get_output_name( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_output_name_v = has_get_output_name::value; + +template +struct has_set_output_name : std::false_type +{ +}; + +template +struct has_set_output_name().set_output_name( uint32_t(), std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_output_name_v = has_set_output_name::value; + +template +struct has_has_output_name : std::false_type +{ +}; + +template +struct has_has_output_name().has_output_name( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_output_name_v = has_has_output_name::value; + +template +struct has_new_color : std::false_type +{ +}; + +template +struct has_new_color().new_color() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_new_color_v = has_new_color::value; + +template +struct has_current_color : std::false_type +{ +}; + +template +struct has_current_color().current_color() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_current_color_v = has_current_color::value; + +template +struct has_clear_colors : std::false_type +{ +}; + +template +struct has_clear_colors().clear_colors( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_colors_v = has_clear_colors::value; + +template +struct has_color : std::false_type +{ +}; + +template +struct has_color().color( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_color_v = has_color::value; + +template +struct has_paint : std::false_type +{ +}; + +template +struct has_paint().paint( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_paint_v = has_paint::value; + +template +struct has_eval_color : std::false_type +{ +}; + +template +struct has_eval_color().eval_color( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_eval_color_v = has_eval_color::value; + +template +struct has_eval_fanins_color : std::false_type +{ +}; + +template +struct has_eval_fanins_color().eval_fanins_color( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_eval_fanins_color_v = has_eval_fanins_color::value; + +template +struct has_EXCDC_interface : std::false_type +{ +}; + +template +struct has_EXCDC_interface>> : std::true_type +{ +}; + +template +inline constexpr bool has_EXCDC_interface_v = has_EXCDC_interface::value; + +template +struct has_EXODC_interface : std::false_type +{ +}; + +template +struct has_EXODC_interface>> : std::true_type +{ +}; + +template +inline constexpr bool has_EXODC_interface_v = has_EXODC_interface::value; + +/*! \brief SFINAE based on iterator type (for compute functions). + */ +template +using iterates_over_t = std::enable_if_t::value_type, T>, T>; + +/*! \brief SFINAE based on iterator type for truth tables (for compute functions). + */ +template +using iterates_over_truth_table_t = std::enable_if_t::value_type>::value, typename std::iterator_traits::value_type>; + +template +inline constexpr bool iterates_over_v = std::is_same_v; + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/utils/algorithm.hpp b/third-party/mockturtle/include/mockturtle/utils/algorithm.hpp new file mode 100644 index 00000000000..6397fff25c6 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/algorithm.hpp @@ -0,0 +1,230 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file algorithm.hpp + \brief STL-like algorithm extensions + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken +*/ + +#pragma once + +#include + +namespace mockturtle +{ + +template +T tree_reduce( Iterator first, Iterator last, T const& init, BinaryOperation&& op ) +{ + const auto len = std::distance( first, last ); + + switch ( len ) + { + case 0u: + return init; + case 1u: + return *first; + case 2u: + return op( *first, *( first + 1 ) ); + default: + { + const auto m = len / 2; + return op( tree_reduce( first, first + m, init, op ), tree_reduce( first + m, last, init, op ) ); + } + break; + } +} + +template +T ternary_tree_reduce( Iterator first, Iterator last, T const& init, TernaryOperation&& op ) +{ + const auto len = std::distance( first, last ); + + switch ( len ) + { + case 0u: + return init; + case 1u: + return *first; + case 2u: + return op( init, *first, *( first + 1 ) ); + case 3u: + return op( *first, *( first + 1 ), *( first + 2 ) ); + default: + { + const auto m1 = len / 3; + const auto m2 = ( len - m1 ) / 2; + return op( ternary_tree_reduce( first, first + m1, init, op ), + ternary_tree_reduce( first + m1, first + m1 + m2, init, op ), + ternary_tree_reduce( first + m1 + m2, last, init, op ) ); + } + break; + } +} + +template +Iterator max_element_unary( Iterator first, Iterator last, UnaryOperation&& fn, T const& init ) +{ + auto best = last; + auto max = init; + for ( ; first != last; ++first ) + { + if ( const auto v = fn( *first ) > max ) + { + max = v; + best = first; + } + } + return best; +} + +template>> +constexpr auto range( T begin, T end ) +{ + struct iterator + { + using value_type = T; + + value_type curr_; + bool operator!=( iterator const& other ) const { return curr_ != other.curr_; } + iterator& operator++() + { + ++curr_; + return *this; + } + iterator operator++( int ) + { + auto copy = *this; + ++( *this ); + return copy; + } + value_type operator*() const { return curr_; } + }; + struct iterable_wrapper + { + T begin_; + T end_; + auto begin() { return iterator{ begin_ }; } + auto end() { return iterator{ end_ }; } + }; + return iterable_wrapper{ begin, end }; +} + +template>> +constexpr auto range( T end ) +{ + return range( {}, end ); +} + +/*! \brief Performs the set union of two sorted sets. + * + * Compared to std::set_union, limits the copy to `limit`. + * Moreover, it returns the number of elements copied if the + * union operation is successful. Else, it returns -1. + * + */ +template +int32_t set_union_safe( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, uint32_t limit ) +{ + /* special case: sets are at the limit */ + if ( std::distance( first1, last1 ) == limit && std::distance( first2, last2 ) == limit ) + { + while ( first1 != last1 ) + { + if ( *first1 != *first2 ) + return -1; + + *result = *first1; + ++first1; + ++first2; + ++result; + } + + return static_cast( limit ); + } + + uint32_t size = 0; + while ( size < limit ) + { + if ( first1 == last1 ) + { + size += std::distance( first2, last2 ); + if ( size <= limit ) + { + std::copy( first2, last2, result ); + return static_cast( size ); + } + else + { + return -1; + } + } + else if ( first2 == last2 ) + { + size += std::distance( first1, last1 ); + if ( size <= limit ) + { + std::copy( first1, last1, result ); + return static_cast( size ); + } + else + { + return -1; + } + } + + if ( *first1 < *first2 ) + { + *result = *first1; + ++first1; + } + else if ( *first2 < *first1 ) + { + *result = *first2; + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + } + + ++result; + ++size; + } + + if ( std::distance( first1, last1 ) + std::distance( first2, last2 ) > 0 ) + return -1; + + return static_cast( size ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/cost_functions.hpp b/third-party/mockturtle/include/mockturtle/utils/cost_functions.hpp new file mode 100644 index 00000000000..6b5315bc18a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/cost_functions.hpp @@ -0,0 +1,146 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cost_functions.hpp + \brief Various cost functions for (optimization) algorithms + + \author Heinz Riener + \author Mathias Soeken + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +template +struct unit_cost +{ + uint32_t operator()( Ntk const& ntk, node const& node ) const + { + (void)ntk; + (void)node; + return 1u; + } +}; + +template +struct mc_cost +{ + uint32_t operator()( Ntk const& ntk, node const& node ) const + { + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( node ) ) + { + return 0u; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( node ) ) + { + return 0u; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( node ) ) + { + if ( ntk.fanin_size( node ) > 1u ) + { + return ntk.fanin_size( node ) - 1u; + } + return 0u; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( node ) ) + { + if ( ntk.fanin_size( node ) > 1u ) + { + return ntk.fanin_size( node ) - 1u; + } + return 0u; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( node ) ) + { + return 0u; + } + } + + // TODO (Does not take into account general node functions) + return 1u; + } +}; + +struct lut_unitary_cost +{ + std::pair operator()( uint32_t num_leaves ) const + { + if ( num_leaves < 2u ) + return { 0u, 0u }; + return { 1u, 1u }; /* area, delay */ + } + + std::pair operator()( kitty::dynamic_truth_table const& tt ) const + { + if ( tt.num_vars() < 2u ) + return { 0u, 0u }; + return { 1u, 1u }; /* area, delay */ + } +}; + +template> +uint32_t costs( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + uint32_t total{ 0u }; + NodeCostFn cost_fn{}; + ntk.foreach_gate( [&]( auto const& n ) { + total += cost_fn( ntk, n ); + } ); + return total; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/cuts.hpp b/third-party/mockturtle/include/mockturtle/utils/cuts.hpp new file mode 100644 index 00000000000..5a9c4404fc0 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/cuts.hpp @@ -0,0 +1,591 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cuts.hpp + \brief Data structure for cuts + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "algorithm.hpp" + +namespace mockturtle +{ + +struct empty_cut_data +{ +}; + +/*! \brief A data-structure to hold a cut. + * + * The cut class is specialized via two template arguments, `MaxLeaves` and `T`. + * `MaxLeaves` controls the maximum number of leaves a cut can hold. To + * guarantee an efficient implementation, this value should be \f$k \cdot l\f$, + * where \f$k\f$ is the maximum cut size and \f$l\f$ is the maximum fanin size + * of a gate in the logic network. The second template argument `T` can be a + * type for which a data entry is created in the cut to store additional data, + * e.g., to compute the cost of a cut. It defaults to `empty_cut_data`, which + * is an empty struct that does not consume memory. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector data{1, 2, 3, 4}; + + cut<10> cut1; + cut1.set_leaves( data.begin(), data.end() ); + + cut<10, uint32_t> cut2; + cut2.set_leaves( data.begin(), data.end() ); + cut2.data() = 42u; + + struct cut_data { uint32_t costs; }; + cut<20, cut_data> c3; + c3.set_leaves( std::vector{1, 2, 3} ); + c3->costs = 37u; + \endverbatim + */ +template +class cut +{ +public: + /*! \brief Default constructor. + */ + cut() = default; + + /*! \brief Copy constructor. + * + * Copies leaves, length, signature, and data. + * + * \param other Other cut + */ + cut( cut const& other ) + { + _cend = _end = std::copy( other.begin(), other.end(), _leaves.begin() ); + _length = other._length; + _signature = other._signature; + _data = other._data; + } + + /*! \brief Assignment operator. + * + * Copies leaves, length, signature, and data. + * + * \param other Other cut + */ + cut& operator=( cut const& other ); + + /*! \brief Sets leaves (using iterators). + * + * \param begin Begin iterator to leaves + * \param end End iterator to leaves (exclusive) + */ + template + void set_leaves( Iterator begin, Iterator end ); + + /*! \brief Sets leaves (using container). + * + * Convenience function, which extracts the begin and end iterators from the + * container. + */ + template + void set_leaves( Container const& c ); + + /*! \brief Add leaves (using iterators). + * + * \param begin Begin iterator to leaves + * \param end End iterator to leaves (exclusive) + */ + template + void add_leaves( Iterator begin, Iterator end ); + + /*! \brief Signature of the cut. */ + auto signature() const { return _signature; } + + /*! \brief Returns the size of the cut (number of leaves). */ + auto size() const { return _length; } + + /*! \brief Begin iterator (constant). */ + auto begin() const { return _leaves.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _cend; } + + /*! \brief Begin iterator (mutable). */ + auto begin() { return _leaves.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _end; } + + /*! \brief Access to data (mutable). */ + T* operator->() { return &_data; } + + /*! \brief Access to data (constant). */ + T const* operator->() const { return &_data; } + + /*! \brief Access to data (mutable). */ + T& data() { return _data; } + + /*! \brief Access to data (constant). */ + T const& data() const { return _data; } + + /*! \brief Checks whether the cut is a subset of another cut. + * + * If \f$L_1\f$ are the leaves of the current cut and \f$L_2\f$ are the leaves + * of `that`, then this method returns true if and only if + * \f$L_1 \subseteq L_2\f$. + * + * \param that Other cut + */ + bool dominates( cut const& that ) const; + + /*! \brief Merges two cuts. + * + * This method merges two cuts and stores the result in `res`. The merge of + * two cuts is the union \f$L_1 \cup L_2\f$ of the two leaf sets \f$L_1\f$ of + * the cut and \f$L_2\f$ of `that`. The merge is only successful if the + * union has not more than `cut_size` elements. In that case, the function + * returns `false`, otherwise `true`. + * + * \param that Other cut + * \param res Resulting cut + * \param cut_size Maximum cut size + * \return True, if resulting cut is small enough + */ + bool merge( cut const& that, cut& res, uint32_t cut_size ) const; + +private: + std::array _leaves; + uint32_t _length; + uint64_t _signature; + typename std::array::const_iterator _cend; + typename std::array::iterator _end; + + T _data; +}; + +/*! \brief Compare two cuts. + * + * Default comparison function for two cuts. A cut is smaller than another + * cut, if it has fewer leaves. + * + * This function should be specialized for custom cuts, if additional data + * changes the cost of a cut. + */ +template +bool operator<( cut const& c1, cut const& c2 ) +{ + return c1.size() < c2.size(); +} + +/*! \brief Prints a cut. + */ +template +std::ostream& operator<<( std::ostream& os, cut const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}"; + return os; +} + +template +cut& cut::operator=( cut const& other ) +{ + if ( &other != this ) + { + _cend = _end = std::copy( other.begin(), other.end(), _leaves.begin() ); + _length = other._length; + _signature = other._signature; + _data = other._data; + } + return *this; +} + +template +template +void cut::set_leaves( Iterator begin, Iterator end ) +{ + _cend = _end = std::copy( begin, end, _leaves.begin() ); + _length = static_cast( std::distance( begin, end ) ); + _signature = 0; + + while ( begin != end ) + { + _signature |= UINT64_C( 1 ) << ( *begin++ & 0x3f ); + } +} + +template +template +void cut::set_leaves( Container const& c ) +{ + set_leaves( std::begin( c ), std::end( c ) ); +} + +template +template +void cut::add_leaves( Iterator begin, Iterator end ) +{ + _cend = _end = std::copy( begin, end, _end ); + _length = static_cast( std::distance( _leaves.begin(), _end ) ); + + while ( begin != end ) + { + _signature |= UINT64_C( 1 ) << ( *begin++ & 0x3f ); + } +} + +template +bool cut::dominates( cut const& that ) const +{ + /* quick check for counter example */ + if ( _length > that._length || ( _signature & that._signature ) != _signature ) + { + return false; + } + + if ( _length == that._length ) + { + return std::equal( begin(), end(), that.begin() ); + } + + if ( _length == 0 ) + { + return true; + } + + // this is basically + // return std::includes( that.begin(), that.end(), begin(), end() ) + // but it turns out that this code is faster compared to the standard + // implementation. + for ( auto it2 = that.begin(), it1 = begin(); it2 != that.end(); ++it2 ) + { + if ( *it2 > *it1 ) + { + return false; + } + if ( ( *it2 == *it1 ) && ( ++it1 == end() ) ) + { + return true; + } + } + + return false; +} + +template +bool cut::merge( cut const& that, cut& res, uint32_t cut_size ) const +{ + if ( _length + that._length > cut_size ) + { + const auto sign = _signature + that._signature; + if ( uint32_t( __builtin_popcount( static_cast( sign & 0xffffffff ) ) ) + uint32_t( __builtin_popcount( static_cast( sign >> 32 ) ) ) > cut_size ) + { + return false; + } + } + + int32_t length = set_union_safe( begin(), end(), that.begin(), that.end(), res.begin(), cut_size ); + if ( length >= 0 ) + { + res._cend = res._end = res.begin() + length; + res._length = static_cast( length ); + res._signature = _signature | that._signature; + return true; + } + return false; +} + +/*! \brief A data-structure to hold a set of cuts. + * + * The aim of a cut set is to contain cuts and maintain two properties. First, + * all cuts are ordered according to the `<` operator, and second, all cuts + * are irredundant, i.e., no cut in the set dominates another cut in the set. + * + * The cut set is defined using the `CutType` of cuts it should hold and a + * maximum number of cuts it can hold. No check is performed whether a cut set + * is full, and therefore the caller must not insert cuts into a full set. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + cut_set, 30> cuts; + + cut<10> c1, c2, c3, c4; + + c1.set_leaves( {1, 2, 3} ); + c2.set_leaves( {4, 5} ); + c3.set_leaves( {1, 2} ); + c4.set_leaves( {1, 3, 4} ); + + cuts.insert( c1 ); + cuts.insert( c2 ); + cuts.insert( c3 ); + cuts.insert( c4 ); + + assert( cuts.size() == 3 ); + + std::cout << cuts << std::endl; + + // will print: + // { 4, 5 } + // { 1, 2 } + // { 1, 3, 4 } + \endverbatim + */ +template +class cut_set +{ +public: + /*! \brief Standard constructor. + */ + cut_set(); + + /*! \brief Clears a cut set. + */ + void clear(); + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ); + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const; + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + */ + void insert( CutType const& cut ); + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto const& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ); + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ); + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; +}; + +template +cut_set::cut_set() +{ + clear(); +} + +template +void cut_set::clear() +{ + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } +} + +template +template +CutType& cut_set::add_cut( Iterator begin, Iterator end ) +{ + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; +} + +template +bool cut_set::is_dominated( CutType const& cut ) const +{ + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; +} + +template +void cut_set::insert( CutType const& cut ) +{ + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( _pcuts.begin(), _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + auto ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return *a < *b; } ); + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; +} + +template +void cut_set::update_best( uint32_t index ) +{ + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; +} + +template +void cut_set::limit( uint32_t size ) +{ + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/utils/include/supergate.hpp b/third-party/mockturtle/include/mockturtle/utils/include/supergate.hpp new file mode 100644 index 00000000000..8177a6b304f --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/include/supergate.hpp @@ -0,0 +1,92 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file supergate.hpp + \brief Defines the composed gate and supergate data structure. + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../io/genlib_reader.hpp" + +#include + +namespace mockturtle +{ + +template +struct composed_gate +{ + /* unique ID */ + uint32_t id; + + /* gate is a supergate */ + bool is_super{ false }; + + /* pointer to the root library gate */ + gate const* root{ nullptr }; + + /* support of the composed gate */ + uint32_t num_vars{ 0 }; + + /* function */ + kitty::dynamic_truth_table function; + + /* area */ + double area{ 0.0 }; + + /* pin-to-pin delays */ + std::array tdelay{}; + + /* fanin gates */ + std::vector*> fanin{}; +}; + +template +struct supergate +{ + /* pointer to the root gate */ + composed_gate const* root{}; + + /* area */ + double area{ 0.0 }; + + /* pin-to-pin delay */ + std::array tdelay{}; + + /* np permutation vector */ + std::vector permutation{}; + + /* pin negations */ + uint32_t polarity{ 0 }; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/mixed_radix.hpp b/third-party/mockturtle/include/mockturtle/utils/mixed_radix.hpp new file mode 100644 index 00000000000..1660a49f9d4 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/mixed_radix.hpp @@ -0,0 +1,113 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mixed_radix.hpp + \brief Mixed radix loop + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle +{ + +/*! \brief Mixed radix enumeration. + * + * The iterator pair `begin` and `end` represent a list of radixes, which are + * used to enumerate all combinations of indexes. + * + * For example if the radixes are \f$2, 3, 3\f$, then the callable `fn` is + * called on the index lists \f$0, 0, 0\f$, \f$0, 0, 1\f$, \f$0, 0, 2\f$, + * \f$\dots\f$, \f$1, 2, 1\f$, \f$1, 2, 2\f$. + * + * The callable `fn` expects two parameters which are an iterator pair of the + * indexes. If it returns a `bool`, the iteration is stopped, if the return + * value is `false`. + * + * \param begin Begin iterator of radixes + * \param end End iterator of radixes + * \param fn Callable + */ +template +void foreach_mixed_radix_tuple( Iterator begin, Iterator end, Fn&& fn ) +{ + constexpr auto is_bool_f = std::is_invocable_r_v::iterator, std::vector::iterator>; + constexpr auto is_void_f = std::is_invocable_r_v::iterator, std::vector::iterator>; + + static_assert( is_bool_f || is_void_f ); + + std::vector positions( std::distance( begin, end ), 0u ); + + while ( true ) + { + if constexpr ( is_bool_f ) + { + if ( !fn( positions.begin(), positions.end() ) ) + { + return; + } + } + else + { + fn( positions.begin(), positions.end() ); + } + + auto itm = end - 1; + auto itp = positions.end() - 1; + auto ret = false; + while ( !ret && *itp == ( *itm - 1 ) ) + { + *itp = 0; + if ( itp != positions.begin() ) + { + --itp; + } + + if ( itm == begin ) + { + ret = true; + } + else + { + --itm; + } + } + + if ( ret ) + { + break; + } + + ( *itp )++; + } +} + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/name_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/name_utils.hpp new file mode 100644 index 00000000000..19e4d9d66c5 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/name_utils.hpp @@ -0,0 +1,159 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file name_utils.hpp + \brief Utility functions to restore network names after optimization. + + \author Marcel Walter + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "node_map.hpp" + +namespace mockturtle +{ + +/*! \brief Restores the network name that might have been given to network's former incarnation. + * + * \param ntk_src The source logic network, which potentially has a name + * \param ntk_dest The destination logic network, whose name is to be restored + */ +template +void restore_network_name( const NtkSrc& ntk_src, NtkDest& ntk_dest ) noexcept +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + if constexpr ( has_get_network_name_v && has_set_network_name_v ) + { + ntk_dest.set_network_name( ntk_src.get_network_name() ); + } +} + +/*! \brief Restores all names that might have been given to a network's former incarnation. + * + * **Required network functions for the NtkSrc:** + * - `foreach_node` + * - `foreach_fanin` + * - `foreach_po` + * - `get_node` + * + * \param ntk_src The source logic network, which potentially has named signals + * \param ntk_dest The destination logic network, whose names are to be restored + * \param old2new Mapping of nodes from ntk_src to signals of ntk_dest + */ +template +void restore_names( const NtkSrc& ntk_src, NtkDest& ntk_dest, node_map, NtkSrc>& old2new ) noexcept +{ + restore_network_name( ntk_src, ntk_dest ); + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node function" ); + static_assert( has_foreach_fanin_v, "NtkSrc does not implement the foreach_fanin function" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po function" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node function" ); + + const auto restore_signal_name = [&ntk_src, &ntk_dest, &old2new]( const auto& f ) { + if ( ntk_src.has_name( f ) ) + { + const auto name = ntk_src.get_name( f ); + + ntk_dest.set_name( old2new[ntk_src.get_node( f )], name ); + } + }; + + const auto restore_output_name = [&ntk_src, &ntk_dest]( [[maybe_unused]] const auto& po, const auto i ) { + if ( ntk_src.has_output_name( i ) ) + { + const auto name = ntk_src.get_output_name( i ); + + ntk_dest.set_output_name( i, name ); + } + }; + + ntk_src.foreach_node( [&ntk_src, &restore_signal_name]( const auto& n ) { ntk_src.foreach_fanin( n, restore_signal_name ); } ); + + ntk_src.foreach_po( restore_output_name ); + } +} + +/*! \brief Restore PI and PO names, matching by order. + * + * **Required network functions for NtkSrc:** + * - `foreach_pi` + * - `foreach_po` + * - `num_pis` + * - `num_pos` + * - `has_name` + * - `get_name` + * - `make_signal` + * - `has_output_name` + * - `get_output_name` + * + * **Required network functions for NtkDest:** + * - `foreach_pi` + * - `num_pis` + * - `num_pos` + * - `set_name` + * - `make_signal` + * - `set_output_name` + * + * \param ntk_src The source logic network, which potentially has named signals + * \param ntk_dest The destination logic network, whose names are to be restored + */ +template +void restore_pio_names_by_order( const NtkSrc& ntk_src, NtkDest& ntk_dest ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_has_name_v && has_get_name_v, "NtkSrc does not implement the has_name and/or get_name functions" ); + static_assert( has_has_output_name_v && has_get_output_name_v, "NtkSrc does not implement the has_output_name and/or get_output_name functions" ); + static_assert( has_set_name_v && has_set_output_name_v, "NtkDest does not implement the set_name and/or set_output_name functions" ); + + assert( ntk_src.num_pis() == ntk_dest.num_pis() ); + assert( ntk_src.num_pos() == ntk_dest.num_pos() ); + + std::vector pi_names( ntk_src.num_pis(), "" ); + ntk_src.foreach_pi( [&]( auto const& n, auto i ) { + if ( ntk_src.has_name( ntk_src.make_signal( n ) ) ) + pi_names[i] = ntk_src.get_name( ntk_src.make_signal( n ) ); + } ); + ntk_dest.foreach_pi( [&]( auto const& n, auto i ) { + if ( pi_names[i] != "" ) + ntk_dest.set_name( ntk_dest.make_signal( n ), pi_names[i] ); + } ); + + ntk_src.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk_src.has_output_name( i ) ) + ntk_dest.set_output_name( i, ntk_src.get_output_name( i ) ); + } ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/node_map.hpp b/third-party/mockturtle/include/mockturtle/utils/node_map.hpp new file mode 100644 index 00000000000..6fb4e1279a1 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/node_map.hpp @@ -0,0 +1,555 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file node_map.hpp + \brief Map indexed by network nodes + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Associative container network nodes + * + * This container helps to store and access values associated to nodes + * in a network. + * + * Two implementations are provided, one using std::vector and another + * using std::unordered_map as internal storage. The former + * implementation can be pre-allocated and provides a fast way to + * access the data. The later implementation offers a way to + * associate values to a subset of nodes and to check whether a value + * is available. + */ +template> +class node_map; + +/*! \brief Vector node map + * + * This container is initialized with a network to derive the size + * according to the number of nodes. The container can be accessed + * via nodes, or indirectly via signals, from which the corresponding + * node is derived. + * + * The implementation uses a vector as underlying data structure which + * is indexed by the node's index. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `node_to_index` + * + */ +template +class node_map> +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::vector; + using reference = typename container_type::reference; + using const_reference = typename container_type::const_reference; + +public: + /*! \brief Default constructor. */ + explicit node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared( ntk.size() ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Constructor with default value. + * + * Initializes all values in the container to `init_value`. + */ + node_map( Ntk const& ntk, T const& init_value ) + : ntk( &ntk ), + data( std::make_shared( ntk.size(), init_value ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Deep copy. */ + node_map copy() const + { + node_map copy( ntk ); + *( copy.data ) = *data; + return copy; + } + + /*! \brief Mutable access to value by node. */ + reference operator[]( node const& n ) + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Constant access to value by node. */ + const_reference operator[]( node const& n ) const + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + reference operator[]( signal const& f ) + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + const_reference operator[]( signal const& f ) const + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with `init_value`. + * + * \param init_value Initialization value after resize + */ + void reset( T const& init_value = {} ) + { + data->clear(); + data->resize( ntk->size(), init_value ); + } + + /*! \brief Resizes the map. + * + * This function should be called, if the node_map's size needs to + * be changed without clearing its data. + * + * \param init_value Initialization value after resize + */ + void resize( T const& init_value = {} ) + { + if ( ntk->size() > data->size() ) + { + data->resize( ntk->size(), init_value ); + } + } + +private: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Unordered node map + * + * This implementation of the container is initialized with a network. + * The map entries are constructed on the fly. The container + * can be accessed via ndoes, or indirectly via signals, from which + * the corresponding node is derived. + * + * The implementation uses an std::unordered_map as underlying data + * structure which is indexed by the node's index. + * + * This implementation is aliased as `unordered_node_map`. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * + */ +template +class node_map> +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::unordered_map; + using reference = T&; + using const_reference = const T&; + +public: + /*! \brief Default constructor. */ + explicit node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared() ) + { + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Deep copy. */ + node_map copy() const + { + node_map copy( ntk ); + *( copy.data ) = *data; + return copy; + } + + /*! \brief Check if a key is already defined. */ + bool has( node const& n ) const + { + return data->find( ntk->node_to_index( n ) ) != data->end(); + } + + /*! \brief Check if a key is already defined. */ + template>> + bool has( signal const& f ) const + { + return data->find( ntk->node_to_index( ntk->get_node( f ) ) ) != data->end(); + } + + /*! \brief Erase a key (if it exists). */ + void erase( node const& n ) + { + if ( has( n ) ) + { + data->erase( ntk->node_to_index( n ) ); + } + } + + /*! \brief Erase a key (if it exists). */ + template>> + void erase( signal const& f ) + { + if ( has( ntk->get_node( f ) ) ) + { + data->erase( ntk->node_to_index( ntk->get_node( f ) ) ); + } + } + + /*! \brief Mutable access to value by node. */ + reference operator[]( node const& n ) + { + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Constant access to value by node. */ + const_reference operator[]( node const& n ) const + { + assert( has( n ) && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + reference operator[]( signal const& f ) + { + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + const_reference operator[]( signal const& f ) const + { + assert( has( ntk->get_node( f ) ) && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Clear all entries of the map. + * + * All data in the map is cleared. + */ + void reset() + { + data->clear(); + } + + void resize() + { + } + +protected: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Template alias `unordered_node_map` */ +template +using unordered_node_map = node_map>; + +/*! \brief Vector-based node map with validity query + * + * This container is initialized with a network to derive the size + * according to the number of nodes. The container can be accessed + * via nodes, or indirectly via signals, from which the corresponding + * node is derived. + * + * The implementation uses a vector as underlying data structure, so + * that it benefits from fast access. It is supplemented with an + * additional validity field such that it can be used like an + * `unordered_node_map`. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `node_to_index` + * + */ +template +class incomplete_node_map +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::vector>; + +public: + /*! \brief Default constructor. */ + explicit incomplete_node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared( ntk.size() ) ) + { + static_assert( !std::is_same_v, "T cannot be std::monostate" ); + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Constructor with default value. + * + * Initializes all values in the container to `init_value`. + */ + incomplete_node_map( Ntk const& ntk, T const& init_value ) + : ntk( &ntk ), + data( std::make_shared( ntk.size(), init_value ) ) + { + static_assert( !std::is_same_v, "T cannot be std::monostate" ); + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Check if a key is already defined. */ + bool has( node const& n ) const + { + return std::holds_alternative( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Check if a key is already defined. */ + template>> + bool has( signal const& f ) const + { + return std::holds_alternative( ( *data )[ntk->node_to_index( ntk->get_node( f ) )] ); + } + + /*! \brief Erase a key (if it exists). */ + void erase( node const& n ) + { + ( *data )[ntk->node_to_index( n )] = std::monostate(); + } + + /*! \brief Erase a key (if it exists). */ + template>> + void erase( signal const& f ) + { + ( *data )[ntk->node_to_index( ntk->get_node( f ) )] = std::monostate(); + } + + /*! \brief Mutable access to value by node. */ + T& operator[]( node const& n ) + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + if ( !has( n ) ) + { + ( *data )[ntk->node_to_index( n )] = T(); + } + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Constant access to value by node. */ + T const& operator[]( node const& n ) const + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + assert( has( n ) ); + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + T& operator[]( signal const& f ) + { + auto n = ntk->get_node( f ); + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + if ( !has( n ) ) + { + ( *data )[ntk->node_to_index( n )] = T(); + } + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + T const& operator[]( signal const& f ) const + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + assert( has( ntk->get_node( f ) ) ); + return std::get( ( *data )[ntk->node_to_index( ntk->get_node( f ) )] ); + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with the place holder (empty) element. + */ + void reset() + { + data->clear(); + data->resize( ntk->size() ); + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with `init_value`. + * + * \param init_value Initialization value after resize + */ + void reset( T const& init_value ) + { + data->clear(); + data->resize( ntk->size(), init_value ); + } + + /*! \brief Resizes the map. + * + * This function should be called, if the node_map's size needs to + * be changed without clearing its data. + */ + void resize() + { + if ( ntk->size() > data->size() ) + { + data->resize( ntk->size() ); + } + } + +private: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Initializes a network for copying together with node map. + * + * This utility function is helpful when creating a network from another one, + * a very common task. It creates the network of type `NtkDest` and already + * creates a node map to map nodes from the source network, of type `NtkSrc` to + * nodes of the new network. The function map constant inputs and creates and + * maps primary inputs. + */ +template +std::pair, NtkSrc>> initialize_copy_network( NtkSrc const& src ) +{ + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + + node_map, NtkSrc> old2new( src ); + NtkDest dest; + old2new[src.get_constant( false )] = dest.get_constant( false ); + if ( src.get_node( src.get_constant( true ) ) != src.get_node( src.get_constant( false ) ) ) + { + old2new[src.get_constant( true )] = dest.get_constant( true ); + } + src.foreach_pi( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + } ); + return { dest, old2new }; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/standard_cell.hpp b/third-party/mockturtle/include/mockturtle/utils/standard_cell.hpp new file mode 100644 index 00000000000..917550dc0f8 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/standard_cell.hpp @@ -0,0 +1,98 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file standard_cell.hpp + \brief Defines logic cells. + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../io/genlib_reader.hpp" + +namespace mockturtle +{ + +struct standard_cell +{ + /* Unique name */ + std::string name; + + /* Unique ID */ + uint32_t id; + + /* Pointer to a gate representing each individual output */ + std::vector gates; + + /* Area */ + double area; +}; + +/*! \brief Reconstruct standard cells from GENLIB gates. + * + * This function returns a vector of standard cells given + * GENLIB gates. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( in, genlib_reader( gates ) ); + + // Extract standard cells + std::vector cells = get_standard_cells( gates ); + \endverbatim + */ +inline std::vector get_standard_cells( std::vector const& gates ) +{ + std::unordered_map name_to_index; + std::vector cells; + + for ( gate const& g : gates ) + { + if ( auto it = name_to_index.find( g.name ); it != name_to_index.end() ) + { + /* add to existing cell (multi-output) */ + cells[it->second].gates.push_back( g ); + } + else + { + name_to_index[g.name] = cells.size(); + cells.emplace_back( standard_cell{ g.name, static_cast( cells.size() ), { g }, g.area } ); + } + } + + return cells; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/utils/stopwatch.hpp b/third-party/mockturtle/include/mockturtle/utils/stopwatch.hpp new file mode 100644 index 00000000000..6b1cf66f4bb --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/stopwatch.hpp @@ -0,0 +1,178 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file stopwatch.hpp + \brief Stopwatch + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Stopwatch interface + * + * This class implements a stopwatch interface to track time. It starts + * tracking time at construction and stops tracking time at deletion + * automatically. A reference to a duration object is passed to the + * constructor. After stopping the time the measured time interval is added + * to the durationr reference. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + { // some block + stopwatch t( time ); + + // do some work + } // stopwatch is stopped here + + std::cout << fmt::format( "{:5.2f} seconds passed\n", to_seconds( time ) ); + \endverbatim + */ +template +class stopwatch +{ +public: + using clock = Clock; + using duration = typename Clock::duration; + using time_point = typename Clock::time_point; + + /*! \brief Default constructor. + * + * Starts tracking time. + */ + explicit stopwatch( duration& dur ) + : dur( dur ), + beg( clock::now() ) + { + } + + /*! \brief Default deconstructor. + * + * Stops tracking time and updates duration. + */ + ~stopwatch() + { + dur += ( clock::now() - beg ); + } + +private: + duration& dur; + time_point beg; +}; + +/*! \brief Calls a function and tracks time. + * + * The function that is passed as second parameter can be any callable object + * that takes no parameters. This construction can be used to avoid + * pre-declaring the result type of a computation that should be tracked. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + auto result = call_with_stopwatch( time, [&]() { return function( parameters ); } ); + \endverbatim + * + * \param dur Duration reference (time will be added to it) + * \param fn Callable object with no arguments + */ +template +std::invoke_result_t call_with_stopwatch( typename Clock::duration& dur, Fn&& fn ) +{ + stopwatch t( dur ); + return fn(); +} + +/*! \brief Constructs an object and calls time. + * + * This function can track the time for the construction of an object and + * returns the constructed object. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + // create vector with 100000 elements initialized to 42 + auto result = make_with_stopwatch>( time, 100000, 42 ); + \endverbatim + */ +template +T make_with_stopwatch( typename Clock::duration& dur, Args... args ) +{ + stopwatch t( dur ); + return T{ std::forward( args )... }; +} + +/*! \brief Utility function to convert duration into seconds. */ +template +inline double to_seconds( Duration const& dur ) +{ + return std::chrono::duration_cast>( dur ).count(); +} + +template +class print_time +{ +public: + print_time() + : _t( new stopwatch( _d ) ) + { + } + + ~print_time() + { + delete _t; + std::cout << fmt::format( "[i] run-time: {:5.2f} secs\n", to_seconds( _d ) ); + } + +private: + stopwatch* _t{ nullptr }; + typename stopwatch::duration _d{}; +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/struct_library.hpp b/third-party/mockturtle/include/mockturtle/utils/struct_library.hpp new file mode 100644 index 00000000000..9d966254300 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/struct_library.hpp @@ -0,0 +1,1546 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file struct_library.hpp + \brief Implements utilities for structural matching + + \author Alessandro Tempia Calvino + \author Gianluca Radi +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" + +#include "../io/genlib_reader.hpp" +#include "include/supergate.hpp" + +namespace mockturtle +{ + +struct struct_library_params +{ + /*! \brief Load gates with minimum size only */ + bool load_minimum_size_only{ true }; + + /*! \brief Reports loaded gates */ + bool verbose{ false }; +}; + +/*! \brief Library of gates for structural matching + * + * This class creates a technology library from a set + * of input gates. + * + * Gates are processed to derive rules in the AIG format. + * Then, every rule and subrule gets a unique id and the AND table is built. + * Every gate gets a unique label comprehensive of its rule id and whether it is positive or negative. + * + * The template parameter `NInputs` selects the maximum number of variables + * allowed for a gate in the library. + * + * By default, `struct_library` is used in `tech_library` when NInputs is greater than 6. + * + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + // struct library + mockturtle::struct_library lib( gates ); + \endverbatim + */ +template +class struct_library +{ +public: + enum class node_type + { + none, + zero_, + pi_, + and_, + or_, + mux_, + xor_ + }; + + struct signal + { + union + { + struct + { + uint32_t inv : 1; + uint32_t index : 31; + }; + uint32_t data; + }; + + bool operator==( signal const& other ) const + { + return data == other.data; + } + }; + + /* struct for representing nodes in dsd decomposition */ + struct dsd_node + { + node_type type; + + uint32_t index; + + std::vector fanin = {}; + }; + + /* struct for labels to assign to gates */ + struct label + { + union + { + struct + { + uint32_t inv : 1; + uint32_t index : 31; + }; + uint32_t data; + }; + bool operator==( label const& other ) const + { + return data == other.data; + } + }; + + struct signal_hash + { + std::size_t operator()( signal const& s ) const noexcept + { + return std::hash{}( s.data ); + } + }; + + struct tuple_s_hash + { + std::size_t operator()( std::tuple const& t ) const noexcept + { + size_t h1 = signal_hash()( std::get<0>( t ) ); + size_t h2 = signal_hash()( std::get<1>( t ) ); + return (uint64_t)h1 ^ ( ( (uint64_t)h2 ) << 32 ); // or use boost::hash_combine + } + }; + +private: + static constexpr uint32_t invalid_index = UINT32_MAX; + using supergates_list_t = std::vector>; + using composed_list_t = std::vector>; + using lib_rule = absl::flat_hash_map, kitty::hash>; + using rule = std::vector; + using lib_table = absl::flat_hash_map, uint32_t, tuple_s_hash>; + using map_label_gate = std::unordered_map; + +public: + explicit struct_library( std::vector const& gates, struct_library_params const& ps = {} ) + : _gates( gates ), + _ps( ps ), + _supergates(), + _dsd_map(), + _and_table(), + _label_to_gate() + {} + +public: + /*! \brief Construct the structural library. + * + * Generates the patterns for structural matching. + * Variable `min_vars` defines the minimum number of + * gate inputs considered for the library creation. + * 0 < min_vars < UINT32_MAX + */ + void construct( uint32_t min_vars = 2u ) + { + generate_library( min_vars ); + } + + /*! \brief Construct the structural library. + * + * Generates the patterns for structural matching. + */ + const map_label_gate& get_struct_library() const + { + return _label_to_gate; + } + + /*! \brief Get the pattern ID. + * + * \param id1 first pattern id. + * \param id2 second pattern id. + * Returns a pattern ID if found, UINT32_MAX otherwise given the + * children IDs. This function works with only AND operators. + */ + const uint32_t get_pattern_id( uint32_t id1, uint32_t id2 ) const + { + signal l, r; + l.data = id1; + /* ignore input negations */ + if ( l.data == 3 ) + l.data = 2; + r.data = id2; + if ( r.data == 3 ) + r.data = 2; + std::tuple key; + if ( l.index <= r.index ) + key = std::make_tuple( l, r ); + else + key = std::make_tuple( r, l ); + auto match = _and_table.find( key ); + if ( match != _and_table.end() ) + return match->second; + return UINT32_MAX; + } + + /*! \brief Get the gates matching the pattern ID. + * + * Returns a list of gates that match the pattern ID. + */ + const supergates_list_t* get_supergates_pattern( uint32_t id, bool phase ) const + { + auto match = _label_to_gate.find( ( id << 1 ) | ( phase ? 1 : 0 ) ); + if ( match != _label_to_gate.end() ) + { + return &( match->second ); + } + return nullptr; + } + + /*! \brief Returns the number of large gates. + * + * Number of gates with more than 6 inputs. + */ + const uint32_t get_num_large_gates() const + { + return num_large_gates; + } + + /*! \brief Print and table. + * + */ + void print_and_table() + { + for ( auto elem : _and_table ) + { + auto first0 = std::get<0>( elem.first ); + auto first1 = std::get<1>( elem.first ); + std::cout << "<" << ( first0.inv ? "!" : "" ) << first0.index; + std::cout << ", " << ( first1.inv ? "!" : "" ) << first1.index << "> "; + std::cout << elem.second << "\n"; + } + } + +private: + void generate_library( uint32_t min_vars ) + { + /* select and load gates */ + _supergates.reserve( _gates.size() ); + generate_composed_gates(); + + /* mark dominate gates */ + std::vector skip_gates( _supergates.size(), false ); + filter_gates( skip_gates ); + + std::vector indexes( _supergates.size() ); + std::iota( indexes.begin(), indexes.end(), 0 ); + uint32_t max_label = 1; + uint32_t gate_pol = 0; // polarity of AND equivalent gate + uint32_t shift = 0; + + /* sort cells by increasing order of area */ + std::stable_sort( indexes.begin(), indexes.end(), + [&]( auto const& a, auto const& b ) -> bool { + return _supergates[a].area < _supergates[b].area; + } ); + + for ( uint32_t const ind : indexes ) + { + composed_gate const& gate = _supergates[ind]; + + if ( gate.num_vars < 2 || skip_gates[ind] ) + continue; + + /* DSD decomposition */ + rule rule = {}; + std::vector support = {}; + for ( uint32_t i = 0; i < gate.num_vars; i++ ) + { + rule.push_back( { node_type::pi_, i, {} } ); + support.push_back( i ); + } + auto cpy = gate.function; + gate_disjoint = false; + compute_dsd( cpy, support, rule ); + + /* ignore gates with reconvergence */ + if ( gate_disjoint ) + continue; + + if ( gate.num_vars > 6 ) + { + ++num_large_gates; + } + + _dsd_map.insert( { gate.function, rule } ); + if ( _ps.verbose ) + { + std::cout << "Dsd:\n"; + print_rule( rule, rule[rule.size() - 1] ); + } + + /* Aig conversion */ + auto aig_rule = map_to_aig( rule ); + if ( _ps.verbose ) + { + std::cout << "\nAig:\n"; + print_rule( aig_rule, aig_rule[aig_rule.size() - 1] ); + } + + /* Rules derivation */ + std::vector> der_rules = {}; + der_rules.push_back( aig_rule ); + std::vector> depths = { { get_depth( aig_rule, aig_rule[aig_rule[aig_rule.size() - 1].fanin[0].index] ), get_depth( aig_rule, aig_rule[aig_rule[aig_rule.size() - 1].fanin[1].index] ) } }; + create_rules_from_dsd( der_rules, aig_rule, aig_rule[aig_rule.size() - 1], depths, true, true ); + if ( _ps.verbose ) + { + std::cout << "\nDerived:\n"; + } + + /* Indexing of rules and subrules, and_table construction, and gates' label assignement */ + for ( auto elem : der_rules ) + { + gate_pol = 0; + shift = 0; + std::vector perm( gate.num_vars ); + auto index_rule = do_indexing_rule( elem, elem[elem.size() - 1], max_label, gate_pol, perm, shift ); + + /* skip gate creation for small gates (<`min_vars` inputs) */ + if ( gate.num_vars < min_vars ) + continue; + + supergate sg = { &gate, + static_cast( gate.area ), + gate.tdelay, + perm, + gate_pol }; + + /* permute pin-to-pin delays */ + for ( uint32_t i = 0; i < gate.num_vars; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + auto& v = _label_to_gate[index_rule.data]; + + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + v.insert( it, sg ); + + if ( _ps.verbose ) + { + print_rule( elem, elem[elem.size() - 1] ); + std::cout << "\n"; + for ( const auto& [label, gates] : _label_to_gate ) + { + std::cout << label << "\n"; + for ( auto sg : gates ) + { + std::cout << ( sg.root )->root->expression << "\n"; + } + } + } + } + + if ( _ps.verbose ) + { + std::cout << "\n"; + std::cout << "And table:\n"; + print_and_table(); + std::cout << "\n"; + } + } + if ( _ps.verbose ) + std::cout << "\n"; + } + + void generate_composed_gates() + { + /* filter multi-output gates */ + std::unordered_map multioutput_map; + multioutput_map.reserve( _gates.size() ); + + for ( const auto& g : _gates ) + { + if ( multioutput_map.find( g.name ) != multioutput_map.end() ) + { + multioutput_map[g.name] += 1; + } + else + { + multioutput_map[g.name] = 1; + } + } + + /* create composed gates */ + uint32_t ignored = 0; + for ( const auto& g : _gates ) + { + std::array pin_to_pin_delays{}; + + /* filter large gates and multi-output gates */ + if ( g.function.num_vars() > NInputs || multioutput_map[g.name] > 1 ) + { + ++ignored; + continue; + } + + auto i = 0u; + for ( auto const& pin : g.pins ) + { + /* use worst pin delay */ + pin_to_pin_delays[i++] = std::max( pin.rise_block_delay, pin.fall_block_delay ); + } + + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + } + + bool compare_sizes( composed_gate const& s1, composed_gate const& s2 ) + { + if ( s1.area < s2.area ) + return true; + else if ( s1.area > s2.area ) + return false; + + /* compute average pin delay */ + float s1_delay = 0, s2_delay = 0; + assert( s1.num_vars == s2.num_vars ); + for ( uint32_t i = 0; i < s1.num_vars; ++i ) + { + s1_delay += s1.tdelay[i]; + s2_delay += s2.tdelay[i]; + } + + if ( s1_delay < s2_delay ) + return true; + else if ( s1_delay > s2_delay ) + return false; + else if ( s1.root->name < s2.root->name ) + return true; + + return false; + } + + void filter_gates( std::vector& skip_gates ) + { + for ( uint32_t i = 0; i < skip_gates.size() - 1; ++i ) + { + if ( _supergates[i].root == nullptr ) + continue; + + if ( skip_gates[i] ) + continue; + + auto const& tti = _supergates[i].function; + for ( uint32_t j = i + 1; j < skip_gates.size(); ++j ) + { + auto const& ttj = _supergates[j].function; + + /* get the same functionality */ + if ( skip_gates[j] || tti != ttj ) + continue; + + if ( _ps.load_minimum_size_only ) + { + if ( compare_sizes( _supergates[i], _supergates[j] ) ) + { + skip_gates[j] = true; + continue; + } + else + { + skip_gates[i] = true; + break; + } + } + + /* is i smaller than j */ + bool smaller = _supergates[i].area < _supergates[j].area; + + /* is i faster for every pin */ + bool faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( _supergates[i].tdelay[k] > _supergates[j].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[j] = true; + continue; + } + + /* is j faster for every pin */ + faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( _supergates[j].tdelay[k] > _supergates[i].tdelay[k] ) + faster = false; + } + + if ( !smaller && faster ) + { + skip_gates[i] = true; + break; + } + } + } + } + + uint32_t try_top_dec( kitty::dynamic_truth_table& tt, uint32_t num_vars ) + { + uint32_t i = 0; + for ( ; i < num_vars; i++ ) + { + auto res = is_top_dec( tt, i, false ); + if ( res.type != node_type::none ) + break; + } + return i; + } + + dsd_node do_top_dec( kitty::dynamic_truth_table& tt, uint32_t index, std::vector mapped_support ) + { + auto node = is_top_dec( tt, index, false, &tt ); + + node.fanin[0].index = mapped_support[index]; + return node; + } + + std::tuple try_bottom_dec( kitty::dynamic_truth_table& tt, uint32_t num_vars ) + { + uint32_t i = 0; + uint32_t j = 0; + dsd_node res; + for ( i = 0; i < num_vars; i++ ) + { + for ( j = i + 1; j < num_vars; j++ ) + { + res = is_bottom_dec( tt, i, j ); + if ( res.type != node_type::none ) + break; + } + if ( res.type != node_type::none ) + break; + } + std::tuple ret = { i, j }; + return ret; + } + + dsd_node do_bottom_dec( kitty::dynamic_truth_table& tt, uint32_t i, uint32_t j, uint32_t new_index, std::vector& mapped_support ) + { + auto node = is_bottom_dec( tt, i, j, &tt, new_index, false ); + + node.fanin[0].index = mapped_support[i]; + node.fanin[1].index = mapped_support[j]; + + mapped_support[i] = node.index; + return node; + } + + dsd_node do_shannon_dec( kitty::dynamic_truth_table tt, uint32_t index, kitty::dynamic_truth_table& co0, kitty::dynamic_truth_table& co1, std::vector mapped_support ) + { + auto node = shannon_dec( tt, index, &co0, &co1 ); + node.fanin[0].index = mapped_support[index]; + return node; + } + + void update_support( std::vector& v, uint32_t index ) + { + uint32_t i = 0; + for ( ; i < v.size() && i < index; i++ ) + ; + + for ( ; i < v.size() - 1; i++ ) + { + v[i] = v[i + 1]; + } + + v.pop_back(); + } + + template + void min_base_shrink( TT& tt, TT& tt_shr ) + { + kitty::min_base_inplace( tt ); + kitty::shrink_to_inplace( tt_shr, tt ); + } + + uint32_t is_PI( kitty::dynamic_truth_table const& rem, uint32_t n_vars ) + { + for ( uint32_t i = 0; i < n_vars; i++ ) + { + auto var = rem.construct(); + kitty::create_nth_var( var, i ); + if ( rem == var ) + { + return i; + } + } + return invalid_index; + } + + uint32_t is_inv_PI( kitty::dynamic_truth_table const& rem, uint32_t n_vars ) + { + for ( uint32_t i = 0; i < n_vars; i++ ) + { + auto var = rem.construct(); + kitty::create_nth_var( var, i ); + if ( rem == ~var ) + { + return i; + } + } + return invalid_index; + } + + void update_found_rule( kitty::dynamic_truth_table& tt, std::vector& mapped_support, std::vector& rule ) + { + uint32_t count_old = 0; + uint32_t count_curr = 0; + std::vector new_rule; + auto found_rule = get_rules( tt ); + std::copy_if( found_rule.begin(), found_rule.end(), std::back_inserter( new_rule ), []( dsd_node n ) { + return ( n.type != node_type::pi_ ); + } ); + for_each( found_rule.begin(), found_rule.end(), [&]( dsd_node elem ) { + if ( elem.type == node_type::pi_ ) + count_old++; + } ); + for_each( rule.begin(), rule.end(), [&]( dsd_node elem ) { + count_curr++; + } ); + /* update index of node */ + std::transform( new_rule.begin(), new_rule.end(), new_rule.begin(), [&]( dsd_node& n ) -> dsd_node { + return { n.type, n.index + count_curr - count_old, n.fanin }; + } ); + /* update index of signal of fanins of nodes */ + std::transform( new_rule.begin(), new_rule.end(), new_rule.begin(), [&]( dsd_node& n ) -> dsd_node { + transform( n.fanin.begin(), n.fanin.end(), n.fanin.begin(), [&]( signal s ) -> signal { + if ( s.index >= count_old ) + return { s.inv, s.index + count_curr - count_old }; + else + return { s.inv, mapped_support[s.index] }; + } ); + return { n.type, n.index, n.fanin }; + } ); + rule.insert( rule.end(), new_rule.begin(), new_rule.end() ); + } + + /*! \brief Compute DSD decomposition for a boolean function recursively. + * + * \param tt dynamic truth table representing the function. + * \param mapped_support vector indicating function's support at every recursive step. + * \param rule DSD decomposition of the function. + * Returns index of dsd_node to add to rule. + */ + uint32_t compute_dsd( kitty::dynamic_truth_table& tt, std::vector mapped_support, std::vector& rule ) + { + /* Function has been already found */ + if ( !get_rules( tt ).empty() ) + { + update_found_rule( tt, mapped_support, rule ); + return rule.size() - 1; + } + /* try top decomposition */ + uint32_t i = try_top_dec( tt, tt.num_vars() ); + if ( i < tt.num_vars() ) // it was top decomposable + { + auto res = do_top_dec( tt, i, mapped_support ); + + update_support( mapped_support, i ); + + kitty::dynamic_truth_table tt_shr( tt.num_vars() - 1 ); + min_base_shrink( tt, tt_shr ); + + if ( is_PI( tt_shr, tt_shr.num_vars() ) == invalid_index && is_inv_PI( tt_shr, tt_shr.num_vars() ) == invalid_index ) // check if remainder is PI + { + res.fanin.push_back( { 0, compute_dsd( tt_shr, mapped_support, rule ) } ); + } + else + { + if ( is_PI( tt_shr, tt_shr.num_vars() ) != invalid_index ) + { + res.fanin.push_back( { 0, mapped_support[is_PI( tt_shr, tt_shr.num_vars() )] } ); + } + else + { + res.fanin.push_back( { 1, mapped_support[is_inv_PI( tt_shr, tt_shr.num_vars() )] } ); + } + } + res.index = rule.size(); + rule.push_back( res ); + + return res.index; + } + else /* try bottom decomposition */ + { + auto couple = try_bottom_dec( tt, tt.num_vars() ); + i = std::get<0>( couple ); + uint32_t j = std::get<1>( couple ); + + if ( i < tt.num_vars() ) // it was bottom decomposable + { + auto res = do_bottom_dec( tt, i, j, rule.size(), mapped_support ); + rule.push_back( res ); + + update_support( mapped_support, j ); + + kitty::dynamic_truth_table tt_shr( tt.num_vars() - 1 ); + min_base_shrink( tt, tt_shr ); + + return compute_dsd( tt_shr, mapped_support, rule ); + } + else /* do shannon decomposition */ + { + kitty::dynamic_truth_table co0( tt.num_vars() ); + kitty::dynamic_truth_table co1( tt.num_vars() ); + kitty::dynamic_truth_table co0_shr( tt.num_vars() - 1 ); + kitty::dynamic_truth_table co1_shr( tt.num_vars() - 1 ); + + uint32_t index = find_unate_var( tt ); + + auto res = do_shannon_dec( tt, index, co0, co1, mapped_support ); + + /* check for reconvergence */ + gate_disjoint = true; + + uint32_t inv_var_co1 = is_inv_PI( co1, co1.num_vars() ); + uint32_t var_co1 = is_PI( co1, co1.num_vars() ); + uint32_t inv_var_co0 = is_inv_PI( co0, co0.num_vars() ); + uint32_t var_co0 = is_PI( co0, co0.num_vars() ); + + update_support( mapped_support, index ); + + if ( inv_var_co1 == invalid_index && var_co1 == invalid_index ) // check if co1 is PI + { + min_base_shrink( co1, co1_shr ); + res.fanin.insert( res.fanin.begin(), { 0, compute_dsd( co1_shr, mapped_support, rule ) } ); + } + else + { + if ( inv_var_co1 != invalid_index ) + { + uint32_t map_inv_var_co1 = mapped_support[inv_var_co1]; + res.fanin.insert( res.fanin.begin(), { 1, map_inv_var_co1 } ); + } + else + { + uint32_t map_var_co1 = mapped_support[var_co1]; + res.fanin.insert( res.fanin.begin(), { 0, map_var_co1 } ); + } + } + + if ( inv_var_co0 == invalid_index && var_co0 == invalid_index ) // check if co0 is PI + { + min_base_shrink( co0, co0_shr ); + res.fanin.insert( res.fanin.begin(), { 0, compute_dsd( co0_shr, mapped_support, rule ) } ); + } + else + { + if ( inv_var_co0 != invalid_index ) + { + uint32_t map_inv_var_co0 = mapped_support[inv_var_co0]; + res.fanin.insert( res.fanin.begin(), { 1, map_inv_var_co0 } ); + } + else + { + uint32_t map_var_co0 = mapped_support[var_co0]; + res.fanin.insert( res.fanin.begin(), { 0, map_var_co0 } ); + } + } + + res.index = rule.size(); + rule.push_back( res ); + + return res.index; + } + } + } + + rule get_rules( kitty::dynamic_truth_table const& tt ) + { + auto match = _dsd_map.find( tt ); + if ( match != _dsd_map.end() ) + return match->second; + return {}; + } + + dsd_node* get_father( rule& rule, dsd_node& node ) + { + for ( uint32_t i = 0; i < rule.size(); i++ ) + { + if ( rule[i].type != node_type::pi_ && rule[i].type != node_type::zero_ && ( rule[i].fanin[0].index == node.index || rule[i].fanin[1].index == node.index ) ) + return &rule[i]; + } + return nullptr; + } + + dsd_node* find_node( rule& r, uint32_t i ) + { + for ( uint32_t j = 0; j < r.size(); j++ ) + { + if ( r[j].index == i ) + return &r[j]; + } + return nullptr; + } + + /*! \brief Convert rule derived from DSD decomposition into aig format. + * + * \param r rule to convert. + * Returns rule converted into aig format. + */ + rule map_to_aig( rule& r ) + { + std::vector rule( r ); + std::vector aig_rule; + + std::transform( rule.begin(), rule.end(), rule.begin(), []( dsd_node n ) -> dsd_node { + for ( auto& s : n.fanin ) + { + s.index += 1; + } + return { n.type, n.index + 1, n.fanin }; + } ); + + rule.insert( rule.begin(), { node_type::zero_, 0, {} } ); + + for ( typename std::vector::reverse_iterator i = rule.rbegin(); i != rule.rend(); ++i ) + { + dsd_node n = *i; + dsd_node new_node; + + if ( n.type == node_type::and_ || n.type == node_type::pi_ || n.type == node_type::zero_ ) + { + new_node = n; + } + else if ( n.type == node_type::or_ ) + { + new_node = { node_type::and_, n.index, { { n.fanin[0].inv ^ 1u, n.fanin[0].index }, { n.fanin[1].inv ^ 1u, n.fanin[1].index } } }; + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() ), { { 1, 0 }, { 1, n.index } } }; + aig_rule.insert( aig_rule.begin(), new_root ); + } + } + else if ( n.type == node_type::mux_ ) + { + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + dsd_node node_or = { node_type::and_, n.index + 2, { { 1, n.index }, { 1, n.index + 1 } } }; + dsd_node node_and1 = { node_type::and_, n.index + 1, { { 0, n.fanin[2].index }, { n.fanin[1].inv, n.fanin[1].index } } }; + new_node = { node_type::and_, n.index, { { 1, n.fanin[2].index }, { n.fanin[0].inv, n.fanin[0].index } } }; // and0_node + + // node already in aig_rule must have index and fanin index update (index += 2, fanin_index -> (>= n.index -> +2; nothing)) + for ( auto& elem : aig_rule ) + { + elem.index += 2; + for ( auto& s : elem.fanin ) + { + if ( s.index >= n.index ) + s.index += 2; + } + } + + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() + 2 ), { { 1, 0 }, { 1, static_cast( rule.size() ) + 1 } } }; + dsd_node node_or = { node_type::and_, static_cast( rule.size() + 1 ), { { 1, static_cast( rule.size() - 1 ) }, { 1, static_cast( rule.size() ) } } }; + dsd_node node_and1 = { node_type::and_, static_cast( rule.size() ), { { 0, n.fanin[2].index }, { n.fanin[1].inv, n.fanin[1].index } } }; + new_node = { node_type::and_, static_cast( rule.size() - 1 ), { { 1, n.fanin[2].index }, { n.fanin[0].inv, n.fanin[0].index } } }; // and0_node + + aig_rule.insert( aig_rule.begin(), new_root ); + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + } + else if ( n.type == node_type::xor_ ) + { + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + dsd_node node_or = { node_type::and_, n.index + 2, { { 1, n.index }, { 1, n.index + 1 } } }; + dsd_node node_and1 = { node_type::and_, n.index + 1, { { 0, n.fanin[0].index }, { 1, n.fanin[1].index } } }; + new_node = { node_type::and_, n.index, { { 1, n.fanin[0].index }, { 0, n.fanin[1].index } } }; // and0_node + + // node already in aig_rule must have index and fanin index update (index += 2, fanin_index -> (>= n.index -> +2; nothing)) + for ( auto& elem : aig_rule ) + { + elem.index += 2; + for ( auto& s : elem.fanin ) + { + if ( s.index >= n.index ) + s.index += 2; + } + } + + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() + 2 ), { { 1, 0 }, { 1, static_cast( rule.size() + 1 ) } } }; + dsd_node node_or = { node_type::and_, static_cast( rule.size() + 1 ), { { 1, static_cast( rule.size() - 1 ) }, { 1, static_cast( rule.size() ) } } }; + dsd_node node_and1 = { node_type::and_, static_cast( rule.size() ), { { 0, n.fanin[0].index }, { 1, n.fanin[1].index } } }; + new_node = { node_type::and_, static_cast( rule.size() - 1 ), { { 1, n.fanin[0].index }, { 0, n.fanin[1].index } } }; // and0_node + + aig_rule.insert( aig_rule.begin(), new_root ); + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + } + aig_rule.insert( aig_rule.begin(), new_node ); + } + return aig_rule; + } + + void swap( rule& rule, dsd_node* node_i, dsd_node* node_j ) + { + auto i = node_i->index; + auto j = node_j->index; + node_i->index = j; + node_j->index = i; + std::swap( rule[i], rule[j] ); + } + + /* makes left or right move to derive a new rule */ + void make_move( rule& rule, dsd_node* target, dsd_node* r, uint8_t left ) + { + auto targ_index = 0 + left; + auto r_index = 1 - targ_index; + auto temp_index = target->fanin[targ_index].index; + auto temp_inv = target->fanin[targ_index].inv; + // swap position internal to rule of the two elements + swap( rule, target, r ); + auto temp = target; + target = r; + r = temp; + + // adjust children + target->fanin[targ_index].index = r->index; + target->fanin[targ_index].inv = 0; + r->fanin[r_index].index = temp_index; + r->fanin[r_index].inv = temp_inv; + } + + /* checks whether a rule with the same left depth or right depth has already been encountered */ + bool check_depths( rule rule, dsd_node root, std::vector> depth_branches, uint32_t left ) // for left = 1 check if left move is possible + { + auto left_node = rule[root.fanin[0].index]; + auto right_node = rule[root.fanin[1].index]; + auto left_depth = get_depth( rule, left_node ); + auto right_depth = get_depth( rule, right_node ); + auto left_it1 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ left_depth - 1, right_depth + 1 } ); + auto left_it2 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ right_depth + 1, left_depth - 1 } ); + auto right_it1 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ left_depth + 1, right_depth - 1 } ); + auto right_it2 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ right_depth - 1, left_depth + 1 } ); + if ( left ) + return left_it1 == depth_branches.end() && left_it2 == depth_branches.end(); + else + return right_it1 == depth_branches.end() && right_it2 == depth_branches.end(); + } + + /*! \brief Recursively create new rules from the given one. + * For every dsd node of the original rule, left and right moves are tried. + * Then, the same algorithm is applied to all derived rules. + * The algorithm stops if no new acceptable rules can be found. + * A new rule is acceptable if no other rule with the same left and right depths has been found. + * \param new_rules vector of derived rules. + * \param rule original rule. + * \param start_node node of rule on which we try the right and left moves. + * \param depth_branches vector of encountered left and right depths. + * \param can_left specifies if we can perform a left move. + * \param can_right specifies if we can perform a right move. + */ + void create_rules_from_dsd( std::vector& new_rules, rule rule, dsd_node start_node, std::vector>& depth_branches, bool can_left, bool can_right ) + { + if ( start_node.type == node_type::pi_ || start_node.type == node_type::zero_ ) // if you cannot produce new rules or you are a PI return + return; + + std::vector left_rule( rule ); + std::vector right_rule( rule ); + std::vector> next_depths = {}; + auto left_node = &left_rule[start_node.fanin[0].index]; + auto right_node = &right_rule[start_node.fanin[1].index]; + bool new_left = false; + bool new_right = false; + + std::tuple depths = { get_depth( rule, *left_node ), get_depth( rule, *right_node ) }; + depth_branches.push_back( depths ); + + /* left move */ + if ( can_left && left_node->type == start_node.type && start_node.fanin[0].inv == 0 && check_depths( rule, start_node, depth_branches, 1 ) ) + { + auto r = &left_rule[start_node.index]; + + make_move( left_rule, left_node, r, 1 ); + + new_rules.push_back( left_rule ); + new_left = true; + depth_branches.push_back( { get_depth( left_rule, left_rule[left_rule[left_node->index].fanin[0].index] ), get_depth( left_rule, left_rule[left_rule[left_node->index].fanin[1].index] ) } ); + } + /* right move */ + if ( can_right && right_node->type == start_node.type && start_node.fanin[1].inv == 0 && check_depths( rule, start_node, depth_branches, 0 ) ) + { + auto r = &right_rule[start_node.index]; + + make_move( right_rule, right_node, r, 0 ); + + new_rules.push_back( right_rule ); + new_right = true; + depth_branches.push_back( { get_depth( right_rule, right_rule[right_rule[right_node->index].fanin[0].index] ), get_depth( right_rule, right_rule[right_rule[right_node->index].fanin[1].index] ) } ); + } + + /* initial rule, start_node left children */ + create_rules_from_dsd( new_rules, rule, rule[start_node.fanin[0].index], next_depths, true, true ); + /* initial rule, start_node right children */ + create_rules_from_dsd( new_rules, rule, rule[start_node.fanin[1].index], next_depths, true, true ); + /* left rule, start_node new root */ + if ( new_left ) + { + create_rules_from_dsd( new_rules, left_rule, left_rule[start_node.index], depth_branches, true, false ); + } + /* right rule, start_node new root */ + if ( new_right ) + { + create_rules_from_dsd( new_rules, right_rule, right_rule[start_node.index], depth_branches, false, true ); + } + } + + uint32_t compute_canonized_polarity( uint32_t polarity, uint32_t left_pi, uint32_t right_pi, uint32_t obs_pi ) + { + uint32_t mask_l = 0; + uint32_t mask_r = 0; + uint32_t mask_obs = 0; + for ( uint32_t i = 0; i < obs_pi; i++ ) + { + mask_obs |= ( 1 << i ); + } + for ( uint32_t i = obs_pi; i < obs_pi + left_pi; i++ ) + { + mask_l |= ( 1 << i ); + } + for ( uint32_t i = obs_pi + left_pi; i < obs_pi + left_pi + right_pi; i++ ) + { + mask_r |= ( 1 << i ); + } + return ( ( polarity & mask_l ) << right_pi ) | ( ( polarity & mask_r ) >> left_pi ) | ( polarity & mask_obs ); + } + + void compute_canonized_permutation( std::vector& perm, uint32_t left_pi, uint32_t right_pi, uint32_t obs_pi ) + { + std::vector copy( perm ); + for ( uint32_t i = obs_pi; i < obs_pi + right_pi; i++ ) + { + perm[i] = copy[( i + left_pi )]; + } + for ( uint32_t i = right_pi + obs_pi; i < perm.size(); i++ ) + { + perm[i] = copy[( i - right_pi )]; + } + } + + /*! \brief Recursively assigns indexes to a rule and its subrules and builds and_table. + * It also computes negations and permutations for the gate whose rule is being passed as parameter. + * + * \param r rule to index. + * \param n dsd node to start from. + * \param max max index assigned. + * \param polarity polarity of gate. + * \param perm permutation of gate. + * \param shift specifies the number of PIs encountered. + * Returns label to be assigned to gate whose rule is r. + */ + label do_indexing_rule( rule r, dsd_node n, uint32_t& max, uint32_t& polarity, std::vector& perm, uint32_t& shift ) + { + if ( n.type == node_type::pi_ ) + { + perm[shift] = n.index - 1; + return { 0, 1 }; + } + if ( n.type == node_type::zero_ ) + return { 0, 0 }; + + uint32_t obs_pi = shift; + + /* do indexing on the left */ + uint32_t left_index = do_indexing_rule( r, r[n.fanin[0].index], max, polarity, perm, shift ).index; + if ( r[n.fanin[0].index].type == node_type::pi_ ) + { + polarity |= ( n.fanin[0].inv << shift ); + shift++; + } + /* encountered PIs on the left */ + uint32_t left_pi = shift - obs_pi; + + /* do indexing on the right */ + uint32_t right_index = do_indexing_rule( r, r[n.fanin[1].index], max, polarity, perm, shift ).index; + if ( r[n.fanin[1].index].type == node_type::pi_ ) + { + polarity |= ( n.fanin[1].inv << shift ); + shift++; + } + /* encountered PIs on the right */ + uint32_t right_pi = shift - obs_pi - left_pi; + + /* check if it is inverted gate */ + if ( n.fanin[0].index == 0 && n.fanin[1].inv && n.index == r.size() - 1 ) + { + return { 1, right_index }; + } + signal left, right; + + /* ignore invertion of PIs */ + if ( r[n.fanin[0].index].type == node_type::pi_ ) + left.inv = 0; + else + left.inv = (uint64_t)n.fanin[0].inv; + left.index = left_index; + if ( r[n.fanin[1].index].type == node_type::pi_ ) + right.inv = 0; + else + right.inv = (uint64_t)n.fanin[1].inv; + right.index = right_index; + + std::tuple t; + + /* canonize and_table on left index being smaller than right one */ + if ( left.index <= right.index ) + t = std::make_tuple( left, right ); + else + { + /* new polarity */ + polarity = compute_canonized_polarity( polarity, left_pi, right_pi, obs_pi ); + + /* new permutation */ + compute_canonized_permutation( perm, left_pi, right_pi, obs_pi ); + + t = std::make_tuple( right, left ); + } + auto match = _and_table.find( t ); + if ( match != _and_table.end() ) + return { 0, match->second }; + max++; + /* insert new value in and_table */ + _and_table.insert( { t, max } ); + return { 0, max }; + } + + template + dsd_node is_top_dec( const TT& tt, uint32_t var_index, bool allow_xor = false, TT* func = nullptr ) + { + constexpr uint32_t kInvalidIndex = (1u << 31) - 1; // 0x7fffffff + + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + auto var = tt.construct(); + kitty::create_nth_var( var, var_index ); + + if ( kitty::implies( tt, var ) ) + { + if ( func ) + { + *func = kitty::cofactor1( tt, var_index ); + } + dsd_node res = { node_type::and_, var_index, {} }; + res.fanin.push_back( { 0, kInvalidIndex } ); + return res; + } + else if ( kitty::implies( var, tt ) ) + { + if ( func ) + { + *func = kitty::cofactor0( tt, var_index ); + } + dsd_node res = { node_type::or_, var_index, {} }; + res.fanin.push_back( { 0, kInvalidIndex } ); + return res; + } + else if ( kitty::implies( tt, ~var ) ) + { + if ( func ) + { + *func = kitty::cofactor0( tt, var_index ); + } + dsd_node res = { node_type::and_, var_index, {} }; + res.fanin.push_back( { 1, kInvalidIndex } ); + return res; + } + else if ( kitty::implies( ~var, tt ) ) + { + if ( func ) + { + *func = kitty::cofactor1( tt, var_index ); + } + dsd_node res = { node_type::or_, var_index, {} }; + res.fanin.push_back( { 1, kInvalidIndex } ); + return res; + } + + if ( allow_xor ) + { + /* try XOR */ + const auto co0 = kitty::cofactor0( tt, var_index ); + const auto co1 = kitty::cofactor1( tt, var_index ); + + if ( kitty::equal( co0, ~co1 ) ) + { + if ( func ) + { + *func = co0; + } + dsd_node res = { node_type::xor_, var_index, {} }; + res.fanin.push_back( { 0, kInvalidIndex } ); + return res; + } + } + + return { node_type::none, var_index, {} }; + } + + template + dsd_node is_bottom_dec( const TT& tt, uint32_t var_index1, uint32_t var_index2, TT* func = nullptr, uint32_t new_index = invalid_index, bool allow_xor = false ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = kitty::cofactor0( tt, var_index1 ); + const auto tt1 = kitty::cofactor1( tt, var_index1 ); + + const auto tt00 = kitty::cofactor0( tt0, var_index2 ); + const auto tt01 = kitty::cofactor1( tt0, var_index2 ); + const auto tt10 = kitty::cofactor0( tt1, var_index2 ); + const auto tt11 = kitty::cofactor1( tt1, var_index2 ); + + const auto eq01 = kitty::equal( tt00, tt01 ); + const auto eq02 = kitty::equal( tt00, tt10 ); + const auto eq03 = kitty::equal( tt00, tt11 ); + const auto eq12 = kitty::equal( tt01, tt10 ); + const auto eq13 = kitty::equal( tt01, tt11 ); + const auto eq23 = kitty::equal( tt10, tt11 ); + + const auto num_pairs = + static_cast( eq01 ) + + static_cast( eq02 ) + + static_cast( eq03 ) + + static_cast( eq12 ) + + static_cast( eq13 ) + + static_cast( eq23 ); + + if ( num_pairs != 2u && num_pairs != 3 ) + { + return { node_type::none, invalid_index, {} }; + } + + if ( !eq01 && !eq02 && !eq03 ) // 00 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt11, tt00 ); + } + dsd_node res = { node_type::or_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq01 && !eq12 && !eq13 ) // 01 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt10 ); + } + dsd_node res = { node_type::and_, new_index, {} }; + res.fanin.push_back( { 1, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq02 && !eq12 && !eq23 ) // 10 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt10 ); + } + dsd_node res = { node_type::or_, new_index, {} }; + res.fanin.push_back( { 1, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq03 && !eq13 && !eq23 ) // 11 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt11, tt00 ); + } + dsd_node res = { node_type::and_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( allow_xor ) // XOR + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt00 ); + } + dsd_node res = { node_type::xor_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + + return { node_type::none, invalid_index, {} }; + } + + template + uint32_t find_unate_var( const TT tt ) + { + for ( uint32_t index = 0; index < tt.num_vars() - 2; ++index ) + { + const auto tt0 = kitty::cofactor0( tt, index ); + const auto tt1 = kitty::cofactor1( tt, index ); + if ( ( ( tt0 & tt1 ) == tt0 ) && ( ( tt0 & tt1 ) == tt1 ) ) + return index; + } + + return tt.num_vars() - 1; + } + + template + dsd_node shannon_dec( const TT& tt, uint32_t index, TT* func0 = nullptr, TT* func1 = nullptr ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = kitty::cofactor0( tt, index ); + const auto tt1 = kitty::cofactor1( tt, index ); + + dsd_node res = { node_type::mux_, index, {} }; + res.fanin.push_back( { 0, index } ); + + if ( func0 && func1 ) + { + *func0 = tt0; + *func1 = tt1; + } + + return res; + } + + /*! \brief Get depth of rule starting from a specific dsd_node. + * + * \param rule rule + * \param n dsd_node to start from + * Returns depth of rule starting from n. + */ + uint32_t get_depth( rule rule, dsd_node n ) + { + if ( n.type == node_type::pi_ || n.type == node_type::zero_ ) + { + return 0; + } + uint32_t max_depth; + uint32_t left_depth = get_depth( rule, rule[n.fanin[0].index] ); + uint32_t right_depth = get_depth( rule, rule[n.fanin[1].index] ); + max_depth = ( left_depth > right_depth ) ? left_depth : right_depth; + return max_depth + 1; + } + + std::string to_string( node_type t ) + { + switch (t) { + case node_type::and_: + return "*"; + case node_type::or_: + return "+"; + case node_type::mux_: + return "+"; + case node_type::xor_: + return "xor"; + case node_type::pi_: + return "pi"; + case node_type::none: + return "none"; + case node_type::zero_: + return "zero"; + default: + return ""; + } + } + + void print_dsd_node( dsd_node& n ) + { + std::cout << n.index << " " << to_string( n.type ) << " "; + for ( auto elem : n.fanin ) + std::cout << "{" << elem.index << ", " << elem.inv << "}"; + std::cout << "\n"; + } + + void print_rule( rule& r ) + { + for ( auto elem : r ) + print_dsd_node( elem ); + } + + /*! \brief Print expression of a rule. + * + * \param rule rule. + * \param n dsd_node to start from. + */ + void print_rule( rule rule, dsd_node n ) + { + if ( n.type == node_type::pi_ ) + { + std::cout << char( 'a' + n.index ); + return; + } + if ( n.type == node_type::zero_ ) + { + std::cout << "0"; + return; + } + else + { + std::cout << "("; + if ( n.fanin[0].inv ) + { + std::cout << "!"; + } + if ( n.type == node_type::mux_ ) + { + std::cout << "!" << char( 'a' + n.fanin[2].index ) << " * "; + } + print_rule( rule, rule[n.fanin[0].index] ); + std::cout << " " << to_string( n.type ) << " "; + if ( n.type == node_type::mux_ ) + { + std::cout << char( 'a' + n.fanin[2].index ) << " * "; + } + if ( n.fanin[1].inv ) + { + std::cout << "!"; + } + print_rule( rule, rule[n.fanin[1].index] ); + std::cout << ")"; + } + } + +private: + bool gate_disjoint{ false }; /* flag for gate support*/ + uint32_t num_large_gates{ 0 }; + + std::vector const& _gates; /* collection of gates */ + struct_library_params const _ps; + + composed_list_t _supergates; /* list of composed_gates */ + lib_rule _dsd_map; /* hash map for DSD decomposition of gates */ + lib_table _and_table; /* AND table */ + map_label_gate _label_to_gate; /* map label to gate */ +}; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/super_utils.hpp b/third-party/mockturtle/include/mockturtle/utils/super_utils.hpp new file mode 100644 index 00000000000..21316f8c9e4 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/super_utils.hpp @@ -0,0 +1,464 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file super_utils.hpp + \brief Implements utilities to create supergates for technology mapping + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../io/genlib_reader.hpp" +#include "../io/super_reader.hpp" +#include "include/supergate.hpp" + +namespace mockturtle +{ + +struct super_utils_params +{ + /*! \brief load multi-output gates in simple supergates */ + bool load_multioutput_in_single{ false }; + + /*! \brief reports loaded supergates */ + bool verbose{ false }; +}; + +/*! \brief Utilities to generate supergates + * + * This class creates supergates starting from supergates + * specifications contained in `supergates_spec` extracted + * from a SUPER file. + * + * Multi-output gates are also extracted from the list of + * GENLIB gates. However multi-output gates are currently not + * supported as supergates members. + * + * This utility is called by `tech_library` to construct + * the library for technology mapping. + */ +template +class super_utils +{ +private: + static constexpr uint32_t truth_table_size = 6; + +public: + explicit super_utils( std::vector const& gates, super_lib const& supergates_spec = {}, super_utils_params const ps = {} ) + : _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _supergates(), + _multioutput_gates() + { + if ( _supergates_spec.supergates.size() == 0 ) + { + generate_library_with_genlib(); + } + else + { + generate_library_with_super(); + } + } + + /*! \brief Get the all the supergates. + * + * Returns a list of supergates created accordingly to + * the standard library and the supergates specifications. + */ + const std::deque>& get_super_library() const + { + return _supergates; + } + + /*! \brief Get the number of standard gates. + * + * Returns the number of standard gates contained in the + * supergate library. + */ + const uint32_t get_standard_library_size() const + { + return simple_gates_size; + } + + /*! \brief Get multi-output gates. + * + * Returns a list of multioutput gates. + */ + const std::vector>>& get_multioutput_library() const + { + return _multioutput_gates; + } + +public: + void generate_library_with_genlib() + { + uint32_t initial_size = _supergates.size(); + + std::unordered_map multioutput_map; + std::unordered_map multioutput_idx; + multioutput_map.reserve( _gates.size() ); + + /* look for multi-output gates (gates with the same name) */ + uint32_t multioutput_i = 0; + for ( const auto& g : _gates ) + { + if ( multioutput_map.find( g.name ) != multioutput_map.end() ) + { + /* assign an index */ + if ( multioutput_map[g.name] == 1 ) + multioutput_idx[g.name] = multioutput_i++; + + multioutput_map[g.name] += 1; + } + else + { + multioutput_map[g.name] = 1; + multioutput_idx[g.name] = UINT32_MAX; + } + } + + /* create composed gates */ + uint32_t ignored = 0; + uint32_t ignored_id = 0; + uint32_t large_gates = 0; + for ( const auto& g : _gates ) + { + std::array pin_to_pin_delays{}; + + if ( g.function.num_vars() > NInputs ) + { + ++ignored; + ignored_id = g.id; + continue; + } + if ( g.function.num_vars() > truth_table_size ) + { + ++large_gates; + continue; + } + + auto i = 0u; + for ( auto const& pin : g.pins ) + { + /* use worst pin delay */ + pin_to_pin_delays[i++] = std::max( pin.rise_block_delay, pin.fall_block_delay ); + } + + if ( multioutput_map[g.name] == 1 || _ps.load_multioutput_in_single ) + { + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + + if ( multioutput_map[g.name] > 1 ) + { + uint32_t idx = multioutput_idx[g.name]; + if ( _multioutput_gates.size() <= idx ) + _multioutput_gates.emplace_back( std::vector>() ); + + _multioutput_gates[multioutput_idx[g.name]].emplace_back( + composed_gate{ static_cast( idx ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + } + + simple_gates_size = _supergates.size() - initial_size; + + if ( _ps.verbose ) + { + std::cout << fmt::format( "[i] Loading {} simple library cells\n", simple_gates_size + large_gates ); + std::cout << fmt::format( "[i] Loading {} multi-output library cells\n", _multioutput_gates.size() ); + } + + if ( ignored > 0 ) + { + std::cerr << fmt::format( "[i] WARNING: {} gates IGNORED (e.g., {}), too many inputs for the library settings\n", ignored, _gates[ignored_id].name ); + } + } + + void generate_library_with_super() + { + if ( _supergates_spec.max_num_vars > NInputs || _supergates_spec.max_num_vars > truth_table_size ) + { + std::cerr << fmt::format( + "[e] ERROR: NInputs ({}) should be greater or equal than the max number of variables ({}) in the super file.\n", NInputs, _supergates_spec.max_num_vars ); + std::cerr << "[i] WARNING: ignoring supergates, proceeding with standard library." << std::endl; + generate_library_with_genlib(); + return; + } + + /* create a map for the gates IDs */ + std::unordered_map gates_map; + + for ( auto const& g : _gates ) + { + if ( gates_map.find( g.name ) != gates_map.end() ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring genlib gate {}, duplicated name entry in supergates.", g.name ) << std::endl; + } + else + { + gates_map[g.name] = g.id; + } + } + + /* creating input variables */ + for ( uint8_t i = 0; i < _supergates_spec.max_num_vars; ++i ) + { + kitty::dynamic_truth_table tt{ NInputs }; + kitty::create_nth_var( tt, i ); + + _supergates.emplace_back( composed_gate{ static_cast( i ), + false, + nullptr, + 0, + tt, + 0.0, + {}, + {} } ); + } + + generate_library_with_genlib(); + + uint32_t super_count = 0; + + /* add supergates */ + for ( auto const& g : _supergates_spec.supergates ) + { + uint32_t root_match_id; + if ( auto it = gates_map.find( g.name ); it != gates_map.end() ) + { + root_match_id = it->second; + } + else + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, no reference in genlib.", g.id ) << std::endl; + continue; + } + + uint32_t num_vars = _gates[root_match_id].num_vars; + + if ( num_vars != g.fanin_id.size() ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, wrong number of fanins.", g.id ) << std::endl; + continue; + } + if ( num_vars > _supergates_spec.max_num_vars ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, too many variables for the library settings.", g.id ) << std::endl; + continue; + } + + std::vector*> sub_gates; + + bool error = false; + bool simple_gate = true; + for ( uint32_t f : g.fanin_id ) + { + if ( f >= g.id + _supergates_spec.max_num_vars ) + { + error = true; + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, wrong fanins.", g.id ) << std::endl; + } + if ( f < _supergates_spec.max_num_vars ) + { + sub_gates.emplace_back( &_supergates[f] ); + } + else + { + sub_gates.emplace_back( &_supergates[f + simple_gates_size] ); + simple_gate = false; + } + } + + if ( error ) + { + continue; + } + + /* force at `is_super = false` simple gates considered as supergates. + * This is necessary to not have duplicates since tech_library + * computes indipendently the permutations for simple gates. + * Moreover simple gates permutations could be incomplete in SUPER + * libraries which are constrained by the number of gates. */ + bool is_super_verified = g.is_super; + if ( simple_gate ) + { + is_super_verified = false; + } + + float area = compute_area( root_match_id, sub_gates ); + const kitty::dynamic_truth_table tt = compute_truth_table( root_match_id, sub_gates ); + + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + is_super_verified, + &_gates[root_match_id], + 0, + tt, + area, + {}, + sub_gates } ); + + if ( g.is_super ) + { + ++super_count; + } + + auto& s = _supergates[_supergates.size() - 1]; + s.num_vars = compute_support( s ); + compute_delay_parameters( s ); + } + + /* minimize supergates */ + for ( auto& g : _supergates ) + { + if ( g.is_super ) + { + g.function = shrink_to( g.function, static_cast( g.num_vars ) ); + } + } + + if ( _ps.verbose ) + { + std::cout << fmt::format( "[i] Loaded {} supergates in the library\n", super_count ); + } + } + +private: + inline float compute_area( uint32_t root_id, std::vector*> const& sub_gates ) + { + float area = _gates[root_id].area; + for ( auto const f : sub_gates ) + { + area += f->area; + } + + return area; + } + + inline uint32_t compute_support( composed_gate& s ) + { + std::array used_pins{}; + + return compute_support_rec( s, used_pins ); + } + + uint32_t compute_support_rec( composed_gate& s, std::array& used_pins ) + { + /* termination: input variable */ + if ( s.root == nullptr ) + { + if ( used_pins[s.id]++ == 0u ) + { + return 1; + } + return 0; + } + + uint32_t support = 0; + for ( auto const pin : s.fanin ) + { + support += compute_support_rec( *pin, used_pins ); + } + return support; + } + + inline kitty::dynamic_truth_table compute_truth_table( uint32_t root_id, std::vector*> const& sub_gates ) + { + std::vector ttv; + + for ( auto const f : sub_gates ) + { + ttv.emplace_back( f->function ); + } + + return kitty::compose_truth_table( _gates[root_id].function, ttv ); + } + + inline void compute_delay_parameters( composed_gate& s ) + { + const auto& root = *( s.root ); + + auto i = 0u; + for ( auto const& pin : root.pins ) + { + float worst_delay = std::max( pin.rise_block_delay, pin.fall_block_delay ); + + compute_delay_pin_rec( s, *( s.fanin[i++] ), worst_delay ); + } + } + + void compute_delay_pin_rec( composed_gate& root, composed_gate& s, float delay ) + { + /* termination: input variable */ + if ( s.root == nullptr ) + { + root.tdelay[s.id] = std::max( root.tdelay[s.id], delay ); + return; + } + + auto i = 0u; + for ( auto const& pin : s.root->pins ) + { + float worst_delay = delay + std::max( pin.rise_block_delay, pin.fall_block_delay ); + + compute_delay_pin_rec( root, *( s.fanin[i++] ), worst_delay ); + } + } + +protected: + uint32_t simple_gates_size{ 0 }; + + std::vector const& _gates; + super_lib const& _supergates_spec; + super_utils_params const _ps; + std::deque> _supergates; + std::vector>> _multioutput_gates; +}; /* class super_utils */ + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/utils/tech_library.hpp b/third-party/mockturtle/include/mockturtle/utils/tech_library.hpp new file mode 100644 index 00000000000..b3fa2e0098d --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/tech_library.hpp @@ -0,0 +1,1683 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file tech_library.hpp + \brief Implements utilities to enumerates gates for technology mapping + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" + +#include "../io/genlib_reader.hpp" +#include "../io/super_reader.hpp" +#include "include/supergate.hpp" +#include "standard_cell.hpp" +#include "struct_library.hpp" +#include "super_utils.hpp" + +namespace mockturtle +{ + +/* +std::string const mcnc_library = "GATE inv1 1 O=!a; PIN * INV 1 999 0.9 0.3 0.9 0.3\n" + "GATE inv2 2 O=!a; PIN * INV 2 999 1.0 0.1 1.0 0.1\n" + "GATE inv3 3 O=!a; PIN * INV 3 999 1.1 0.09 1.1 0.09\n" + "GATE inv4 4 O=!a; PIN * INV 4 999 1.2 0.07 1.2 0.07\n" + "GATE nand2 2 O=!(a*b); PIN * INV 1 999 1.0 0.2 1.0 0.2\n" + "GATE nand3 3 O=!(a*b*c); PIN * INV 1 999 1.1 0.3 1.1 0.3\n" + "GATE nand4 4 O=!(a*b*c*d); PIN * INV 1 999 1.4 0.4 1.4 0.4\n" + "GATE nor2 2 O=!(a+b); PIN * INV 1 999 1.4 0.5 1.4 0.5\n" + "GATE nor3 3 O=!(a+b+c); PIN * INV 1 999 2.4 0.7 2.4 0.7\n" + "GATE nor4 4 O=!(a+b+c+d); PIN * INV 1 999 3.8 1.0 3.8 1.0\n" + "GATE and2 3 O=a*b; PIN * NONINV 1 999 1.9 0.3 1.9 0.3\n" + "GATE or2 3 O=a+b; PIN * NONINV 1 999 2.4 0.3 2.4 0.3\n" + "GATE xor2a 5 O=a*!b+!a*b; PIN * UNKNOWN 2 999 1.9 0.5 1.9 0.5\n" + "#GATE xor2b 5 O=!(a*b+!a*!b); PIN * UNKNOWN 2 999 1.9 0.5 1.9 0.5\n" + "GATE xnor2a 5 O=a*b+!a*!b; PIN * UNKNOWN 2 999 2.1 0.5 2.1 0.5\n" + "#GATE xnor2b 5 O=!(a*!b+!a*b); PIN * UNKNOWN 2 999 2.1 0.5 2.1 0.5\n" + "GATE aoi21 3 O=!(a*b+c); PIN * INV 1 999 1.6 0.4 1.6 0.4\n" + "GATE aoi22 4 O=!(a*b+c*d); PIN * INV 1 999 2.0 0.4 2.0 0.4\n" + "GATE oai21 3 O=!((a+b)*c); PIN * INV 1 999 1.6 0.4 1.6 0.4\n" + "GATE oai22 4 O=!((a+b)*(c+d)); PIN * INV 1 999 2.0 0.4 2.0 0.4\n" + "GATE buf 2 O=a; PIN * NONINV 1 999 1.0 0.0 1.0 0.0\n" + "GATE zero 0 O=CONST0;\n" + "GATE one 0 O=CONST1;"; +*/ + +enum class classification_type : uint32_t +{ + /*! \brief generate the NP configurations (n! * 2^n) + * Direct matching: best up to ~200 library gates */ + np_configurations = 0, + + /*! \brief generate the P configurations (n!) + * Matching by N-canonization: best for more + * than ~200 library gates */ + p_configurations = 1, + + /*! \brief generate the n configurations (2^n) + * Direct fast matching, less quality */ + n_configurations = 2, +}; + +struct tech_library_params +{ + /*! \brief Load large gates with more than 6 inputs */ + bool load_large_gates{ true }; + + /*! \brief Loads multioutput gates in the library */ + bool load_multioutput_gates{ true }; + + /*! \brief Don't load symmetrical permutations of gate pins (drastically speeds-up mapping) */ + bool ignore_symmetries{ false }; + + /*! \brief Load gates with minimum size only */ + bool load_minimum_size_only{ true }; + + /*! \brief Remove dominated gates (larger sizes) */ + bool remove_dominated_gates{ true }; + + /*! \brief Loads multioutput gates in single-output library */ + bool load_multioutput_gates_single{ false }; + + /*! \brief reports np enumerations */ + bool verbose{ false }; + + /*! \brief reports all the entries in the library */ + bool very_verbose{ false }; +}; + +namespace detail +{ + +template +struct tuple_tt_hash; + +template +struct tuple_tt_hash +{ + inline std::size_t operator()( std::array, NumOutputs> const& tts ) const + { + std::size_t seed = kitty::hash_block( tts[0]._bits ); + + for ( auto i = 1; i < NumOutputs; ++i ) { + kitty::hash_combine( seed, kitty::hash_block( tts[i]._bits ) ); + } + + return seed; + } +}; + +template +struct tuple_tt_hash +{ + inline std::size_t operator()( std::array, NumOutputs> const& tts ) const + { + std::size_t seed = kitty::hash_block( tts[0]._bits[0] ); + + for ( auto i = 0; i < NumOutputs; ++i ) { + for (auto j = 0; j < tts[i]._bits.size(); ++j) { + if (i == 0 && j == 0) { + continue; + } + kitty::hash_combine( seed, kitty::hash_block( tts[i]._bits[j] ) ); + } + } + + return seed; + } +}; + +} // namespace detail + +/*! \brief Library of gates for Boolean matching + * + * This class creates a technology library from a set + * of input gates. Each NP- or P-configuration of the gates + * are enumerated and inserted in the library. + * + * The configuration is selected using the template + * parameter `Configuration`. P-configuration is suggested + * for big libraries with few symmetric gates. The template + * parameter `NInputs` selects the maximum number of variables + * allowed for a gate in the library. + * + * The library can be generated also using supergates definitions. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + // standard library + mockturtle::tech_library lib( gates ); + + super_lib supergates_spec; + lorina::read_super( "file.super", super_reader( supergates_spec ) ); + // library with supergates + mockturtle::tech_library lib_super( gates, supergates_spec ); + \endverbatim + */ +template +class tech_library +{ +private: + static constexpr float epsilon = 0.0005; + static constexpr uint32_t max_multi_outputs = 2; + using supergates_list_t = std::vector>; + using TT = kitty::static_truth_table; + using tt_hash = kitty::hash; + using multi_tt_hash = detail::tuple_tt_hash; + using index_t = absl::flat_hash_map; + using lib_t = absl::flat_hash_map; + using multi_relation_t = std::array; + using multi_supergates_list_t = std::array>, max_multi_outputs>; + using multi_lib_t = absl::flat_hash_map; + using multi_func_t = absl::flat_hash_map; + using struct_lib_t = absl::flat_hash_map; + +public: + explicit tech_library( std::vector const& gates, tech_library_params const ps = {}, super_lib const& supergates_spec = {} ) + : _use_supergates( false ), + _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _cells( get_standard_cells( _gates ) ), + _super( _gates, _supergates_spec, super_utils_params{ ps.load_multioutput_gates_single, ps.verbose } ), + _struct( _gates, struct_library_params{ ps.load_minimum_size_only, ps.very_verbose } ), + _super_lib(), + _multi_lib(), + _struct_lib() + { + static_assert( NInputs < 16, "The technology library database supports NInputs up to 15\n" ); + + generate_library(); + + if ( ps.load_multioutput_gates ) + generate_multioutput_library(); + + if ( ps.load_large_gates ) + { + _struct.construct( 2 ); + } + } + + explicit tech_library( std::vector const& gates, super_lib const& supergates_spec, tech_library_params const ps = {} ) + : _use_supergates( true ), + _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _cells( get_standard_cells( _gates ) ), + _super( _gates, _supergates_spec, super_utils_params{ ps.load_multioutput_gates_single, ps.verbose } ), + _struct( _gates, struct_library_params{ ps.load_minimum_size_only, ps.very_verbose } ), + _super_lib(), + _multi_lib(), + _struct_lib() + { + static_assert( NInputs < 16, "The technology library database supports NInputs up to 15\n" ); + + generate_library(); + + if ( ps.load_multioutput_gates ) + generate_multioutput_library(); + + if ( ps.load_large_gates ) + { + _struct.construct( 2 ); + } + } + + tech_library ( const tech_library& ) = delete; + tech_library& operator=( const tech_library& ) = delete; + + /*! \brief Get the gates matching the function. + * + * Returns a list of gates that match the function represented + * by the truth table. + */ + const supergates_list_t* get_supergates( TT const& tt ) const + { + auto match = _super_lib.find( tt ); + if ( match != _super_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the multi-output gates matching the function. + * + * Returns a list of multi-output gates that match the function + * represented by the truth table. + */ + const multi_supergates_list_t* get_multi_supergates( std::array const& tts ) const + { + auto match = _multi_lib.find( tts ); + if ( match != _multi_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the multi-output gate function ID for a single output. + * + * Returns the function ID of a multi-output gate output if matched. This function + * supports up to 6 inputs. Returns zero in case of no match. + */ + uint64_t get_multi_function_id( uint64_t const& tt ) const + { + auto match = _multi_funcs.find( tt ); + if ( match != _multi_funcs.end() ) + return match->second; + return 0; + } + + /*! \brief Get the pattern ID for structural matching. + * + * Returns a pattern ID if found, UINT32_MAX otherwise given the + * children IDs. This function works with only AND operators. + */ + uint32_t get_pattern_id( uint32_t id1, uint32_t id2 ) const + { + return _struct.get_pattern_id( id1, id2 ); + } + + /*! \brief Get the gates matching the pattern ID and phase. + * + * Returns a list of gates that match the pattern ID and the given polarity. + */ + const supergates_list_t* get_supergates_pattern( uint32_t id, bool phase ) const + { + return _struct.get_supergates_pattern( id, phase ); + } + + /*! \brief Get inverter information. + * + * Returns area, delay, and ID of the smallest inverter. + */ + const std::tuple get_inverter_info() const + { + return std::make_tuple( _inv_area, _inv_delay, _inv_id ); + } + + /*! \brief Get buffer information. + * + * Returns area, delay, and ID of the smallest buffer. + */ + const std::tuple get_buffer_info() const + { + return std::make_tuple( _buf_area, _buf_delay, _buf_id ); + } + + /*! \brief Returns the maximum number of variables of the gates. */ + unsigned max_gate_size() + { + return _max_size; + } + + /*! \brief Returns the original gates. */ + const std::vector get_gates() const + { + return _gates; + } + + /*! \brief Returns the standard cells. */ + const std::vector& get_cells() const + { + return _cells; + } + + /*! \brief Returns multioutput gates. */ + const std::vector>>& get_multioutput_gates() const + { + return _super.get_multioutput_library(); + } + + /*! \brief Returns the number of multi-output gates loaded in the library. */ + const uint32_t num_multioutput_gates() const + { + if ( !_ps.load_multioutput_gates ) + return 0; + return _multi_lib.size(); + } + + /*! \brief Returns the number of gates for structural matching. */ + const uint32_t num_structural_gates() const + { + return _struct.get_struct_library().size(); + } + + /*! \brief Returns the number of gates for structural matching with more than 6 inputs. */ + const uint32_t num_structural_large_gates() const + { + return _struct.get_struct_library().size(); + } + +private: + void generate_library() + { + bool inv = false; + bool buf = false; + + /* extract the smallest inverter and buffer info */ + for ( auto& gate : _gates ) + { + if ( gate.function.num_vars() == 1 ) + { + /* extract inverter delay and area */ + if ( kitty::is_const0( kitty::cofactor1( gate.function, 0 ) ) ) + { + /* get the smallest area inverter */ + if ( !inv || gate.area < _inv_area - epsilon ) + { + _inv_area = gate.area; + _inv_delay = compute_worst_delay( gate ); + _inv_id = gate.id; + inv = true; + } + } + else + { + /* get the smallest area buffer */ + if ( !buf || gate.area < _buf_area - epsilon ) + { + _buf_area = gate.area; + _buf_delay = compute_worst_delay( gate ); + _buf_id = gate.id; + buf = true; + } + } + } + } + + auto const& supergates = _super.get_super_library(); + uint32_t const standard_gate_size = _super.get_standard_library_size(); + + std::vector skip_gates( supergates.size(), false ); + + if ( _ps.load_minimum_size_only || _ps.remove_dominated_gates ) + { + filter_gates( supergates, skip_gates ); + } + + /* generate the configurations for the standard gates */ + uint32_t i = 0u; + uint32_t skip_count = 0; + for ( auto const& gate : supergates ) + { + uint32_t np_count = 0; + + if ( skip_gates[skip_count++] ) + { + /* exclude gate */ + ++i; + continue; + } + + if ( gate.root == nullptr ) + { + /* exclude PIs */ + continue; + } + + _max_size = std::max( _max_size, gate.num_vars ); + + if ( i++ < standard_gate_size ) + { + const auto on_np = [&]( auto const& tt, auto neg, auto const& perm ) { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + 0 }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + sg.polarity |= ( ( neg >> perm[i] ) & 1 ) << i; /* permutate input negation to match the right pin */ + } + + const auto static_tt = kitty::extend_to( tt ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && ( _ps.ignore_symmetries || sg.tdelay == it->tdelay ) ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + }; + + const auto on_p = [&]( auto const& tt, auto const& perm ) { + /* get all the configurations that lead to the N-class representative */ + auto [tt_canon, phases] = kitty::exact_n_canonization_complete( tt ); + + for ( auto phase : phases ) + { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( phase ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt_canon ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && ( _ps.ignore_symmetries || sg.tdelay == it->tdelay ) ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + } + }; + + if constexpr ( Configuration == classification_type::np_configurations ) + { + /* NP enumeration of the function */ + const auto tt = gate.function; + kitty::exact_np_enumeration( tt, on_np ); + } + else if ( Configuration == classification_type::n_configurations ) + { + /* N enumeration of the function */ + const auto tt = gate.function; + std::vector pin_order( tt.num_vars() ); + std::iota( pin_order.begin(), pin_order.end(), 0 ); + kitty::exact_n_enumeration( tt, [&]( auto const& tt, auto neg ) { on_np( tt, neg, pin_order ); } ); + } + else + { + /* P enumeration followed by N canonization of the function */ + const auto tt = gate.function; + kitty::exact_p_enumeration( tt, on_p ); + } + } + else + { + /* process the supergates */ + if ( !gate.is_super ) + { + /* ignore simple gates */ + continue; + } + + const auto on_np = [&]( auto const& tt, auto neg ) { + std::vector perm( gate.num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( neg ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && sg.tdelay == it->tdelay ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + }; + + const auto on_p = [&]() { + auto [tt_canon, phases] = kitty::exact_n_canonization_complete( gate.function ); + std::vector perm( gate.num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto phase : phases ) + { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( phase ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt_canon ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && sg.tdelay == it->tdelay ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + } + }; + + if constexpr ( Configuration == classification_type::np_configurations ) + { + /* N enumeration of the function */ + const auto tt = gate.function; + kitty::exact_n_enumeration( tt, on_np ); + } + else + { + /* N canonization of the function */ + const auto tt = gate.function; + on_p(); + } + } + + if ( _ps.very_verbose ) + { + std::cout << "Gate " << gate.root->name << ", num_vars = " << gate.num_vars << ", np entries = " << np_count << std::endl; + } + } + + if ( !inv ) + { + std::cerr << "[i] WARNING: inverter gate has not been detected in the library" << std::endl; + } + + if ( !buf ) + { + std::cerr << "[i] WARNING: buffer gate has not been detected in the library" << std::endl; + } + + if ( _ps.very_verbose ) + { + for ( auto const& entry : _super_lib ) + { + kitty::print_hex( entry.first ); + std::cout << ": "; + for ( auto const& gate : entry.second ) + { + printf( "%d(a:%.2f, p:%d) ", gate.root->id, gate.area, gate.polarity ); + } + std::cout << std::endl; + } + } + } + + /* Supports only NP configurations */ + void generate_multioutput_library() + { + uint32_t np_count = 0; + std::string ignored_name; + bool consistency_check = true; + + /* load multi-output gates */ + auto const& multioutput_gates = _super.get_multioutput_library(); + + uint32_t ignored_gates = 0; + for ( auto const& multi_gate : multioutput_gates ) + { + /* select the on up to max_multi_outputs outputs */ + if ( multi_gate.size() > max_multi_outputs ) + { + ignored_name = multi_gate[0].root->name; + ++ignored_gates; + continue; + } + + std::array order = { 0 }; + + const auto on_np = [&]( auto const& tts, auto neg, auto const& perm ) { + std::vector> multi_sg; + + for ( auto const& gate : multi_gate ) + { + multi_sg.emplace_back( supergate{ &gate, + static_cast( gate.area ), + {}, + perm, + 0 } ); + } + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + uint32_t j = 0; + for ( auto& sg : multi_sg ) + { + sg.tdelay[i] = multi_gate[j++].tdelay[perm[i]]; + sg.polarity |= ( ( neg >> perm[i] ) & 1 ) << i; /* permutate input negation to match the right pin */ + } + } + + std::array static_tts = {}; + std::array sorted_tts = {}; + + /* canonize output */ + for ( auto i = 0; i < tts.size(); ++i ) + { + static_tts[i] = kitty::extend_to( tts[i] ); + if constexpr (NInputs <= 6) { + if ( ( tts[i]._bits & 1 ) == 1 ) + { + static_tts[i] = ~static_tts[i]; + multi_sg[i].polarity |= 1 << NInputs; /* set flipped output polarity*/ + } + } else { + if ( ( tts[i]._bits[0] & 1 ) == 1 ) + { + static_tts[i] = ~static_tts[i]; + multi_sg[i].polarity |= 1 << NInputs; /* set flipped output polarity*/ + } + } + } + + std::iota( order.begin(), order.end(), 0 ); + + std::stable_sort( order.begin(), order.end(), [&]( size_t a, size_t b ) { + return static_tts[a] < static_tts[b]; + } ); + + std::transform( order.begin(), order.end(), sorted_tts.begin(), [&]( size_t a ) { + return static_tts[a]; + } ); + + // std::stable_sort( static_tts.begin(), static_tts.end() ); + + auto& v = _multi_lib[sorted_tts]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v[0].begin(), v[0].end(), multi_sg[0], [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated elements due to symmetries */ + while ( it != v[0].end() ) + { + /* if different gate, exit */ + if ( multi_sg[0].root->id != it->root->id ) + break; + + /* if already in the library, exit */ + if ( multi_sg[order[0]].polarity != it->polarity ) + { + ++it; + continue; + } + + bool same_delay = true; + size_t d = std::distance( v[0].begin(), it ); + for ( auto i = 0; i < multi_sg.size(); ++i ) + { + if ( multi_sg[order[i]].tdelay != v[i][d].tdelay ) + { + same_delay = false; + break; + } + } + + /* do not add if equivalent to another in the library */ + if ( same_delay ) + { + to_add = false; + break; + } + + ++it; + } + + if ( to_add ) + { + size_t d = std::distance( v[0].begin(), it ); + for ( auto i = 0; i < multi_sg.size(); ++i ) + { + v[i].insert( v[i].begin() + d, multi_sg[order[i]] ); + } + ++np_count; + } + }; + + /* NP enumeration of the function */ + std::vector tts; + for ( auto gate : multi_gate ) + tts.push_back( gate.function ); + kitty::exact_multi_np_enumeration( tts, on_np ); + + /* NPN enumeration of the single outputs */ + uint32_t pin = 0; + for ( auto const& gate : multi_gate ) + { + consistency_check &= check_delay_consistency( gate, pin++ ); + exact_npn_enumeration( gate.function, [&]( auto const& tt, auto neg, auto const& perm ) { + (void)neg; + (void)perm; + _multi_funcs[tt._bits[0]] = gate.function._bits[0]; + } ); + } + } + + /* update area based on the single output contribution */ + multi_update_area(); + + if ( _ps.verbose && ignored_gates > 0 ) + { + std::cerr << fmt::format( "[i] WARNING: {} multi-output gates IGNORED (e.g., {}), too many outputs for the library settings\n", ignored_gates, ignored_name ); + } + + if ( !consistency_check ) + { + std::cerr << "[i] WARNING: technology mapping using multi-output cells with warnings might generate required time violations or circuits with dangling pins\n"; + } + + // std::cout << _multi_lib.size() << "\n"; + } + + void multi_update_area() + { + /* update area for each sub-function in a multi-output gate with their contribution */ + for ( auto& pair : _multi_lib ) + { + auto& multi_gates = pair.second; + for ( auto i = 0; i < multi_gates[0].size(); ++i ) + { + /* get sum of area and area count */ + double area = 0; + uint32_t contribution_count = 0; + std::array area_contribution = { 0 }; + for ( auto j = 0; j < max_multi_outputs; ++j ) + { + auto& gate = multi_gates[j][i]; + const TT tt = kitty::extend_to( gate.root->function ); + + /* get the area of the smallest match with a simple gate */ + const auto match = get_supergates( tt ); + if ( match == nullptr ) + continue; + + area_contribution[j] = ( *match )[0].area; + area += area_contribution[j]; + ++contribution_count; + + // std::cout << fmt::format( "Contribution {}\t = {}\n", ( *match )[0].root->root->name, area_contribution[j] ); + } + + /* compute scaling factor and remaining area for non-matched gates */ + double scaling_factor = 1.0; + double remaining_area = 0; + + if ( contribution_count != max_multi_outputs ) + { + scaling_factor = 0.9; + + if ( area > multi_gates[0][i].area ) + scaling_factor -= ( area - multi_gates[0][i].area ) / area; + + remaining_area = ( multi_gates[0][i].area - area * scaling_factor ); + area = area * scaling_factor + remaining_area; + remaining_area /= ( max_multi_outputs - contribution_count ); + } + + /* assign weighted contribution */ + // double area_old = multi_gates[0][i].area; + // double area_check = 0; + for ( auto j = 0; j < max_multi_outputs; ++j ) + { + auto& gate = multi_gates[j][i]; + + if ( area_contribution[j] > 0 ) + gate.area = scaling_factor * area_contribution[j] * gate.area / area; + else + gate.area = remaining_area; + + // area_check += gate.area; + } + + // std::cout << fmt::format( "Area before: {}\t Area after {}\n", area_old, area_check ); + } + } + } + + bool check_delay_consistency( composed_gate const& g, uint32_t pin ) + { + TT tt = kitty::extend_to( g.function ); + uint16_t polarity = 0; + + /* canonicalize in case of P-configurations */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( tt, g.num_vars ); + tt = std::get<0>( canon ); + polarity = static_cast( std::get<1>( canon ) ); + } + + auto entry = _super_lib.find( tt ); + if ( entry == _super_lib.end() ) + { + std::cerr << fmt::format( "[i] WARNING: library does not contain cells that can implement output pin {} of the multi-output cell {}\n", pin, g.root->name ); + return false; + } + + /* check delay (at least one entry must have better or equal delay) */ + for ( auto const& sg : entry->second ) + { + bool valid = true; + for ( uint32_t i = 0; i < g.num_vars; ++i ) + { + float pin_delay = sg.tdelay[i]; + if ( ( sg.polarity >> i ) & 1 ) + pin_delay += _inv_delay; + + float mo_pin_delay = g.tdelay[i]; + if ( ( polarity >> i ) & 1 ) + mo_pin_delay += _inv_delay; + + if ( pin_delay > mo_pin_delay ) + { + valid = false; + break; + } + } + + if ( valid ) + { + return true; + } + } + + std::cerr << fmt::format( "[i] WARNING: library does not contain cells that could match the delay of output pin {} of multi-output cell {}\n", pin, g.root->name ); + return false; + } + + float compute_worst_delay( gate const& g ) + { + float worst_delay = 0.0f; + + /* consider only block_delay */ + for ( auto const& pin : g.pins ) + { + float worst_pin_delay = static_cast( std::max( pin.rise_block_delay, pin.fall_block_delay ) ); + worst_delay = std::max( worst_delay, worst_pin_delay ); + } + return worst_delay; + } + + bool compare_sizes( composed_gate const& s1, composed_gate const& s2 ) + { + if ( s1.area < s2.area ) + return true; + else if ( s1.area > s2.area ) + return false; + + /* compute average pin delay */ + float s1_delay = 0, s2_delay = 0; + assert( s1.num_vars == s2.num_vars ); + for ( uint32_t i = 0; i < s1.num_vars; ++i ) + { + s1_delay += s1.tdelay[i]; + s2_delay += s2.tdelay[i]; + } + + if ( s1_delay < s2_delay ) + return true; + else if ( s1_delay > s2_delay ) + return false; + else if ( s1.root->name < s2.root->name ) + return true; + + return false; + } + + void filter_gates( std::deque> const& supergates, std::vector& skip_gates ) + { + assert( supergates.size() >= skip_gates.size() ); + for ( uint32_t i = 0; i < skip_gates.size() - 1; ++i ) + { + if ( supergates[i].root == nullptr ) + continue; + + if ( skip_gates[i] ) + continue; + + auto const& tti = supergates[i].function; + for ( uint32_t j = i + 1; j < skip_gates.size(); ++j ) + { + auto const& ttj = supergates[j].function; + + /* get the same functionality */ + if ( skip_gates[j] || tti != ttj ) + continue; + + if ( _ps.load_minimum_size_only ) + { + if ( compare_sizes( supergates[i], supergates[j] ) ) + { + skip_gates[j] = true; + continue; + } + else + { + skip_gates[i] = true; + break; + } + } + + /* is i smaller than j */ + bool smaller = supergates[i].area <= supergates[j].area; + + /* is i faster for every pin */ + bool faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( supergates[i].tdelay[k] > supergates[j].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[j] = true; + continue; + } + + /* is j faster for every pin */ + smaller = supergates[i].area >= supergates[j].area; + faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( supergates[j].tdelay[k] > supergates[i].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[i] = true; + break; + } + } + } + } + +private: + /* inverter info */ + float _inv_area{ 0.0 }; + float _inv_delay{ 0.0 }; + uint32_t _inv_id{ UINT32_MAX }; + + /* buffer info */ + float _buf_area{ 0.0 }; + float _buf_delay{ 0.0 }; + uint32_t _buf_id{ UINT32_MAX }; + + unsigned _max_size{ 0 }; /* max #fanins of the gates in the library */ + + bool _use_supergates; + + std::vector const _gates; /* collection of gates */ + super_lib const _supergates_spec; /* collection of supergates declarations */ + tech_library_params const _ps; + + std::vector const _cells; /* collection of standard cells */ + + super_utils _super; /* supergates generation */ + struct_library _struct; /* library for structural matching */ + lib_t _super_lib; /* library of enumerated gates */ + multi_lib_t _multi_lib; /* library of enumerated multioutput gates */ + multi_func_t _multi_funcs; /* enumerated functions for multioutput gates */ + struct_lib_t _struct_lib; /* library of gates for patterns IDs */ +}; /* class tech_library */ + +template +struct exact_supergate +{ + signal root; + + /* number of inputs of the supergate */ + uint8_t n_inputs{ 0 }; + /* saved polarities for inputs and/or outputs */ + uint8_t polarity{ 0 }; + + /* area */ + float area{ 0 }; + /* worst delay */ + float worstDelay{ 0 }; + /* pin-to-pin delay */ + std::array tdelay{ 0 }; + + exact_supergate( signal const root ) + : root( root ) {} +}; + +struct exact_library_params +{ + /* area of a gate */ + float area_gate{ 1.0f }; + /* area of an inverter */ + float area_inverter{ 0.0f }; + /* delay of a gate */ + float delay_gate{ 1.0f }; + /* delay of an inverter */ + float delay_inverter{ 0.0f }; + + /* classify in NP instead of NPN */ + bool np_classification{ false }; + /* Compute DC classes for matching with don't cares */ + bool compute_dc_classes{ false }; + /* verbose */ + bool verbose{ false }; +}; + +/*! \brief Library of graph structures for Boolean matching + * + * This class creates a technology library from a database + * of structures classified in NPN classes. Each NPN-entry in + * the database is stored in its NP class by removing the output + * inverter if present. The class creates supergates from the + * database computing area and delay information. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + mockturtle::mig_npn_resynthesis mig_resyn{ true }; + mockturtle::exact_library lib( mig_resyn ); + \endverbatim + */ +template +class exact_library +{ + using supergates_list_t = std::vector>; + using TT = kitty::static_truth_table; + using tt_hash = kitty::hash; + using lib_t = std::unordered_map; + using dc_transformation_t = std::tuple>; + using dc_t = std::pair; + using dc_lib_t = std::unordered_map, tt_hash>; + +public: + explicit exact_library( exact_library_params const& ps = {} ) + : _database(), + _ps( ps ), + _super_lib(), + _dc_lib() + { + _super_lib.reserve( 222 ); + } + + template + explicit exact_library( RewritingFn const& rewriting_fn, exact_library_params const& ps = {} ) + : _database(), + _ps( ps ), + _super_lib(), + _dc_lib() + { + _super_lib.reserve( 222 ); + generate_library( rewriting_fn ); + } + + template + void add_library( RewritingFn const& rewriting_fn ) + { + generate_library( rewriting_fn ); + } + + /*! \brief Get the structures matching the function. + * + * Returns a list of graph structures that match the function + * represented by the truth table. + */ + const supergates_list_t* get_supergates( TT const& tt ) const + { + auto match = _super_lib.find( tt ); + if ( match != _super_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the structures matching the function with DC. + * + * Returns a list of graph structures that match the function + * represented by the truth table and its dont care set. + * This functions also updates the phase and permutation vector + * of the original NPN class to the new one obtained using + * don't cares. + */ + const supergates_list_t* get_supergates( TT const& tt, TT const& dc, uint32_t& phase, std::vector& perm ) const + { + auto match = _super_lib.find( tt ); + if ( match == _super_lib.end() ) + return nullptr; + + /* lookup for don't care optimization */ + auto match_dc = _dc_lib.find( tt ); + if ( dc._bits == 0 || match_dc == _dc_lib.end() ) + return &match->second; + + for ( auto const& entry : match_dc->second ) + { + auto const& dc_entry_tt = std::get<0>( entry ); + + /* check for containment */ + if ( ( dc & dc_entry_tt ) == dc_entry_tt ) + { + auto const& dc_entry = std::get<1>( entry ); + + /* update phase and perm */ + uint32_t dc_entry_phase = std::get<1>( dc_entry ); + auto const& dc_entry_perm = std::get<2>( dc_entry ); + std::vector temp_perm( perm.size() ); + uint32_t temp_phase = dc_entry_phase & ( 1 << NInputs ); + for ( auto i = 0u; i < NInputs; ++i ) + { + temp_perm[dc_entry_perm[i]] = perm[i]; + temp_phase |= ( ( dc_entry_phase >> i ) & 1 ) << perm[i]; + } + phase ^= temp_phase; + std::copy( temp_perm.begin(), temp_perm.end(), perm.begin() ); + return std::get<0>( dc_entry ); + } + } + + /* no dont care optimization found */ + return &match->second; + } + + /*! \brief Returns the NPN database of structures. */ + Ntk& get_database() + { + return _database; + } + + /*! \brief Returns the NPN database of structures. */ + const Ntk& get_database() const + { + return _database; + } + + /*! \brief Get inverter information. + * + * Returns area, and delay cost of the inverter. + */ + const std::tuple get_inverter_info() const + { + return std::make_pair( _ps.area_inverter, _ps.delay_inverter ); + } + +private: + template + void generate_library( RewritingFn const& rewriting_fn ) + { + std::vector> pis; + for ( auto i = 0u; i < NInputs; ++i ) + { + pis.push_back( _database.create_pi() ); + } + + /* Compute NPN classes */ + std::unordered_set classes; + TT tt; + do + { + const auto res = kitty::exact_npn_canonization( tt ); + classes.insert( std::get<0>( res ) ); + kitty::next_inplace( tt ); + } while ( !kitty::is_const0( tt ) ); + + /* Constuct supergates */ + for ( auto const& entry : classes ) + { + supergates_list_t supergates_pos; + supergates_list_t supergates_neg; + auto const not_entry = ~entry; + + const auto add_supergate = [&]( auto const& f_new ) { + bool complemented = _database.is_complemented( f_new ); + auto f = f_new; + if ( _ps.np_classification && complemented ) + { + f = !f; + } + exact_supergate sg( f ); + compute_info( sg ); + if ( _ps.np_classification && complemented ) + { + supergates_neg.push_back( sg ); + } + else + { + supergates_pos.push_back( sg ); + } + _database.create_po( f ); + return true; + }; + + kitty::dynamic_truth_table function = kitty::extend_to( entry, NInputs ); + rewriting_fn( _database, function, pis.begin(), pis.end(), add_supergate ); + if ( supergates_pos.size() > 0 ) + { + std::stable_sort( supergates_pos.begin(), supergates_pos.end(), [&]( auto const& a, auto const& b ) { + return a.area < b.area; + } ); + _super_lib.insert( { entry, supergates_pos } ); + } + if ( _ps.np_classification && supergates_neg.size() > 0 ) + { + std::stable_sort( supergates_neg.begin(), supergates_neg.end(), [&]( auto const& a, auto const& b ) { + return a.area < b.area; + } ); + _super_lib.insert( { not_entry, supergates_neg } ); + } + } + + if ( _ps.compute_dc_classes ) + compute_dont_cares_classes(); + + if ( _ps.verbose ) + { + std::cout << "Classified in " << _super_lib.size() << " entries" << std::endl; + for ( auto const& pair : _super_lib ) + { + kitty::print_hex( pair.first ); + std::cout << ": "; + + for ( auto const& gate : pair.second ) + { + printf( "%.2f,%.2f,%x,%d,:", gate.worstDelay, gate.area, gate.polarity, gate.n_inputs ); + for ( auto j = 0u; j < NInputs; ++j ) + printf( "%.2f/", gate.tdelay[j] ); + std::cout << " "; + } + std::cout << std::endl; + } + } + } + + /* Computes delay and area info */ + void compute_info( exact_supergate& sg ) + { + _database.incr_trav_id(); + /* info does not consider input and output inverters */ + bool compl_root = _database.is_complemented( sg.root ); + auto const root = compl_root ? !sg.root : sg.root; + sg.area = compute_info_rec( sg, root, 0.0f ); + + /* output polarity */ + sg.polarity |= ( unsigned( compl_root ) ) << NInputs; + /* number of inputs */ + for ( auto i = 0u; i < NInputs; ++i ) + { + sg.tdelay[i] *= -1; /* invert to positive value */ + if ( sg.tdelay[i] != 0.0f ) + sg.n_inputs++; + } + sg.worstDelay *= -1; + } + + float compute_info_rec( exact_supergate& sg, signal const& root, float delay ) + { + auto n = _database.get_node( root ); + + if ( _database.is_constant( n ) ) + return 0.0f; + + float area = 0.0f; + float tdelay = delay; + + if ( _database.is_pi( n ) ) + { + sg.tdelay[_database.index_to_node( n ) - 1u] = std::min( sg.tdelay[_database.index_to_node( n ) - 1u], tdelay ); + sg.worstDelay = std::min( sg.worstDelay, tdelay ); + sg.polarity |= ( unsigned( _database.is_complemented( root ) ) ) << ( _database.index_to_node( n ) - 1u ); + return area; + } + + tdelay -= _ps.delay_gate; + + /* add gate area once */ + if ( _database.visited( n ) != _database.trav_id() ) + { + area += _ps.area_gate; + _database.set_value( n, 0u ); + _database.set_visited( n, _database.trav_id() ); + } + + if ( _database.is_complemented( root ) ) + { + tdelay -= _ps.delay_inverter; + /* add inverter area only once (shared by fanout) */ + if ( _database.value( n ) == 0u ) + { + area += _ps.area_inverter; + _database.set_value( n, 1u ); + } + } + + _database.foreach_fanin( n, [&]( auto const& child ) { + area += compute_info_rec( sg, child, tdelay ); + } ); + + return area; + } + + void compute_dont_cares_classes() + { + _dc_lib.clear(); + + /* save the size for each NPN class */ + std::unordered_map class_sizes; + for ( auto const& entry : _super_lib ) + { + const unsigned numgates = static_cast( std::get<1>( entry ).front().area ); + class_sizes.insert( { std::get<0>( entry ), numgates } ); + } + + uint32_t conflict_found = 0; + uint32_t total_exploration = 0; + + /* find don't care links */ + for ( auto entry_i = class_sizes.begin(); entry_i != class_sizes.end(); ++entry_i ) + { + auto const& tt_i = std::get<0>( *entry_i ); + auto const current_size = std::get<1>( *entry_i ); + + /* use a map to link the dont cares to the new size, NPN class, negations, and permutation vector */ + using dc_transf_t = std::tuple>; + std::unordered_map dc_sets; + + for ( auto entry_j = class_sizes.begin(); entry_j != class_sizes.end(); ++entry_j ) + { + auto const& tt_j = std::get<0>( *entry_j ); + uint32_t size = std::get<1>( *entry_j ); + + /* evaluate DC only for size improvement */ + if ( size >= current_size ) + continue; + + /* skip the same NPN class if gates are constructed in NP classes */ + if ( _ps.np_classification && tt_i == ~tt_j ) + continue; + + exact_npn_enumeration( tt_j, [&]( auto const& tt, uint32_t phase, std::vector const& perm ) { + /* extract the DC set */ + const auto dc = tt_i ^ tt; + + /* limit the explosion of DC combinations to evaluate */ + // if ( kitty::count_ones( dc ) > 3 ) + // return; + + ++total_exploration; + + /* check existance: filters ~12% of conflicts */ + if ( auto const& p = dc_sets.find( dc ); p != dc_sets.end() ) + { + if ( size < std::get<0>( std::get<1>( *p ) ) ) + dc_sets[dc] = std::make_tuple( size, tt_j, phase, perm ); + + ++conflict_found; + return; + } + + /* check dominance */ + auto it = dc_sets.begin(); + while ( it != dc_sets.end() ) + { + auto const& dc_set_tt = std::get<0>( *it ); + auto const& and_tt = dc_set_tt & dc; + + if ( dc_set_tt == and_tt && std::get<0>( std::get<1>( *it ) ) <= size ) + { + return; + } + else if ( dc == and_tt && size <= std::get<0>( std::get<1>( *it ) ) ) + { + it = dc_sets.erase( it ); + } + else + { + ++it; + } + } + + /* permute phase */ + uint32_t phase_perm = phase & ( 1 << NInputs ); + for ( auto i = 0u; i < NInputs; ++i ) + { + phase_perm |= ( ( phase >> perm[i] ) & 1 ) << i; + } + + /* insert in the dc_sets */ + dc_sets[dc] = std::make_tuple( size, tt_j, phase_perm, perm ); + } ); + } + + /* add entries to the main data structure */ + std::vector dc_transformations; + dc_transformations.reserve( dc_sets.size() ); + + std::array permutation; + + /* insert in a sorted way based on gain */ + /* TODO: optimize to reduce the number of cycles */ + for ( auto i = 0u; i < std::get<1>( *entry_i ); ++i ) + { + for ( auto const& dc : dc_sets ) + { + auto const& transf = std::get<1>( dc ); + + if ( std::get<0>( transf ) != i ) + { + continue; + } + + supergates_list_t const* sg = &_super_lib[std::get<1>( transf )]; + auto const& perm = std::get<3>( transf ); + + assert( perm.size() == NInputs ); + + for ( auto j = 0u; j < NInputs; ++j ) + { + permutation[j] = perm[j]; + } + + dc_transformations.emplace_back( std::make_pair( std::get<0>( dc ), std::make_tuple( sg, std::get<2>( transf ), permutation ) ) ); + } + } + + if ( !dc_transformations.empty() ) + _dc_lib.insert( { tt_i, dc_transformations } ); + } + } + +private: + Ntk _database; + exact_library_params const _ps; + lib_t _super_lib; + dc_lib_t _dc_lib; +}; /* class exact_library */ + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/utils/truth_table_cache.hpp b/third-party/mockturtle/include/mockturtle/utils/truth_table_cache.hpp new file mode 100644 index 00000000000..90fe95e2def --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/utils/truth_table_cache.hpp @@ -0,0 +1,172 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file truth_table_cache.hpp + \brief Truth table cache + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include +#include +#include + +#include "absl/container/flat_hash_map.h" + +namespace mockturtle +{ + +/*! \brief Truth table cache. + * + * A truth table cache is used to store truth tables. Many applications + * require to assign truth tables to nodes in a network. But since the truth + * tables often repeat, it is more convenient to store an index to a cache + * that stores the truth table. In order to reduce space, only one entry for + * a truth table and its complement are stored in the cache. To distinguish + * between both versions, the index to an entry in the truth table cache is + * represented as literal. The truth table whose function maps the input + * assignment \f$0, \dots, 0\f$ to \f$0\f$ is considered the *normal* truth + * table, while its complement is considered the complemented version. Only + * the normal truth table is stored at some index \f$i\f$. A positive literal + * \f$2i\f$ points to the normal truth table at index \f$i\f$. A negative + * literal \f$2i + 1\f$ points to the same truth table but returns its + * complement. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + truth_table_cache cache; + + kitty::dynamic_truth_table maj( 3 ); + kitty::create_majority( maj ); + auto l1 = cache.insert( maj ); // index is 0 + + auto tt = cache[l1 ^ 1]; // tt is ~maj + auto l2 = cache.insert( tt ); // index is 1 + + auto s = cache.size(); // size is 1 + \endverbatim + */ +template +class truth_table_cache +{ +public: + /*! \brief Creates a truth table cache and reserves memory. */ + truth_table_cache( uint32_t capacity = 1000u ); + + /*! \brief Inserts a truth table and returns a literal. + * + * To save space, only normal functions are stored in the truth table cache. + * A function is normal, if the input pattern \f$0, \dots, 0\f$ maps to + * \f$0\f$. If a function is not normal, its complement is inserted into the + * cache and a negative literal is returned. + * + * The default convention for literals is assumed. That is an index \f$i\f$ + * (starting) from \f$0\f$ has positive literal \f$2i\f$ and negative literal + * \f$2i + 1\f$. + * + * \param tt Truth table to insert + * \return Literal of position in cache + */ + uint32_t insert( TT tt ); + + /*! \brief Returns truth table for a given literal. + * + * The function requires that `lit` is smaller than `size()`. + */ + TT operator[]( uint32_t lit ) const; + + /*! \brief Returns number of normalized truth tables in the cache. */ + auto size() const { return _data.size(); } + + /*! \brief Resizes the cache. + * + * Reserve additional space for cache and data. + */ + void resize( uint32_t capacity ); + +private: + absl::flat_hash_map> _indexes; + std::vector _data; +}; + +template +truth_table_cache::truth_table_cache( uint32_t capacity ) +{ + _indexes.reserve( capacity ); + _data.reserve( capacity ); +} + +template +uint32_t truth_table_cache::insert( TT tt ) +{ + uint32_t is_compl{ 0 }; + + if ( kitty::get_bit( tt, 0 ) ) + { + is_compl = 1; + tt = ~tt; + } + + /* is truth table already in cache? */ + const auto it = _indexes.find( tt ); + if ( it != _indexes.end() ) + { + return static_cast( 2 * it->second + is_compl ); + } + + /* add truth table to end of cache */ + const auto size = _data.size(); + const auto index = static_cast( 2 * size + is_compl ); + _data.push_back( tt ); + _indexes[tt] = static_cast( size ); + return index; +} + +template +TT truth_table_cache::operator[]( uint32_t index ) const +{ + auto& entry = _data[index >> 1]; + return ( index & 1 ) ? ~entry : entry; +} + +template +void truth_table_cache::resize( uint32_t capacity ) +{ + _indexes.reserve( capacity ); + _data.reserve( capacity ); +} + +} /* namespace mockturtle */ diff --git a/third-party/mockturtle/include/mockturtle/views/binding_view.hpp b/third-party/mockturtle/include/mockturtle/views/binding_view.hpp new file mode 100644 index 00000000000..1eb529a0ee2 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/binding_view.hpp @@ -0,0 +1,261 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file binding_view.hpp + \brief Implements methods to bind the network to a standard cell library + + \author Alessandro Tempia Calvino + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../io/genlib_reader.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Adds bindings to a technology library and mapping API methods. + * + * This view adds methods to create and manage a mapped network that + * implements gates contained in a technology library. This view + * is returned by the technology mapping command `map`. It can be used + * to report statistics about the network and write the network into + * a verilog file. It always adds the functions `has_binding`, + * `remove_binding`, `add_binding`, `add_binding_with_check`, `get_binding`, + * `get_binding_index`, `get_library`, `compute_area`, `compute_worst_delay`, + * `report_stats`, and `report_gates_usage`. + * + * **Required network functions:** + * - `size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // read cell library in genlib format + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ) + tech_library tech_lib( gates ); + + // call technology mapping to obtain the view + binding_view res = map( aig, tech_lib ); + + // prints stats and gates usage + res.report_stats(); + res.report_gates_usage(); + + // write the mapped network in verilog + write_verilog_with_binding( res, "file.v" ); + \endverbatim + */ +template +class binding_view : public Ntk +{ +public: + using node = typename Ntk::node; + +public: + explicit binding_view( std::vector const& library ) + : Ntk(), _library{ library }, _bindings( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + explicit binding_view( Ntk const& ntk, std::vector const& library ) + : Ntk( ntk ), _library{ library }, _bindings( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + binding_view& operator=( binding_view const& binding_ntk ) + { + Ntk::operator=( binding_ntk ); + _library = binding_ntk._library; + _bindings = binding_ntk._bindings; + return *this; + } + + void add_binding( node const& n, uint32_t gate_id ) + { + assert( gate_id < _library.size() ); + _bindings[n] = gate_id; + } + + bool add_binding_with_check( node const& n, uint32_t gate_id ) + { + assert( gate_id < _library.size() ); + + auto const& cell = _library[gate_id]; + + if ( Ntk::node_function( n ) == cell.function ) + { + _bindings[n] = gate_id; + return true; + } + return false; + } + + void remove_binding( node const& n ) const + { + _bindings.erase( n ); + } + + const gate& get_binding( node const& n ) const + { + return _library[_bindings[n]]; + } + + bool has_binding( node const& n ) const + { + return _bindings.has( n ); + } + + unsigned int get_binding_index( node const& n ) const + { + return _bindings[n]; + } + + const std::vector& get_library() const + { + return _library; + } + + double compute_area() const + { + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_binding( n ) ) + { + area += get_binding( n ).area; + } + } ); + + return area; + } + + double compute_worst_delay() const + { + topo_view ntk_topo{ *this }; + node_map delays( *this ); + double worst_delay = 0; + + ntk_topo.foreach_node( [&]( auto const& n, auto ) { + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + { + delays[n] = 0; + return true; + } + + if ( has_binding( n ) ) + { + auto const& g = get_binding( n ); + double gate_delay = 0; + Ntk::foreach_fanin( n, [&]( auto const& f, auto i ) { + gate_delay = std::max( gate_delay, (double)( delays[f] + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ) ); + } ); + delays[n] = gate_delay; + worst_delay = std::max( worst_delay, gate_delay ); + } + return true; + } ); + + return worst_delay; + } + + void report_stats( std::ostream& os = std::cout ) const + { + os << fmt::format( "[i] Report stats: area = {:>5.2f}; delay = {:>5.2f};\n", compute_area(), compute_worst_delay() ); + } + + void report_gates_usage( std::ostream& os = std::cout ) const + { + std::vector gates_profile( _library.size(), 0u ); + + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_binding( n ) ) + { + auto const& g = get_binding( n ); + ++gates_profile[g.id]; + area += g.area; + } + } ); + + os << "[i] Report gates usage:\n"; + + uint32_t tot_instances = 0u; + for ( auto i = 0u; i < gates_profile.size(); ++i ) + { + if ( gates_profile[i] > 0u ) + { + float tot_gate_area = gates_profile[i] * _library[i].area; + + os << fmt::format( "[i] {:<25}", _library[i].name ) + << fmt::format( "\t Instance = {:>10d}", gates_profile[i] ) + << fmt::format( "\t Area = {:>12.2f}", tot_gate_area ) + << fmt::format( " {:>8.2f} %\n", tot_gate_area / area * 100 ); + + tot_instances += gates_profile[i]; + } + } + + os << fmt::format( "[i] {:<25}", "TOTAL" ) + << fmt::format( "\t Instance = {:>10d}", tot_instances ) + << fmt::format( "\t Area = {:>12.2f} 100.00 %\n", area ); + } + +private: + std::vector _library; + node_map> _bindings; +}; /* binding_view */ + +template +binding_view( T const& ) -> binding_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/cell_view.hpp b/third-party/mockturtle/include/mockturtle/views/cell_view.hpp new file mode 100644 index 00000000000..bc2976d2626 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/cell_view.hpp @@ -0,0 +1,299 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cell_view.hpp + \brief Implements methods to bind the network to a standard cell library + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../utils/node_map.hpp" +#include "../utils/standard_cell.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Adds cells to a technology library and mapping API methods. + * + * This view adds methods to create and manage a mapped network that + * implements cells contained in a technology library. This view + * is returned by the technology mapping command `emap`. It can be used + * to report statistics about the network and write the network into + * a verilog file. It always adds the functions `has_cell`, + * `remove_cell`, `add_cell`, `add_cell_with_check`, `get_cell`, + * `get_cell_index`, `get_library`, `compute_area`, `compute_worst_delay`, + * `report_stats`, and `report_cells_usage`. + * + * **Required network functions:** + * - `size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // read cell library in genlib format + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ) + tech_library tech_lib( gates ); + + // call technology mapping to obtain the view + cell_view res = emap_block( aig, tech_lib ); + + // prints stats and cells usage + res.report_stats(); + res.report_cells_usage(); + \endverbatim + */ +template +class cell_view : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit cell_view( std::vector const& library ) + : Ntk(), _library{ library }, _cells( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + explicit cell_view( Ntk const& ntk, std::vector const& library ) + : Ntk( ntk ), _library{ library }, _cells( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + cell_view& operator=( cell_view const& cell_ntk ) + { + Ntk::operator=( cell_ntk ); + _library = cell_ntk._library; + _cells = cell_ntk._cells; + return *this; + } + + void add_cell( node const& n, uint32_t cell_id ) + { + assert( cell_id < _library.size() ); + _cells[n] = cell_id; + } + + bool add_cell_with_check( node const& n, uint32_t cell_id ) + { + assert( cell_id < _library.size() ); + + auto const& cell = _library[cell_id]; + + if constexpr ( has_num_outputs_v && has_node_function_v ) + { + if ( Ntk::num_outputs( n ) != cell.gates.size() ) + return false; + + for ( uint32_t i = 0; i < Ntk::num_outputs( n ); ++i ) + { + if ( Ntk::node_function_pin( n, i ) != cell.gates[i].function ) + { + return false; + } + } + + _cells[n] = cell_id; + return true; + } + + if ( cell.gates.size() > 1 ) + return false; + + if ( Ntk::node_function( n ) == cell.gates[0].function ) + { + _cells[n] = cell_id; + return true; + } + + return false; + } + + void remove_cell( node const& n ) const + { + _cells.erase( n ); + } + + const standard_cell& get_cell( node const& n ) const + { + return _library[_cells[n]]; + } + + bool has_cell( node const& n ) const + { + return _cells.has( n ); + } + + unsigned int get_cell_index( node const& n ) const + { + return _cells[n]; + } + + const std::vector& get_library() const + { + return _library; + } + + double compute_area() const + { + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_cell( n ) ) + { + area += get_cell( n ).area; + } + } ); + + return area; + } + + double compute_worst_delay() const + { + topo_view ntk_topo{ *this }; + std::vector> delays( Ntk::size() ); + double worst_delay = 0; + + ntk_topo.foreach_node( [&]( auto const& n, auto ) { + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + { + delays[n].push_back( 0 ); + return true; + } + + if ( has_cell( n ) ) + { + auto const& cell = get_cell( n ); + + for ( gate const& g : cell.gates ) + { + double cell_delay = 0; + if constexpr ( has_get_output_pin_v ) + { + Ntk::foreach_fanin( n, [&]( signal const& f, auto i ) { + cell_delay = std::max( cell_delay, delays[Ntk::get_node( f )][Ntk::get_output_pin( f )] + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ); + } ); + } + else + { + Ntk::foreach_fanin( n, [&]( signal const& f, auto i ) { + cell_delay = std::max( cell_delay, delays[Ntk::get_node( f )].front() + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ); + } ); + } + delays[n].push_back( cell_delay ); + worst_delay = std::max( worst_delay, cell_delay ); + } + } + else + { + worst_delay = -1; + return false; + } + return true; + } ); + + return worst_delay; + } + + void report_stats( std::ostream& os = std::cout ) const + { + os << fmt::format( "[i] Report stats: area = {:>5.2f}; delay = {:>5.2f};\n", compute_area(), compute_worst_delay() ); + } + + void report_cells_usage( std::ostream& os = std::cout ) const + { + std::vector cells_profile( _library.size(), 0u ); + + double area = 0; + Ntk::foreach_node( [&]( node const& n, auto ) { + if ( has_cell( n ) ) + { + auto const& g = get_cell( n ); + ++cells_profile[g.id]; + area += g.area; + } + } ); + + os << "[i] Report cells usage:\n"; + + uint32_t tot_instances = 0u; + for ( auto i = 0u; i < cells_profile.size(); ++i ) + { + if ( cells_profile[i] > 0u ) + { + float tot_cell_area = cells_profile[i] * _library[i].area; + + os << fmt::format( "[i] {:<25}", _library[i].name ) + << fmt::format( "\t Instance = {:>10d}", cells_profile[i] ) + << fmt::format( "\t Area = {:>12.2f}", tot_cell_area ) + << fmt::format( " {:>8.2f} %\n", tot_cell_area / area * 100 ); + + tot_instances += cells_profile[i]; + } + } + + os << fmt::format( "[i] {:<25}", "TOTAL" ) + << fmt::format( "\t Instance = {:>10d}", tot_instances ) + << fmt::format( "\t Area = {:>12.2f} 100.00 %\n", area ); + } + +private: + std::vector _library; + node_map> _cells; +}; /* cell_view */ + +template +cell_view( T const& ) -> cell_view; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/views/choice_view.hpp b/third-party/mockturtle/include/mockturtle/views/choice_view.hpp new file mode 100644 index 00000000000..a65eb3c682b --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/choice_view.hpp @@ -0,0 +1,592 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file choice_view.hpp + \brief Implement choices in network + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../networks/events.hpp" +#include "../traits.hpp" + +namespace mockturtle +{ + +struct choice_view_params +{ + bool add_choices_on_substitute{ true }; + bool update_on_add{ true }; +}; + +/*! \brief Implements choices in network + * + * Overrides the interface methods `substitute_node`, `incr_fanout_size`, + * `decr_fanout_size`, `fanout_size`. + * + * This class manages equivalent nodes keeping them saved as alternatives in + * the network. Each node belongs to an equivalence class in which a node + * is the class representative, by default the one with the lowest index. + * Equivalence classes are saved as linked lists. The `_choice_repr` vector + * associates each node to the next one in the linked list (the one closer + * to the representative). The representative is the tail and "points" at itself. + * The `_choice_phase` vector is used to save the polarity of each node in the + * class with respect to the representative. The representative uses its field + * to point to the head of the list. + * + * This view is not compatible with `fanout_view`. + * + * **Required network functions:** + * - `get_node` + * - `size` + * - `node_to_index` + * - `index_to_node` + * - `is_complemented` + * - `make_signal` + */ +template> +class choice_view +{ +}; + +template +class choice_view : public Ntk +{ +public: + choice_view( Ntk const& ntk, choice_view_params const& ps = {} ) : Ntk( ntk ) + { + (void)ps; + } +}; + +template +class choice_view : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + choice_view( choice_view_params const& ps = {} ) + : Ntk(), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + init_choice_classes(); + + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + } + + choice_view( Ntk const& ntk, choice_view_params const& ps = {} ) + : Ntk( ntk ), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + init_choice_classes(); + + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + } + + choice_view& operator=( choice_view const& choice_ntk ) + { + Ntk::operator=( choice_ntk ); + if ( this != &choice_ntk ) + { + this->_choice_repr = choice_ntk._choice_repr; + this->_choice_phase = choice_ntk._choice_phase; + this->_ps = choice_ntk._ps; + } + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + return *this; + } + + ~choice_view() + { + if ( _ps.update_on_add ) + { + Ntk::events().release_add_event( _add_event ); + } + } + + template + std::enable_if_t, void> add_choice( node const& n1, node const& n2 ) + { + add_choice( n1, Ntk::make_signal( n2 ) ); + } + + void add_choice( node const& n1, signal const& s2 ) + { + auto const n2 = Ntk::get_node( s2 ); + auto const id1 = Ntk::node_to_index( n1 ); + auto const id2 = Ntk::node_to_index( n2 ); + + if ( id1 == id2 ) + { + /* same node */ + return; + } + + auto rep1 = get_choice_representative( n1 ); + auto rep2 = get_choice_representative( n2 ); + + auto idrep1 = Ntk::node_to_index( rep1 ); + auto idrep2 = Ntk::node_to_index( rep2 ); + + if ( idrep1 == idrep2 ) + { + /* already in the same equivalence class */ + return; + } + + /* set the representative as the node with lowest index */ + if ( idrep1 > idrep2 ) + { + std::swap( rep1, rep2 ); + std::swap( idrep1, idrep2 ); + } + + /* merge the eq lists */ + bool inv = false; + if ( ( ( _choice_repr->at( id1 ) != n1 && Ntk::is_complemented( _choice_phase->at( id1 ) ) ) != Ntk::is_complemented( s2 ) ) != + ( _choice_repr->at( id2 ) != n2 && Ntk::is_complemented( _choice_phase->at( id2 ) ) ) ) + { + /* before merging, complement nodes accordingly to the new representative phase, if needed */ + invert_phases_in_class( rep2 ); + inv = true; + } + _choice_repr->at( idrep2 ) = Ntk::get_node( _choice_phase->at( idrep1 ) ); + _choice_phase->at( idrep1 ) = _choice_phase->at( idrep2 ); + /* store the right phase */ + _choice_phase->at( idrep2 ) = Ntk::make_signal( rep2 ) ^ inv; + } + + /* Set sig as an equivalence representative of n without including n in the choice list */ + void set_representative( node const& n, signal const& sig ) + { + /* TODO: TEST */ + auto nsig = Ntk::get_node( sig ); + auto repr = get_choice_representative( nsig ); + bool c = false; + + if ( repr != nsig ) + { + c = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( nsig ) ) ); + } + _choice_repr->at( Ntk::node_to_index( n ) ) = repr; + _choice_phase->at( Ntk::node_to_index( n ) ) = Ntk::make_signal( n ) ^ ( Ntk::is_complemented( sig ) ^ c ); + } + + void update_choice_representative( node const& n ) + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + if ( is_choice_representative( n ) ) + return; + + bool inv = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ); + auto repr = get_choice_representative( n ); + _choice_phase->at( Ntk::node_to_index( n ) ) = Ntk::make_signal( _choice_repr->at( Ntk::node_to_index( n ) ) ); + _choice_repr->at( Ntk::node_to_index( n ) ) = n; + _choice_repr->at( Ntk::node_to_index( repr ) ) = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( repr ) ) ); + _choice_phase->at( Ntk::node_to_index( repr ) ) = Ntk::make_signal( repr ); + if ( inv ) + { + invert_phases_in_class( n ); + } + } + + /* returns the new class representative */ + std::optional remove_choice( node const& n ) + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + auto next = Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( n ) ) ); + auto repr = Ntk::node_to_index( get_choice_representative( next ) ); + auto tail = Ntk::node_to_index( Ntk::get_node( _choice_phase->at( Ntk::node_to_index( repr ) ) ) ); + + /* if n is a representative, recompute the representative of the new class */ + if ( repr == Ntk::node_to_index( n ) && tail != Ntk::node_to_index( n ) ) + { + auto new_repr = tail; + node pred = tail; + foreach_choice( tail, [&]( auto const& g ) { + if ( Ntk::node_to_index( g ) != repr && Ntk::node_to_index( g ) < new_repr ) + { + new_repr = Ntk::node_to_index( g ); + } + if ( Ntk::node_to_index( g ) != repr && Ntk::node_to_index( _choice_repr->at( g ) ) == repr ) + { + pred = Ntk::node_to_index( g ); + } + return true; + } ); + auto const new_repr_signal = _choice_phase->at( new_repr ); + bool polarity = Ntk::is_complemented( new_repr_signal ); + if ( new_repr == pred ) + { + _choice_phase->at( new_repr ) = _choice_phase->at( repr ); + } + else + { + _choice_phase->at( new_repr ) = Ntk::make_signal( _choice_repr->at( new_repr ) ); + _choice_repr->at( pred ) = Ntk::index_to_node( tail ); + } + _choice_repr->at( new_repr ) = Ntk::index_to_node( new_repr ); + if ( polarity ) + { + invert_phases_in_class( new_repr ); + } + return new_repr_signal; + } + else if ( tail == n ) + { + _choice_phase->at( Ntk::node_to_index( repr ) ) = Ntk::make_signal( next ); + } + else + { + while ( Ntk::node_to_index( _choice_repr->at( tail ) ) != Ntk::node_to_index( n ) ) + { + tail = Ntk::node_to_index( _choice_repr->at( tail ) ); + } + _choice_repr->at( tail ) = Ntk::index_to_node( next ); + } + + _choice_repr->at( Ntk::node_to_index( n ) ) = n; + return std::nullopt; + } + + void clear_choices() + { + for ( auto i = 0u; i < Ntk::size(); i++ ) + { + _choice_repr->at( i ) = Ntk::index_to_node( i ); + _choice_phase->at( i ) = Ntk::make_signal( Ntk::index_to_node( i ) ); + } + } + + bool delete_choice_from_network( node const& n ) + { + if ( Ntk::is_dead( n ) || !is_choice( n ) ) + { + /* node is already dead or not dangling */ + return false; + } + + Ntk::take_out_node( n ); + remove_choice( n ); + return true; + } + + node get_choice_representative( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + auto rep = _choice_repr->at( Ntk::node_to_index( n ) ); + while ( Ntk::node_to_index( rep ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( rep ) ) ) ) + { + rep = _choice_repr->at( Ntk::node_to_index( rep ) ); + } + return rep; + } + + bool is_choice_representative( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + return _choice_repr->at( Ntk::node_to_index( n ) ) == n; + } + + signal get_choice_representative_signal( node const& n ) const + { + auto repr = Ntk::make_signal( get_choice_representative( n ) ); + + if ( Ntk::get_node( repr ) == n ) + { + return repr; + } + + return repr ^ Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ); + } + + template + std::enable_if_t, typename T::signal> get_choice_representative_signal( signal const& sig ) const + { + auto n = Ntk::get_node( sig ); + auto repr = get_choice_representative( n ); + + if ( repr == n ) + { + return sig; + } + + bool c = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ) != Ntk::is_complemented( sig ); + return Ntk::make_signal( repr ) ^ c; + } + + uint32_t count_choices( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + uint32_t size = 1u; + auto p = n; + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + p = _choice_repr->at( Ntk::node_to_index( p ) ); + size++; + } + p = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( p ) ) ); + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( n ) ) + { + size++; + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + return size; + } + + template + void foreach_choice( node const& n, Fn&& fn ) const + { + auto p = n; + if ( !fn( p ) ) + { + return; + } + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + p = _choice_repr->at( Ntk::node_to_index( p ) ); + if ( !fn( p ) ) + { + return; + } + } + p = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( p ) ) ); + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( n ) ) + { + if ( !fn( p ) ) + { + return; + } + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + } + + /* redefine node substitution */ + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _new] = to_substitute.top(); + to_substitute.pop(); + + if ( _ps.add_choices_on_substitute ) + { + add_choice( _old, _new ); + } + // TODO: add replace choice mode + + for ( auto idx = 1u; idx < Ntk::_storage->nodes.size(); ++idx ) + { + if ( Ntk::is_ci( idx ) || Ntk::is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = Ntk::replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + Ntk::replace_in_outputs( _old, _new ); + + // set old node as choice, reset fanout + Ntk::_storage->nodes[_old].data[0].h1 &= UINT32_C( 0xC0000000 ); + take_out_choice( _old ); + + if ( is_choice( Ntk::get_node( _new ) ) && fanout_size( Ntk::get_node( _new ) ) > 0u ) + { + take_in_choice( Ntk::get_node( _new ) ); + } + } + } + + void take_out_choice( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || Ntk::is_ci( n ) ) + return; + + auto& nobj = Ntk::_storage->nodes[n]; + set_choice_flag( n ); + Ntk::_storage->hash.erase( nobj ); + + for ( auto i = 0u; i < Ntk::fanin_size( n ); ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + /* set childrens in MFFC as choice, decrement the fanout count */ + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_choice( nobj.children[i].index ); + } + } + } + + void take_in_choice( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || Ntk::is_ci( n ) ) + return; + + auto& nobj = Ntk::_storage->nodes[n]; + reset_choice_flag( n ); + Ntk::_storage->hash[nobj] = n; + + for ( auto i = 0u; i < Ntk::fanin_size( n ); ++i ) + { + /* restore choice childrens in MFFC, increment the fanout count */ + if ( incr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_in_choice( nobj.children[i].index ); + } + } + } + + /* redefine methods for choice flag: storage h1 = dead(31), choice(30), fanout_size(29 to 0) */ + uint32_t fanout_size( node const& n ) const + { + return Ntk::_storage->nodes[n].data[0].h1 & UINT32_C( 0x3FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return Ntk::_storage->nodes[n].data[0].h1++ & UINT32_C( 0x3FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --Ntk::_storage->nodes[n].data[0].h1 & UINT32_C( 0x3FFFFFFF ); + } + + inline bool is_choice( node const& n ) const + { + return ( Ntk::_storage->nodes[n].data[0].h1 >> 30 ) & 1; + } + +private: + inline void set_choice_flag( node const& n ) const + { + Ntk::_storage->nodes[n].data[0].h1 |= UINT32_C( 0x40000000 ); + } + + inline void reset_choice_flag( node const& n ) const + { + Ntk::_storage->nodes[n].data[0].h1 &= UINT32_C( 0xBFFFFFFF ); + } + + void init_choice_classes() + { + _choice_repr = std::make_shared>( Ntk::size() ); + _choice_phase = std::make_shared>( Ntk::size() ); + // Ntk::foreach_node( [&]( auto n ) { + for ( auto i = 0u; i < Ntk::size(); i++ ) + { + _choice_repr->at( i ) = Ntk::index_to_node( i ); + _choice_phase->at( i ) = Ntk::make_signal( Ntk::index_to_node( i ) ); + } + } + + void invert_phases_in_class( node const& rep ) + { + assert( Ntk::node_to_index( rep ) < Ntk::size() ); + assert( is_choice_representative( rep ) ); + + auto p = Ntk::get_node( _choice_phase->at( rep ) ); + + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + _choice_phase->at( p ) = !_choice_phase->at( p ); + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + } + + void on_add( node const& n ) + { + if ( Ntk::size() > _choice_repr->size() ) + { + _choice_repr->push_back( n ); + _choice_phase->push_back( Ntk::make_signal( n ) ); + } + } + +private: + std::shared_ptr> _choice_repr; + std::shared_ptr> _choice_phase; + choice_view_params _ps; + std::shared_ptr::add_event_type> _add_event; +}; + +template +choice_view( T const&, choice_view_params const& ps = {} ) -> choice_view; + +} // namespace mockturtle diff --git a/third-party/mockturtle/include/mockturtle/views/immutable_view.hpp b/third-party/mockturtle/include/mockturtle/views/immutable_view.hpp new file mode 100644 index 00000000000..f5ee1186d49 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/immutable_view.hpp @@ -0,0 +1,89 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file immutable_view.hpp + \brief Disables all methods to change the network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Deletes all methods that can change the network. + * + * This view deletes all methods that can change the network structure such as + * `create_not`, `create_and`, or `create_node`. This view is convenient to + * use as a base class for other views that make some computations based on the + * structure when being constructed. Then, changes to the structure invalidate + * these data. + */ +template +class immutable_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /*! \brief Default constructor. + * + * Constructs immutable view on another network. + */ + immutable_view( Ntk const& ntk ) : Ntk( ntk ) + { + } + + signal create_pi() = delete; + void create_po( signal const& s ) = delete; + signal create_ro() = delete; + void create_ri( signal const& s ) = delete; + signal create_buf( signal const& f ) = delete; + signal create_not( signal const& f ) = delete; + signal create_and( signal const& f, signal const& g ) = delete; + signal create_nand( signal const& f, signal const& g ) = delete; + signal create_or( signal const& f, signal const& g ) = delete; + signal create_nor( signal const& f, signal const& g ) = delete; + signal create_lt( signal const& f, signal const& g ) = delete; + signal create_le( signal const& f, signal const& g ) = delete; + signal create_gt( signal const& f, signal const& g ) = delete; + signal create_ge( signal const& f, signal const& g ) = delete; + signal create_xor( signal const& f, signal const& g ) = delete; + signal create_xnor( signal const& f, signal const& g ) = delete; + signal create_maj( signal const& f, signal const& g, signal const& h ) = delete; + signal create_ite( signal const& cond, signal const& f_then, signal const& f_else ) = delete; + signal create_node( std::vector const& fanin, kitty::dynamic_truth_table const& function ) = delete; + signal clone_node( immutable_view const& other, node const& source, std::vector const& fanin ) = delete; + void substitute_node( node const& old_node, node const& new_node ) = delete; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/names_view.hpp b/third-party/mockturtle/include/mockturtle/views/names_view.hpp new file mode 100644 index 00000000000..e633b912b04 --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/names_view.hpp @@ -0,0 +1,210 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file names_view.hpp + \brief Implements methods to declare names for network signals + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +template +class names_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + template + names_view( Ntk const& ntk = Ntk(), StrType name = "" ) + : Ntk( ntk ), _network_name{ name } + { + } + + names_view( names_view const& named_ntk ) + : Ntk( named_ntk ), _network_name( named_ntk._network_name ), _signal_names( named_ntk._signal_names ), _output_names( named_ntk._output_names ) + { + } + + names_view& operator=( names_view const& named_ntk ) + { + if ( this != &named_ntk ) // Check for self-assignment + { + Ntk::operator=( named_ntk ); + _signal_names = named_ntk._signal_names; + _network_name = named_ntk._network_name; + _output_names = named_ntk._output_names; + } + return *this; + } + + /*! \brief Creates a primary input and set its name. + * + * \param name Name of the created primary input + */ + signal create_pi( std::string const& name = {} ) + { + const auto s = Ntk::create_pi(); + if ( !name.empty() ) + { + set_name( s, name ); + } + return s; + } + + /*! \brief Creates a primary output and set its name. + * + * \param s Signal that drives the created primary output + * \param name Name of the created primary output + */ + void create_po( signal const& s, std::string const& name = {} ) + { + const auto index = Ntk::num_pos(); + Ntk::create_po( s ); + if ( !name.empty() ) + { + set_output_name( index, name ); + } + } + + /*! \brief Sets network name. + * + * \param name Name of the network + */ + template + void set_network_name( StrType name ) noexcept + { + _network_name = name; + } + + /*! \brief Gets network name. + * + * \return Network name + */ + std::string get_network_name() const noexcept + { + return _network_name; + } + + /*! \brief Checks if a signal has a name. + * + * Note that complemented signals may have different names. + * + * \param s Signal to be checked + * \return Whether the signal has a name in record + */ + bool has_name( signal const& s ) const + { + return ( _signal_names.find( s ) != _signal_names.end() ); + } + + /*! \brief Sets the name for a signal. + * + * Note that names are set separately for complemented signals. + * + * \param s Signal to be set a name + * \param name Name of the signal + */ + void set_name( signal const& s, std::string const& name ) + { + _signal_names[s] = name; + } + + /*! \brief Gets signal name. + * + * Note that complemented signals may have different names. + * + * \param s Signal to be queried + * \return Name of the signal + */ + std::string get_name( signal const& s ) const + { + return _signal_names.at( s ); + } + + /*! \brief Checks if a primary output has a name. + * + * \param index Index of the primary output to be checked + * \return Whether the primary output has a name in record + */ + bool has_output_name( uint32_t index ) const + { + return ( _output_names.find( index ) != _output_names.end() ); + } + + /*! \brief Sets the name for a primary output. + * + * Note that even if two primary outputs are driven by + * the same signal, they may have different names. + * + * \param index Index of the primary output to set a name + * \param name Name of the primary output + */ + void set_output_name( uint32_t index, std::string const& name ) + { + _output_names[index] = name; + } + + /*! \brief Gets the name of a primary output. + * + * Note that even if two primary outputs are driven by + * the same signal, they may have different names. + * + * \param index Index of the primary output to be queried + * \return Name of the primary output + */ + std::string get_output_name( uint32_t index ) const + { + return _output_names.at( index ); + } + +private: + std::string _network_name; + std::map _signal_names; + std::map _output_names; +}; /* names_view */ + +template +names_view( T const& ) -> names_view; + +template +names_view( T const&, typename T::signal const& ) -> names_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/include/mockturtle/views/topo_view.hpp b/third-party/mockturtle/include/mockturtle/views/topo_view.hpp new file mode 100644 index 00000000000..227e9c36e2a --- /dev/null +++ b/third-party/mockturtle/include/mockturtle/views/topo_view.hpp @@ -0,0 +1,324 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file topo_view.hpp + \brief Reimplements foreach_node to guarantee topological order + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Max Austin +*/ + +#pragma once + +#include +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "immutable_view.hpp" + +namespace mockturtle +{ + +/*! \brief Ensures topological order for of all nodes reachable from the outputs. + * + * Overrides the interface methods `foreach_node`, `foreach_gate`, + * `size`, `num_gates`. + * + * This class computes *on construction* a topological order of the nodes which + * are reachable from the outputs. Constant nodes and primary inputs will also + * be considered even if they are not reachable from the outputs. Further, + * constant nodes and primary inputs will be visited first before any gate node + * is visited. Constant nodes precede primary inputs, and primary inputs are + * visited in the same order in which they were created. + * + * Since the topological order is computed only once when creating an instance, + * this view disables changes to the network interface. Also, since only + * reachable nodes are traversed, not all network nodes may be called in + * `foreach_node` and `foreach_gate`. + * + * **Required network functions:** + * - `get_constant` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_fanin` + * - `incr_trav_id` + * - `set_visited` + * - `trav_id` + * - `visited` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow; aig may not be in topological order + aig_network aig = ...; + + // create a topological view on the network + topo_view aig_topo{aig}; + + // call algorithm that requires topological order + cut_enumeration( aig_topo ); + \endverbatim + */ +template> +class topo_view +{ +}; + +template +class topo_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr bool is_topologically_sorted = true; + + /*! \brief Default constructor. + * + * Constructs topological view on another network. + */ + topo_view( Ntk const& ntk ) : immutable_view( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + update_topo(); + } + + /*! \brief Default constructor. + * + * Constructs topological view, but only for the transitive fan-in starting + * from a given start signal. + */ + topo_view( Ntk const& ntk, typename Ntk::signal const& start_signal ) + : immutable_view( ntk ), + start_signal( start_signal ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + update_topo(); + } + + /*! \brief Reimplementation of `size`. */ + auto size() const + { + return static_cast( topo_order.size() ); + } + + /*! \brief Reimplementation of `num_gates`. */ + auto num_gates() const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + return static_cast( topo_order.size() - offset ); + } + + /*! \brief Reimplementation of `node_to_index`. */ + uint32_t node_to_index( node const& n ) const + { + return std::distance( std::begin( topo_order ), std::find( std::begin( topo_order ), std::end( topo_order ), n ) ); + } + + /*! \brief Reimplementation of `index_to_node`. */ + node index_to_node( uint32_t index ) const + { + return topo_order.at( index ); + } + + /*! \brief Reimplementation of `foreach_node`. */ + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( topo_order.begin(), + topo_order.end(), + fn ); + } + + /*! \brief Implementation of `foreach_node` in reverse topological order. */ + template + void foreach_node_reverse( Fn&& fn ) const + { + detail::foreach_element( topo_order.rbegin(), + topo_order.rend(), + fn ); + } + + /*! \brief Reimplementation of `foreach_gate`. */ + template + void foreach_gate( Fn&& fn ) const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + detail::foreach_element( topo_order.begin() + offset, + topo_order.end(), + fn ); + } + + /*! \brief Implementation of `foreach_gate` in reverse topological order. */ + template + void foreach_gate_reverse( Fn&& fn ) const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + detail::foreach_element( topo_order.rbegin(), + topo_order.rend() - offset, + fn ); + } + + /*! \brief Reimplementation of `foreach_po`. + * + * If `start_signal` is provided in constructor, only this is returned as + * primary output, otherwise reverts to original `foreach_po` implementation. + */ + template + void foreach_po( Fn&& fn ) const + { + if ( start_signal ) + { + std::vector signals( 1, *start_signal ); + detail::foreach_element( signals.begin(), signals.end(), fn ); + } + else + { + Ntk::foreach_po( fn ); + } + } + + uint32_t num_pos() const + { + return start_signal ? 1 : Ntk::num_pos(); + } + + void update_topo() + { + this->incr_trav_id(); + this->incr_trav_id(); + topo_order.reserve( this->size() ); + + /* constants and PIs */ + const auto c0 = this->get_node( this->get_constant( false ) ); + topo_order.push_back( c0 ); + this->set_visited( c0, this->trav_id() ); + + if ( const auto c1 = this->get_node( this->get_constant( true ) ); this->visited( c1 ) != this->trav_id() ) + { + topo_order.push_back( c1 ); + this->set_visited( c1, this->trav_id() ); + } + + this->foreach_ci( [this]( auto n ) { + if ( this->visited( n ) != this->trav_id() ) + { + topo_order.push_back( n ); + this->set_visited( n, this->trav_id() ); + } + } ); + + if ( start_signal ) + { + if ( this->visited( this->get_node( *start_signal ) ) == this->trav_id() ) + return; + create_topo_rec( this->get_node( *start_signal ) ); + } + else + { + Ntk::foreach_co( [this]( auto f ) { + /* node was already visited */ + if ( this->visited( this->get_node( f ) ) == this->trav_id() ) + return; + + create_topo_rec( this->get_node( f ) ); + } ); + } + } + +private: + void create_topo_rec( node const& n ) + { + /* is permanently marked? */ + if ( this->visited( n ) == this->trav_id() ) + return; + + /* ensure that the node is not temporarily marked */ + assert( this->visited( n ) != this->trav_id() - 1 ); + + /* mark node temporarily */ + this->set_visited( n, this->trav_id() - 1 ); + + /* mark children */ + this->foreach_fanin( n, [this]( signal const& f ) { + create_topo_rec( this->get_node( f ) ); + } ); + + /* mark node n permanently */ + this->set_visited( n, this->trav_id() ); + + /* visit node */ + topo_order.push_back( n ); + } + +private: + std::vector topo_order; + std::optional start_signal; +}; + +template +class topo_view : public Ntk +{ +public: + topo_view( Ntk const& ntk ) : Ntk( ntk ) + { + } +}; + +template +topo_view( T const& ) -> topo_view; + +template +topo_view( T const&, typename T::signal const& ) -> topo_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/algorithm.hpp b/third-party/mockturtle/lib/kitty/kitty/algorithm.hpp new file mode 100644 index 00000000000..d89e6ec2a37 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/algorithm.hpp @@ -0,0 +1,489 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file algorithm.hpp + \brief Implements several generic algorithms on truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include "bit_operations.hpp" +#include "detail/constants.hpp" +#include "static_truth_table.hpp" +#include "partial_truth_table.hpp" + +#include +#include + +namespace kitty +{ + +/*! \brief Perform bitwise unary operation on truth table + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto unary_operation( const TT& tt, Fn&& op ) +{ + auto result = tt.construct(); + std::transform( tt.cbegin(), tt.cend(), result.begin(), op ); + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto unary_operation( const static_truth_table& tt, Fn&& op ) +{ + auto result = tt.construct(); + result._bits = op( tt._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +/*! + \param num_blocks_offset Number of blocks that don't need to be computed + (the first `num_blocks_offset` blocks of `result` will remain the same before computation) + */ +template +void unary_operation( partial_truth_table& result, const partial_truth_table& tt, Fn&& op, int const& num_blocks_offset = 0 ) +{ + result.resize( tt.num_bits() ); + std::transform( tt.cbegin() + num_blocks_offset, tt.cend(), result.begin() + num_blocks_offset, op ); +} +/*! \endcond */ + +/*! \brief Perform bitwise binary operation on two truth tables + + The dimensions of `first` and `second` must match. This is ensured + at compile-time for static truth tables, but at run-time for dynamic + truth tables. + + \param first First truth table + \param second Second truth table + \param op Binary operation that takes as input two words (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto binary_operation( const TT& first, const TT& second, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() ); + + auto result = first.construct(); + std::transform( first.cbegin(), first.cend(), second.cbegin(), result.begin(), op ); + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto binary_operation( const static_truth_table& first, const static_truth_table& second, Fn&& op ) +{ + auto result = first.construct(); + result._bits = op( first._bits, second._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +auto binary_operation( const partial_truth_table& first, const partial_truth_table& second, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() ); + + auto result = first.construct(); + std::transform( first.cbegin(), first.cend(), second.cbegin(), result.begin(), op ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +/*! + \param num_blocks_offset Number of blocks that don't need to be computed + (the first `num_blocks_offset` blocks of `result` will remain the same before computation) + */ +template +void binary_operation( partial_truth_table& result, const partial_truth_table& first, const partial_truth_table& second, Fn&& op, int const& num_blocks_offset = 0 ) +{ + assert( first.num_bits() == second.num_bits() ); + + result.resize( first.num_bits() ); + std::transform( first.cbegin() + num_blocks_offset, first.cend(), second.cbegin() + num_blocks_offset, result.begin() + num_blocks_offset, op ); +} +/*! \endcond */ + +/*! \brief Perform bitwise ternary operation on three truth tables + + The dimensions of `first`, `second`, and `third` must match. This + is ensured at compile-time for static truth tables, but at run-time + for dynamic truth tables. + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param op Ternary operation that takes as input three words (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto ternary_operation( const TT& first, const TT& second, const TT& third, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() && second.num_vars() == third.num_vars() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++ ); + } + + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto ternary_operation( const static_truth_table& first, const static_truth_table& second, const static_truth_table& third, Fn&& op ) +{ + auto result = first.construct(); + result._bits = op( first._bits, second._bits, third._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +auto ternary_operation( const partial_truth_table& first, const partial_truth_table& second, const partial_truth_table& third, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() && second.num_bits() == third.num_bits() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++ ); + } + + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \brief Perform bitwise quaternary operation on four truth tables + + The dimensions of all truth tables must match. This + is ensured at compile-time for static truth tables, but at run-time + for dynamic truth tables. + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param fourth Fourth truth table + \param op Quaternary operation that takes as input four words (`uint64_t`) and returns a word + + \return new constructed truth table of same type and dimensions + */ +template +auto quaternary_operation( const TT& first, const TT& second, const TT& third, const TT& fourth, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() && second.num_vars() == third.num_vars() && third.num_vars() == fourth.num_vars() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it4 = fourth.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++, *it4++ ); + } + + result.mask_bits(); + return result; +} + +/*! \cond PRIVATE */ +template +auto quaternary_operation( const static_truth_table& first, const static_truth_table& second, const static_truth_table& third, const static_truth_table& fourth, Fn&& op ) +{ + auto result = first.construct(); + result._bits = op( first._bits, second._bits, third._bits, fourth._bits ); + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +auto quaternary_operation( const partial_truth_table& first, const partial_truth_table& second, const partial_truth_table& third, const partial_truth_table& fourth, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() && second.num_bits() == third.num_bits() && third.num_bits() == fourth.num_bits() ); + + auto result = first.construct(); + auto it1 = first.cbegin(); + const auto it1_e = first.cend(); + auto it2 = second.cbegin(); + auto it3 = third.cbegin(); + auto it4 = fourth.cbegin(); + auto it = result.begin(); + + while ( it1 != it1_e ) + { + *it++ = op( *it1++, *it2++, *it3++, *it4++ ); + } + + result.mask_bits(); + return result; +} +/*! \endcond */ + +/*! \brief Computes a predicate based on two truth tables + + The dimensions of `first` and `second` must match. This is ensured + at compile-time for static truth tables, but at run-time for dynamic + truth tables. + + \param first First truth table + \param second Second truth table + \param op Binary operation that takes as input two words (`uint64_t`) and returns a Boolean + + \return true or false based on the predicate + */ +template +bool binary_predicate( const TT& first, const TT& second, Fn&& op ) +{ + assert( first.num_vars() == second.num_vars() ); + + return std::equal( first.begin(), first.end(), second.begin(), op ); +} + +/*! \cond PRIVATE */ +template +bool binary_predicate( const static_truth_table& first, const static_truth_table& second, Fn&& op ) +{ + return op( first._bits, second._bits ); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +bool binary_predicate( const partial_truth_table& first, const partial_truth_table& second, Fn&& op ) +{ + assert( first.num_bits() == second.num_bits() ); + + return std::equal( first.begin(), first.end(), second.begin(), op ); +} +/*! \endcond */ + +/*! \brief Computes a predicate based on three truth tables + + The dimensions of `first`, `second` and `third` must match. This is ensured + at compile-time for static truth tables, but at run-time for dynamic + truth tables. + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param op Ternary operation that takes as input three words (`uint64_t`) and returns a Boolean + + \return true or false based on the predicate + */ +template +bool ternary_predicate( const TT& first, const TT& second, const TT& third, Fn&& op ) +{ + assert( first.num_blocks() == second.num_blocks() && first.num_blocks() == third.num_blocks() ); + + for ( auto i = 0u; i < first.num_blocks(); ++i ) + { + if ( !op( first._bits[i], second._bits[i], third._bits[i] ) ) + { + return false; + } + } + return true; +} + +/*! \cond PRIVATE */ +template +bool ternary_predicate( const static_truth_table& first, const static_truth_table& second, const static_truth_table& third, Fn&& op ) +{ + return op( first._bits, second._bits, third._bits ); +} +/*! \endcond */ + +/*! \brief Assign computed values to bits + + The functor `op` computes bits which are assigned to the bits of the + truth table. + + \param tt Truth table + \param op Unary operation that takes no input and returns a word (`uint64_t`) +*/ +template +void assign_operation( TT& tt, Fn&& op ) +{ + std::generate( tt.begin(), tt.end(), op ); + tt.mask_bits(); +} + +/*! \cond PRIVATE */ +template +void assign_operation( static_truth_table& tt, Fn&& op ) +{ + tt._bits = op(); + tt.mask_bits(); +} +/*! \endcond */ + +/*! \brief Iterates through each block of a truth table + + The functor `op` is called for every block of the truth table. + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns void +*/ +template +void for_each_block( const TT& tt, Fn&& op ) +{ + std::for_each( tt.cbegin(), tt.cend(), op ); +} + +/*! \brief Iterates through each block of a truth table in reverse + order + + The functor `op` is called for every block of the truth table in + reverse order. + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns void +*/ +template +void for_each_block_reversed( const TT& tt, Fn&& op ) +{ + std::for_each( tt.crbegin(), tt.crend(), op ); +} + +/*! \cond PRIVATE */ +template +void for_each_one_bit_naive( const TT& tt, Fn&& op ) +{ + for ( uint64_t bit = 0u; bit < tt.num_bits(); ++bit ) + { + if ( get_bit( tt, bit ) ) + { + op( bit ); + } + } +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template +void for_each_one_bit_jump( const TT& tt, Fn&& op ) +{ + uint64_t offset = 0, low_bit, value; + + for ( auto block : tt._bits ) + { + while ( block ) + { + low_bit = value = block - ( block & ( block - 1 ) ); + + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + op( offset + detail::de_bruijn64[( static_cast( ( value - ( value >> 1 ) ) * UINT64_C( 0x07EDD5E59A4E28C2 ) ) ) >> 58] ); + + block ^= low_bit; + } + offset += 64; + } +} + +template +void for_each_one_bit_jump( const static_truth_table& tt, Fn&& op ) +{ + uint64_t block = tt._bits; + + while ( block ) + { + uint64_t low_bit = block - ( block & ( block - 1 ) ); + uint64_t value = low_bit; + + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + op( detail::de_bruijn64[( static_cast( ( value - ( value >> 1 ) ) * UINT64_C( 0x07EDD5E59A4E28C2 ) ) ) >> 58] ); + + block ^= low_bit; + } +} +/*! \endcond */ + +/*! \brief Iterates through each 1-bit in the truth table + + The functor `op` is called for every bit position of the truth table + for which the bit is assigned 1. + + \param tt Truth table + \param op Unary operation that takes as input a word (`uint64_t`) and returns void +*/ +template +inline void for_each_one_bit( const TT& tt, Fn&& op ) +{ + for_each_one_bit_naive( tt, op ); +} +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/bit_operations.hpp b/third-party/mockturtle/lib/kitty/kitty/bit_operations.hpp new file mode 100644 index 00000000000..31ac78cddd9 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/bit_operations.hpp @@ -0,0 +1,580 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file bit_operations.hpp + \brief Implements bit manipulation on truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "detail/mscfix.hpp" +#include "partial_truth_table.hpp" +#include "quaternary_truth_table.hpp" +#include "static_truth_table.hpp" +#include "ternary_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Sets bit at index to true + + \param tt Truth table + \param index Bit index +*/ +template +void set_bit( TT& tt, uint64_t index ) +{ + tt._bits[index >> 6] |= uint64_t( 1 ) << ( index & 0x3f ); +} + +/*! \cond PRIVATE */ +template +void set_bit( static_truth_table& tt, uint64_t index ) +{ + tt._bits |= uint64_t( 1 ) << index; +} + +template +void set_bit( ternary_truth_table& tt, uint64_t index, bool value = true ) +{ + set_bit( tt._care, index ); + if ( value ) + set_bit( tt._bits, index ); + else + clear_bit( tt._bits, index ); +} + +template +void set_bit( quaternary_truth_table& tt, uint64_t index, bool value = true ) +{ + if ( value ) + { + set_bit( tt._onset, index ); + clear_bit( tt._offset, index ); + } + else + { + clear_bit( tt._onset, index ); + set_bit( tt._offset, index ); + } +} + +/*! \endcond */ + +/*! \brief Gets bit at index + + \param tt Truth table + \param index Bit index + + \return 1 if bit is set, otherwise 0 +*/ +template +auto get_bit( const TT& tt, uint64_t index ) +{ + return ( tt._bits[index >> 6] >> ( index & 0x3f ) ) & 0x1; +} + +/*! \cond PRIVATE */ +template +auto get_bit( const static_truth_table& tt, uint64_t index ) +{ + return ( tt._bits >> index ) & 0x1; +} + +/*! \brief Gets bit at index + +\param tt Ternary truth table +\param index Bit index + +\return 1 if bit is set, 0 if it is reset or if it is a don't care +*/ +template +auto get_bit( const ternary_truth_table& tt, uint64_t index ) +{ + return get_bit( tt._bits, index ); +} + +/*! \brief Gets bit at index + +\param tt Ternary truth table +\param index Bit index + +\return 1 if bit is set, 0 if it is reset, if it is a don't care, or if it is a don't know +*/ +template +auto get_bit( const quaternary_truth_table& tt, uint64_t index ) +{ + return get_bit( tt._onset, index ) && !get_bit( tt._offset, index ); +} + +/*! \endcond */ + +/*! \brief Clears bit at index (sets bit at index to false) + + \param tt Truth table + \param index Bit index +*/ +template +void clear_bit( TT& tt, uint64_t index ) +{ + tt._bits[index >> 6] &= ~( uint64_t( 1 ) << ( index & 0x3f ) ); +} + +/*! \cond PRIVATE */ +template +void clear_bit( static_truth_table& tt, uint64_t index ) +{ + tt._bits &= ~( uint64_t( 1 ) << index ); +} +/*! \endcond */ + +/*! \brief Flips bit at index + + \param tt Truth table + \param index Bit index +*/ +template +void flip_bit( TT& tt, uint64_t index ) +{ + tt._bits[index >> 6] ^= uint64_t( 1 ) << ( index & 0x3f ); +} + +template +void flip_bit( ternary_truth_table& tt, uint64_t index ) +{ + if ( !get_bit( tt._care, index ) ) + return; + flip_bit( tt._bits, index ); +} + +/*! \cond PRIVATE */ +template +void flip_bit( static_truth_table& tt, uint64_t index ) +{ + tt._bits ^= uint64_t( 1 ) << index; +} + +/*! \brief Checks if a bit in a ternary truth table is a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +bool is_dont_care( const ternary_truth_table& tt, uint64_t index ) +{ + return !get_bit( tt._care, index ); +} + +/*! \brief Checks if a bit in a quaternary truth table is a don't care + + \param tt Quaternary truth table + \param index Bit index +*/ +template +bool is_dont_care( const quaternary_truth_table& tt, uint64_t index ) +{ + return get_bit( tt._onset, index ) && get_bit( tt._offset, index ); +} + +/*! \cond PRIVATE */ +template +bool is_dont_care( const TT& tt, uint64_t index ) +{ + (void)tt; + (void)index; + return false; +} + +/*! \brief Sets a bit in a ternary truth table as a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +void set_dont_care( ternary_truth_table& tt, uint64_t index ) +{ + clear_bit( tt._care, index ); + clear_bit( tt._bits, index ); +} + +/*! \brief Sets a bit in a quaternary truth table as a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +void set_dont_care( quaternary_truth_table& tt, uint64_t index ) +{ + set_bit( tt._onset, index ); + set_bit( tt._offset, index ); +} + +/*! \brief Checks if a bit in a ternary truth table is a don't know + + \param tt Ternary truth table + \param index Bit index +*/ +template +bool is_dont_know( const ternary_truth_table& tt, uint64_t index ) +{ + return !get_bit( tt._care, index ); +} + +/*! \brief Checks if a bit in a quaternary truth table is a don't care + + \param tt Ternary truth table + \param index Bit index +*/ +template +bool is_dont_know( const quaternary_truth_table& tt, uint64_t index ) +{ + return !get_bit( tt._onset, index ) && !get_bit( tt._offset, index ); +} + +/*! \cond PRIVATE */ +template +bool is_dont_know( const TT& tt, uint64_t index ) +{ + (void)tt; + (void)index; + return false; +} + +template +void set_dont_know( quaternary_truth_table& tt, uint64_t index ) +{ + clear_bit( tt._onset, index ); + clear_bit( tt._offset, index ); +} + +/*! Returns a block of bits vector. + */ +template +uint64_t get_block( const TT& tt, uint64_t block_index ) +{ + assert( block_index < tt.num_blocks() ); + return tt._bits[block_index]; +} + +template +uint64_t get_block( const static_truth_table& tt, uint64_t block_index ) +{ + assert( block_index == 0 ); + (void)block_index; + return tt._bits; +} + +/*! \brief Copies bit at index + + Copy the bit from `tt_from` at index `index_from` to `tt_to` at index `index_to`. + + \param tt_from Truth table to copy from + \param index_from Bit index to copy from + \param tt_to Truth table to write to + \param index_to Bit index to write to +*/ +template +void copy_bit( const TTfrom& tt_from, uint64_t index_from, TTto& tt_to, uint64_t index_to ) +{ + if ( get_bit( tt_from, index_from ) ) + { + set_bit( tt_to, index_to ); + } + else + { + clear_bit( tt_to, index_to ); + } +} + +/*! \brief Clears all bits + + \param tt Truth table +*/ +template +void clear( TT& tt ) +{ + std::fill( std::begin( tt._bits ), std::end( tt._bits ), 0 ); +} + +/*! \cond PRIVATE */ +template +void clear( static_truth_table& tt ) +{ + tt._bits = 0; +} +/*! \endcond */ + +/*! \brief Count ones in truth table + + \param tt Truth table +*/ +template +inline uint64_t count_ones( const TT& tt ) +{ + return std::accumulate( tt.cbegin(), tt.cend(), uint64_t( 0 ), + []( auto accu, auto word ) { + return accu + __builtin_popcountll( word ); + } ); +} + +/*! \cond PRIVATE */ +template +inline uint64_t count_ones( const static_truth_table& tt ) +{ + return __builtin_popcountll( tt._bits ); +} + +template +inline uint64_t count_ones( const ternary_truth_table& tt ) +{ + return count_ones( tt._bits & tt._care ); +} +/*! \endcond */ + +/*! \brief Count zeros in truth table + + \param tt Truth table +*/ +template +inline uint64_t count_zeros( const TT& tt ) +{ + return count_ones( ~tt ); +} + +/*! \cond PRIVATE */ +inline int64_t find_first_bit_in_word( uint64_t word ) +{ + int64_t n = 0; + if ( word == 0 ) + { + return -1; + } + + if ( ( word & UINT64_C( 0x00000000FFFFFFFF ) ) == 0 ) + { + n += 32; + word >>= 32; + } + if ( ( word & UINT64_C( 0x000000000000FFFF ) ) == 0 ) + { + n += 16; + word >>= 16; + } + if ( ( word & UINT64_C( 0x00000000000000FF ) ) == 0 ) + { + n += 8; + word >>= 8; + } + if ( ( word & UINT64_C( 0x000000000000000F ) ) == 0 ) + { + n += 4; + word >>= 4; + } + if ( ( word & UINT64_C( 0x0000000000000003 ) ) == 0 ) + { + n += 2; + word >>= 2; + } + if ( ( word & UINT64_C( 0x0000000000000001 ) ) == 0 ) + { + n++; + } + + return n; +} + +inline int64_t find_last_bit_in_word( uint64_t word ) +{ + int64_t n = 0; + if ( word == 0 ) + { + return -1; + } + + if ( ( word & UINT64_C( 0xFFFFFFFF00000000 ) ) == 0 ) + { + n += 32; + word <<= 32; + } + if ( ( word & UINT64_C( 0xFFFF000000000000 ) ) == 0 ) + { + n += 16; + word <<= 16; + } + if ( ( word & UINT64_C( 0xFF00000000000000 ) ) == 0 ) + { + n += 8; + word <<= 8; + } + if ( ( word & UINT64_C( 0xF000000000000000 ) ) == 0 ) + { + n += 4; + word <<= 4; + } + if ( ( word & UINT64_C( 0xC000000000000000 ) ) == 0 ) + { + n += 2; + word <<= 2; + } + if ( ( word & UINT64_C( 0x8000000000000000 ) ) == 0 ) + { + n++; + } + + return 63 - n; +} +/*! \endcond */ + +/*! \brief Finds least-significant one-bit + + Returns -1, if truth table is constant 0. + + \param tt Truth table + \param start Bit to start from (default is 0) +*/ +template +int64_t find_first_one_bit( const TT& tt, int64_t start = 0 ) +{ + uint64_t mask = ~( ( UINT64_C( 1 ) << uint64_t( start % 64 ) ) - 1u ); + auto it = tt.cbegin() + ( start >> 6 ); + if ( it != tt.cend() && ( *it & mask ) != 0 ) + { + return 64 * std::distance( tt.cbegin(), it ) + find_first_bit_in_word( *it & mask ); + } + else if ( it == tt.cend() ) + { + return -1; + } + + it = std::find_if( it + 1, tt.cend(), []( auto word ) { return word != 0; } ); + + if ( it == tt.cend() ) + { + return -1; + } + + return 64 * std::distance( tt.cbegin(), it ) + find_first_bit_in_word( *it ); +} + +/*! \brief Finds most-significant one-bit + + Returns -1, if truth table is constant 0. + + \param tt Truth table +*/ +template +int64_t find_last_one_bit( const TT& tt ) +{ + const auto it = std::find_if( tt.crbegin(), tt.crend(), []( auto word ) { return word != 0; } ); + + if ( it == tt.crend() ) + { + return -1; + } + + return 64 * ( std::distance( it, tt.crend() ) - 1 ) + find_last_bit_in_word( *it ); +} + +/*! \brief Finds least-significant bit difference + + Returns -1, if truth tables are the same + + \param first First truth table + \param second Second truth table +*/ +template +int64_t find_first_bit_difference( const TT& first, const TT& second ) +{ + if constexpr ( std::is_same::value ) + { + assert( first.num_bits() == second.num_bits() ); + } + else + { + assert( first.num_vars() == second.num_vars() ); + } + + auto it = first.cbegin(); + auto it2 = second.cbegin(); + auto w = 0; + + while ( it != first.cend() ) + { + if ( *it ^ *it2 ) + { + return 64 * w + find_first_bit_in_word( *it ^ *it2 ); + } + ++it; + ++it2; + ++w; + } + return -1; +} + +/*! \brief Finds most-significant bit difference + + Returns -1, if truth tables are the same + + \param first First truth table + \param second Second truth table +*/ +template +int64_t find_last_bit_difference( const TT& first, const TT& second ) +{ + if constexpr ( std::is_same::value ) + { + assert( first.num_bits() == second.num_bits() ); + } + else + { + assert( first.num_vars() == second.num_vars() ); + } + + auto it = first.crbegin(); + auto it2 = second.crbegin(); + auto w = first.num_blocks() - 1; + + while ( it != first.crend() ) + { + if ( *it ^ *it2 ) + { + return 64 * w + find_last_bit_in_word( *it ^ *it2 ); + } + ++it; + ++it2; + --w; + } + return -1; +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/constructors.hpp b/third-party/mockturtle/lib/kitty/kitty/constructors.hpp new file mode 100644 index 00000000000..e9de7b5d3fa --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/constructors.hpp @@ -0,0 +1,1590 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file constructors.hpp + \brief Implements operations to construct truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "absl/random/random.h" +#include "cube.hpp" +#include "detail/constants.hpp" +#include "detail/mscfix.hpp" +#include "detail/utils.hpp" +#include "dynamic_truth_table.hpp" +#include "operations.hpp" +#include "operators.hpp" +#include "partial_truth_table.hpp" +#include "static_truth_table.hpp" +#include "ternary_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Creates truth table with number of variables + + If some truth table instance is given, one can create a truth table with the + same type by calling the `construct()` method on it. This function helps if + only the number of variables is known and the base type and uniforms the + creation of static and dynamic truth tables. Note, however, that for static + truth tables `num_vars` must be consistent to the number of variables in the + truth table type. + + \param num_vars Number of variables +*/ +template +inline TT create( unsigned num_vars ) +{ + (void)num_vars; + TT tt; + assert( tt.num_vars() == num_vars ); + return tt; +} + +/*! \cond PRIVATE */ +template<> +inline dynamic_truth_table create( unsigned num_vars ) +{ + return dynamic_truth_table( num_vars ); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +template<> +inline partial_truth_table create( unsigned num_vars ) +{ + return partial_truth_table( 1 << num_vars ); +} +/*! \endcond */ + +/*! \brief Constructs projections (single-variable functions) + + \param tt Truth table + \param var_index Index of the variable, must be smaller than the truth table's number of variables + \param complement If true, realize inverse projection +*/ +template +void create_nth_var( TT& tt, uint8_t var_index, bool complement = false ) +{ + if constexpr ( std::is_same::value ) + { + assert( tt.num_bits() >= ( UINT64_C( 1 ) << var_index ) ); + if ( tt.num_bits() <= 64 ) + { + /* assign from precomputed table */ + tt._bits[0] = complement ? ~detail::projections[var_index] : detail::projections[var_index]; + + /* mask if truth table does not require all bits */ + tt.mask_bits(); + return; + } + } + else + { + if ( tt.num_vars() <= 6 ) + { + /* assign from precomputed table */ + tt._bits[0] = complement ? ~detail::projections[var_index] : detail::projections[var_index]; + + /* mask if truth table does not require all bits */ + tt.mask_bits(); + return; + } + } + + if ( var_index < 6 ) + { + std::fill( std::begin( tt._bits ), std::end( tt._bits ), complement ? ~detail::projections[var_index] : detail::projections[var_index] ); + } + else + { + const auto c = 1 << ( var_index - 6 ); + const auto zero = uint64_t( 0 ); + const auto one = ~zero; + auto block = uint64_t( 0u ); + + while ( block < tt.num_blocks() ) + { + for ( auto i = 0; i < c; ++i ) + { + tt._bits[block++] = complement ? one : zero; + } + for ( auto i = 0; i < c; ++i ) + { + tt._bits[block++] = complement ? zero : one; + } + } + } +} + +/*! \cond PRIVATE */ +template +void create_nth_var( static_truth_table& tt, uint8_t var_index, bool complement = false ) +{ + /* assign from precomputed table */ + tt._bits = complement ? ~detail::projections[var_index] : detail::projections[var_index]; + + /* mask if truth table does not require all bits */ + tt.mask_bits(); +} + +template +void create_nth_var( ternary_truth_table& tt, uint8_t var_index, bool complement = false ) +{ + create_nth_var( tt._bits, var_index, complement ); + tt._care = ~( tt._bits.construct() ); + /* mask if truth table does not require all bits */ + tt.mask_bits(); +} +/*! \endcond */ + +/*! \brief Constructs projections (single-variable functions) out-of-place + + \param tt Truth table + \param var_index Index of the variable, must be smaller than the truth table's number of variables + \param complement If true, realize inverse projection +*/ +template +TT nth_var( uint8_t num_vars, uint8_t var_index, bool complement = false ) +{ + TT tt = create( num_vars ); + create_nth_var( tt, var_index, complement ); + return tt; +} + +/*! \brief Constructs truth table from binary string + + Note that the first character in the string represents the most + significant bit in the truth table. For example, the 2-input AND + function is represented by the binary string "1000". The number of + characters in `binary` must match the number of bits in `tt`. + + \param tt Truth table + \param binary Binary string with as many characters as bits in the truth table +*/ +template +void create_from_binary_string( TT& tt, const std::string& binary ) +{ + assert( binary.size() == tt.num_bits() ); + + clear( tt ); + + size_t i = 0u, j = binary.size(); + do + { + --j; + if ( binary[i++] == '1' ) + { + set_bit( tt, j ); + } + } while ( j ); +} + +/*! \brief Constructs truth table from hexadecimal string + + Note that the first character in the string represents the four most + significant bit in the truth table. For example, the 3-input + majority function is represented by the binary string "E8" or "e8". + The number of characters in `hex` must be one fourth the number of + bits in `tt`. + + \param tt Truth table + \param hex Hexadecimal string +*/ +template +void create_from_hex_string( TT& tt, const std::string& hex ) +{ + clear( tt ); + + /* special case for small truth tables */ + if ( tt.num_vars() < 2 ) + { + assert( hex.size() == 1 ); + const auto i = detail::hex_to_int[static_cast( hex[0] )]; + if ( i & 1 ) + { + set_bit( tt, 0 ); + } + if ( tt.num_vars() == 1u && ( i & 2 ) ) + { + set_bit( tt, 1 ); + } + return; + } + + assert( ( hex.size() << 2 ) == tt.num_bits() ); + + auto j = tt.num_bits() - 1; + + for ( unsigned char c : hex ) + { + const auto i = detail::hex_to_int[c]; + if ( i & 8 ) + { + set_bit( tt, j ); + } + if ( i & 4 ) + { + set_bit( tt, j - 1 ); + } + if ( i & 2 ) + { + set_bit( tt, j - 2 ); + } + if ( i & 1 ) + { + set_bit( tt, j - 3 ); + } + j -= 4; + } +} + +/*! \cond PRIVATE */ +template> +void create_from_hex_string( partial_truth_table& tt, const std::string& hex ) +{ + clear( tt ); + + const auto len = hex.size() << 2; + assert( len >= tt.num_bits() && "truth table length too long" ); + assert( ( len - 4 ) < tt.num_bits() && "truth table length too short" ); + + auto j = tt.num_bits() - 1; + + /* the first char; may not use all 4 bits */ + auto i = detail::hex_to_int[static_cast( hex[0] )]; + + auto s = len - tt.num_bits(); /* number of leading bits not used. 0 <= s < 4 */ + + if ( ( s <= 0u ) && ( i & 8 ) ) + { + set_bit( tt, j ); + } + if ( ( s <= 1u ) && ( i & 4 ) ) + { + set_bit( tt, j + s - 1 ); + } + if ( ( s <= 2u ) && ( i & 2 ) ) + { + set_bit( tt, j + s - 2 ); + } + if ( ( s <= 3u ) && ( i & 1 ) ) + { + set_bit( tt, j + s - 3 ); + } + j -= static_cast( 4u - s ); + + for ( auto c = 1u; c < hex.size(); ++c ) + { + i = detail::hex_to_int[static_cast( hex[c] )]; + if ( i & 8 ) + { + set_bit( tt, j ); + } + if ( i & 4 ) + { + set_bit( tt, j - 1 ); + } + if ( i & 2 ) + { + set_bit( tt, j - 2 ); + } + if ( i & 1 ) + { + set_bit( tt, j - 3 ); + } + j -= 4; + } +} +/*! \endcond */ + +/*! \brief Creates string from raw character data + + Can create a truth table from the data that is produced by + `print_raw`, e.g., from binary files or `std::stringstream`. + + \param tt Truth table + \param in Input stream +*/ +template +void create_from_raw( TT& tt, std::istream& in ) +{ + std::for_each( tt.begin(), tt.end(), [&in]( auto& word ) + { in.read( reinterpret_cast( &word ), sizeof( word ) ); } ); +} + +/*! \brief Constructs a truth table from random value + + Computes random words and assigns them to the truth table. The + number of variables is determined from the truth table. + + \param tt Truth table + \param seed Random seed +*/ +template +void create_random( TT& tt, std::default_random_engine::result_type seed ) +{ + std::seed_seq seed_seq{seed}; + absl::BitGen gen(seed_seq); + + assign_operation(tt, [&gen]() { + return absl::Uniform( + gen, 0ul, std::numeric_limits::max()); + }); +} + +/*! \brief Constructs a truth table from random value + + Computes random words and assigns them to the truth table. The + number of variables is determined from the truth table. Seed is + taken from current time. + + \param tt Truth table +*/ +template +void create_random( TT& tt ) +{ + create_random( tt, static_cast( std::chrono::system_clock::now().time_since_epoch().count() ) ); +} + +/*! \brief Constructs a truth table from a range of words + + The range of words is given in terms of a begin and end iterator. + Hence, it's possible to copy words from a C++ container or a C + array. + + \param tt Truth table + \param begin Begin iterator + \param end End iterator +*/ +template +void create_from_words( TT& tt, InputIt begin, InputIt end ) +{ + assert( std::distance( begin, end ) == static_cast( tt.num_blocks() ) ); + std::copy( begin, end, tt.begin() ); +} + +/*! \brief Creates truth table from cubes representation + + A sum-of-product is represented as a vector of products (called + cubes). + + An empty truth table is given as first argument to determine type + and number of variables. Literals in products that do not fit the + number of variables of the truth table are ignored. + + The cube representation only allows truth table sizes up to 32 + variables. + + \param tt Truth table + \param cubes Vector of cubes + \param esop Use ESOP instead of SOP +*/ +template::value>> +void create_from_cubes( TT& tt, const std::vector& cubes, bool esop = false ) +{ + /* we collect product terms for an (E)SOP, start with const0 */ + clear( tt ); + + for ( auto cube : cubes ) + { + auto product = ~tt.construct(); /* const1 of same size */ + + auto bits = cube._bits; + auto mask = cube._mask; + + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( mask & 1 ) + { + auto var = tt.construct(); + create_nth_var( var, i, !( bits & 1 ) ); + product &= var; + } + bits >>= 1; + mask >>= 1; + } + + if ( esop ) + { + tt ^= product; + } + else + { + tt |= product; + } + } +} + +/*! \brief Creates truth table from clause representation + + A product-of-sum is represented as a vector of sums (called clauses). + + An empty truth table is given as first argument to determine type + and number of variables. Literals in sums that do not fit the + number of variables of the truth table are ignored. + + The clause representation only allows truth table sizes up to 32 + variables. + + \param tt Truth table + \param clauses Vector of clauses + \param esop Use product of exclusive sums instead of POS +*/ +template::value>> +void create_from_clauses( TT& tt, const std::vector& clauses, bool esop = false ) +{ + /* we collect product terms for an (E)SOP, start with const0 */ + clear( tt ); + tt = ~tt; + + for ( auto clause : clauses ) + { + auto sum = tt.construct(); /* const1 of same size */ + + auto bits = clause._bits; + auto mask = clause._mask; + + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( mask & 1 ) + { + auto var = tt.construct(); + create_nth_var( var, i, !( bits & 1 ) ); + + if ( esop ) + { + sum ^= var; + } + else + { + sum |= var; + } + } + bits >>= 1; + mask >>= 1; + } + + tt &= sum; + } +} + +/*! \brief Constructs majority-n function + + The number of variables is determined from the truth table. + + \param tt Truth table +*/ +template::value>> +inline void create_majority( TT& tt ) +{ + create_threshold( tt, tt.num_vars() >> 1 ); +} + +/*! \brief Constructs threshold function + + The resulting function is true, if strictly more than `threshold` inputs are + 1. The number of variables is determined from the truth table. + + \param tt Truth table + \param threshold threshold value +*/ +template +void create_threshold( TT& tt, uint8_t threshold ) +{ + clear( tt ); + + for ( uint64_t x = 0; x < tt.num_bits(); ++x ) + { + if ( __builtin_popcount( static_cast( x ) ) > threshold ) + { + set_bit( tt, x ); + } + } +} + +/*! \brief Constructs equals-k function + + The resulting function is true, if exactly `bitcount` bits are 1. The number + of variables is determiend from the truth table. + + \param tt Truth table + \param bitcount equals-k value +*/ +template::value>> +void create_equals( TT& tt, uint8_t bitcount ) +{ + clear( tt ); + if ( bitcount > tt.num_vars() ) + return; + + if ( tt.num_vars() <= 6 ) + { + const auto word = detail::onehots[tt.num_vars()][bitcount]; + create_from_words( tt, &word, &word + 1 ); + } + else + { + for ( uint64_t x = 0; x < tt.num_bits(); ++x ) + { + if ( __builtin_popcount( static_cast( x ) ) == bitcount ) + { + set_bit( tt, x ); + } + } + } +} + +/*! \brief Constructs symmetric function + + Bits in `counts` are numbered from 0 to 63. If bit `i` is set in `counts`, + the created truth table will evaluate to true, if `i` bits are set in the + input assignment. + + \param tt Truth table + \param counts Bitcount mask +*/ +template +void create_symmetric( TT& tt, uint64_t counts ) +{ + clear( tt ); + + for ( uint64_t x = 0; x < tt.num_bits(); ++x ) + { + if ( ( counts >> __builtin_popcount( static_cast( x ) ) ) & 1 ) + { + set_bit( tt, x ); + } + } +} + +/*! \brief Constructs parity function over n variables + + The number of variables is determined from the truth table. + + \param tt Truth table +*/ +template::value>> +void create_parity( TT& tt ) +{ + clear( tt ); + + *tt.begin() = UINT64_C( 0x6996966996696996 ); + + if ( tt.num_vars() < 6 ) + { + tt.mask_bits(); + } + else if ( tt.num_vars() > 6 ) + { + for ( auto i = 1u; i < tt.num_blocks(); i <<= 1 ) + { + std::transform( tt.begin(), tt.begin() + i, tt.begin() + i, []( auto const& block ) + { return ~block; } ); + } + } +} + +/*! \cond PRIVATE */ +template::value>> +bool create_from_chain( TT& tt, Fn&& next_line, std::vector& steps, std::string* error ) +{ + /* in case of error (makes code more readable) */ + auto fail_with = [&error]( const std::string& line, const std::string& message ) + { + if ( error ) + { + *error = "error in \"" + line + "\": " + message; + } + return false; + }; + + /* initialize variable steps */ + steps.clear(); + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + auto var = tt.construct(); + create_nth_var( var, i ); + steps.push_back( var ); + } + + auto next_step = tt.num_vars() + 1; + + std::string line; + while ( !( line = next_line() ).empty() ) + { + detail::trim( line ); + + /* first character must be an x */ + if ( line[0] != 'x' ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + /* find equals sign */ + const auto eq = line.find( '=' ); + if ( eq == std::string::npos ) + { + return fail_with( line, "no equal sign found" ); + } + + /* next step id */ + const auto step = static_cast( std::stoul( line.substr( 1, eq - 1 ) ) ); + if ( step != next_step ) + { + return fail_with( line, "steps are not in order" ); + } + + line = detail::trim_copy( line.substr( eq + 1 ) ); + + if ( line.empty() ) + { + return fail_with( line, "line uncompleted" ); + } + + /* first character must be an x */ + if ( line[0] != 'x' ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + std::size_t op_pos = 0; + const auto op1 = static_cast( std::stoul( line.substr( 1 ), &op_pos ) ); + + if ( op1 < 1 || op1 >= step ) + { + return fail_with( line, "invalid operand index" ); + } + + line = detail::trim_copy( line.substr( op_pos + 1 ) ); + + if ( line.empty() ) + { + return fail_with( line, "line uncompleted" ); + } + + const auto next_x = line.find( 'x' ); + + if ( next_x == std::string::npos ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + const auto op_code = detail::trim_copy( line.substr( 0, next_x ) ); + + line = detail::trim_copy( line.substr( next_x ) ); + + if ( line.empty() ) + { + return fail_with( line, "line uncompleted" ); + } + + /* first character must be an x */ + if ( line[0] != 'x' ) + { + return fail_with( line, "variables must be prefixed with x" ); + } + + const auto op2 = static_cast( std::stoul( line.substr( 1 ) ) ); + + if ( op2 < 1 || op2 >= step ) + { + return fail_with( line, "invalid operand index" ); + } + + /* now process arguments */ + auto tt_step = tt.construct(); + + if ( op_code == "!|" ) + { + tt_step = ~binary_or( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == ">" ) + { + tt_step = binary_and( steps[op1 - 1], ~steps[op2 - 1] ); + } + else if ( op_code == "<" ) + { + tt_step = binary_and( ~steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "^" ) + { + tt_step = binary_xor( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "!&" ) + { + tt_step = ~binary_and( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "&" ) + { + tt_step = binary_and( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "=" ) + { + tt_step = ~binary_xor( steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == "<=" ) + { + tt_step = binary_or( ~steps[op1 - 1], steps[op2 - 1] ); + } + else if ( op_code == ">=" ) + { + tt_step = binary_or( steps[op1 - 1], ~steps[op2 - 1] ); + } + else if ( op_code == "|" ) + { + tt_step = binary_or( steps[op1 - 1], steps[op2 - 1] ); + } + else + { + return fail_with( op_code, "invalid operator" ); + } + steps.push_back( tt_step ); + ++next_step; + } + + return true; +} +/*! \endcond */ + +/*! \brief Constructs truth table from Boolean chain + + If ``tt`` has \f$n\f$ variables, then each string in ``steps`` is of the form + + \verbatim embed:rst + :: + + x = x x + \endverbatim + + where ```` is an increasing number starting from \f$n + 1\f$, and ```` + and ```` refer to previous steps or primary inputs where \f$j < i\f$ and + \f$k < i\f$. Primary inputs are indexed from \f$1\f$ to \f$n\f$. The last + computed step will be assigned to ``tt``. The following operators are + supported: + + \verbatim embed:rst + +----------+-------------------------+-------------+ + | ```` | Operation | Truth table | + +==========+=========================+=============+ + | ``"!|"`` | Nondisjunction | 0001 | + +----------+-------------------------+-------------+ + | ``">"`` | Nonimplication | 0010 | + +----------+-------------------------+-------------+ + | ``"<"`` | Converse nonimplication | 0100 | + +----------+-------------------------+-------------+ + | ``"^"`` | Exclusive disjunction | 0110 | + +----------+-------------------------+-------------+ + | ``"!&"`` | Nonconjunction | 0111 | + +----------+-------------------------+-------------+ + | ``"&"`` | Conjunction | 1000 | + +----------+-------------------------+-------------+ + | ``"="`` | Equivalence | 1001 | + +----------+-------------------------+-------------+ + | ``">="`` | Nonimplication | 1011 | + +----------+-------------------------+-------------+ + | ``"<="`` | Implication | 1101 | + +----------+-------------------------+-------------+ + | ``"|"`` | Disjunction | 1110 | + +----------+-------------------------+-------------+ + \endverbatim + + The following example will generate the majority function: + + \verbatim embed:rst + .. code-block:: cpp + + kitty::static_truth_table<3> tt; + kitty::create_from_chain( tt, {"x4 = x1 & x2", + "x5 = x1 & x3", + "x6 = x2 & x3", + "x7 = x4 | x5", + "x8 = x6 | x7"} ); + \endverbatim + + If parsing fails, the function returns ``false``, and if ``error`` is not + ``nullptr``, it contains a descriptive reason, why parsing failed. Otherwise, + the function returns ``true``. + + \param tt Truth table + \param steps Vector of steps + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_from_chain( TT& tt, const std::vector& steps, std::string* error = nullptr ) +{ + std::vector vec_steps; + auto it = steps.begin(); + if ( !create_from_chain( + tt, [&it, &steps]() + { return ( it != steps.end() ) ? *it++ : std::string(); }, + vec_steps, error ) ) + { + return false; + } + + tt = vec_steps.back(); + return true; +} + +/*! \brief Constructs truth tables from Boolean chain + + Like ``create_from_chain``, but also returns all internally computed steps. + + \param num_vars Number of input variables + \param tts Truth table for all steps, tt[i] corresponds to step x\f$(i + 1)\f$ + \param steps Vector of steps + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_multiple_from_chain( unsigned num_vars, std::vector& tts, const std::vector& steps, std::string* error = nullptr ) +{ + auto tt = create( num_vars ); + tts.clear(); + auto it = steps.begin(); + if ( !create_from_chain( + tt, [&it, &steps]() + { return ( it != steps.end() ) ? *it++ : std::string(); }, + tts, error ) ) + { + return false; + } + + tt = tts.back(); + return true; +} + +/*! \brief Constructs truth table from Boolean chain + + Like the other ``create_from_chain`` function, but reads chain from an input + stream instead of a vector of strings. Lines are separated by a new line. + Empty lines are skipped over. + + \param tt Truth table + \param in Input stream to read chain + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_from_chain( TT& tt, std::istream& in, std::string* error = nullptr ) +{ + std::vector vec_steps; + if ( !create_from_chain( + tt, [&in]() + { + std::string line; + while ( true ) + { + if ( std::getline( in, line ) ) + { + detail::trim( line ); + if ( !line.empty() ) + { + return line; + } + } + else + { + return std::string(); + } + } }, + vec_steps, error ) ) + { + return false; + } + + tt = vec_steps.back(); + return true; +} + +/*! \brief Constructs truth tables from Boolean chain + + Like ``create_from_chain``, but also returns all internally computed steps. + + \param num_vars Number of input variables + \param tts Truth table for all steps, tt[i] corresponds to step x\f$(i + 1)\f$ + \param in Input stream to read chain + \param error If not null, a pointer to store the error message + + \return True on success +*/ +template::value>> +bool create_multiple_from_chain( unsigned num_vars, std::vector& tts, std::istream& in, std::string* error = nullptr ) +{ + auto tt = create( num_vars ); + tts.clear(); + if ( !create_from_chain( + tt, [&in]() + { + std::string line; + while ( true ) + { + if ( std::getline( in, line ) ) + { + detail::trim( line ); + if ( !line.empty() ) + { + return line; + } + } + else + { + return std::string(); + } + } }, + tts, error ) ) + { + return false; + } + + tt = tts.back(); + return true; +} + +/*! \brief Creates characteristic function + + Creates the truth table of the characteristic function, which contains one + additional variable. The new output variable will be the most-significant + variable of the new function. + + \param tt Truth table for characteristic function + \param from Input truth table +*/ +template::value>> +inline void create_characteristic( TT& tt, const TTFrom& from ) +{ + assert( tt.num_vars() == from.num_vars() + 1 ); + + auto var = tt.construct(); + create_nth_var( var, from.num_vars() ); + + auto ext = tt.construct(); + extend_to_inplace( ext, from ); + + tt = ~var ^ ext; +} + +/*! \brief Creates truth table from textual expression + + An expression `E` is a constant `0` or `1`, or a truth table `a`, + `b`, ..., `p` from the vector `input_tts`, the negation of an + expression `!E`, the conjunction of multiple expressions `(E...E)`, + the disjunction of multiple expressions `{E...E}`, the exclusive OR + of multiple expressions `[E...E]`, or the majority of three + expressions ``. + + \param tt Truth table + \param from Expression as string + \param input_tts Input truth tables assigned to a, b, ... +*/ +template::value>> +bool create_from_expression( TT& tt, const std::string& expression, std::vector const& input_tts ) +{ + enum stack_symbols + { + FUNC, + AND, + OR, + XOR, + MAJ, + NEG + }; + std::stack symbols; + std::stack truth_tables; + + const auto push_tt = [&]( TT& func ) + { + while ( !symbols.empty() && symbols.top() == NEG ) + { + func = ~func; + symbols.pop(); + } + symbols.push( FUNC ); + truth_tables.push( func ); + }; + + for ( auto const& c : expression ) + { + switch ( c ) + { + default: + if ( c >= 'a' && c <= 'p' ) + { + assert( input_tts.size() > uint64_t( c - 'a' ) ); + auto var = input_tts[c - 'a']; + push_tt( var ); + } + else + { + std::cerr << "[e] unexpected symbol in expression: " << c << "\n"; + return false; + } + break; + case '0': + { + auto func = tt.construct(); + push_tt( func ); + } + break; + case '1': + { + auto func = ~tt.construct(); + push_tt( func ); + } + break; + case '!': + symbols.push( NEG ); + break; + case '(': + symbols.push( AND ); + break; + case '{': + symbols.push( OR ); + break; + case '[': + symbols.push( XOR ); + break; + case '<': + symbols.push( MAJ ); + break; + case ')': + { + auto func = ~tt.construct(); + while ( !symbols.empty() && symbols.top() == FUNC ) + { + func &= truth_tables.top(); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != AND ) + { + std::cerr << "[e] could not parse AND expression\n"; + return false; + } + symbols.pop(); + push_tt( func ); + } + break; + case '}': + { + auto func = tt.construct(); + while ( !symbols.empty() && symbols.top() == FUNC ) + { + func |= truth_tables.top(); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != OR ) + { + std::cerr << "[e] could not parse OR expression\n"; + return false; + } + symbols.pop(); + push_tt( func ); + } + break; + case ']': + { + auto func = tt.construct(); + while ( !symbols.empty() && symbols.top() == FUNC ) + { + func ^= truth_tables.top(); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != XOR ) + { + std::cerr << "[e] could not parse XOR expression\n"; + return false; + } + symbols.pop(); + push_tt( func ); + } + break; + case '>': + { + std::vector children; + while ( !symbols.empty() && symbols.top() == FUNC ) + { + children.push_back( truth_tables.top() ); + symbols.pop(); + truth_tables.pop(); + } + if ( symbols.empty() || symbols.top() != MAJ ) + { + std::cerr << "[e] could not parse MAJ expression\n"; + return false; + } + if ( children.size() != 3u ) + { + std::cerr << "[e] MAJ expression must have three children\n"; + return false; + } + symbols.pop(); + auto func = ternary_majority( children[0], children[1], children[2] ); + push_tt( func ); + } + break; + } + } + + if ( symbols.size() != 1 || truth_tables.size() != 1 ) + { + std::cerr << "[e] expression parsing incomplete\n"; + return false; + } + + tt = truth_tables.top(); + return true; +} + +/*! \brief Creates truth table from textual expression + + An expression `E` is a constant `0` or `1`, or a variable `a`, `b`, ..., `p`, + the negation of an expression `!E`, the conjunction of multiple expressions + `(E...E)`, the disjunction of multiple expressions `{E...E}`, the exclusive + OR of multiple expressions `[E...E]`, or the majority of three expressions + ``. Examples are `[(ab)(!ac)]` to describe if-then-else, or `!{!a!b}` + to describe the application of De Morgan's law to `(ab)`. The size of the + truth table must fit the largest variable in the expression, e.g., if `c` is + the largest variable, then the truth table have at least three variables. + + \param tt Truth table + \param from Expression as string +*/ +template::value>> +bool create_from_expression( TT& tt, const std::string& expression ) +{ + std::vector inputs_tts( tt.num_vars() ); + for ( uint8_t i = 0u; i < tt.num_vars(); ++i ) + { + auto var = tt.construct(); + create_nth_var( var, i ); + inputs_tts[i] = var; + } + return create_from_expression( tt, expression, inputs_tts ); +} + +namespace detail +{ +template::value>> +bool formula_execute_operation( std::stack& truth_tables, unsigned const op ) +{ + auto fn1 = truth_tables.top(); + truth_tables.pop(); + auto fn2 = truth_tables.top(); + truth_tables.pop(); + + if ( op == 3 ) /* AND */ + { + truth_tables.push( fn1 & fn2 ); + } + else if ( op == 2 ) /* XOR */ + { + truth_tables.push( fn1 ^ fn2 ); + } + else if ( op == 1 ) /* OR */ + { + truth_tables.push( fn1 | fn2 ); + } + else + { + return false; + } + return true; +} +} /* namespace detail */ + +/*! \brief Creates a truth table from a Boolean formula + + Translates a Boolean expression to a truth table with + the variable names and ordering defined by `var_names`. + The supported Boolean operations are the negation `!a` + or `a'`, the conjunction `a*b` or `a&b` or `a b`, + the disjunction `a+b`, or `a|b`, and the exclusive OR + `a^b`. Brackets `()` can be used for the operation + order. + + \param tt Truth table + \param from Expression as string + \param input_tts Variable names +*/ +template::value>> +bool create_from_formula( TT& tt, const std::string& expression, const std::vector& var_names ) +{ + enum stack_symbols + { + MARK = 0, + OR = 1, + XOR = 2, + AND = 3, + NEG = 4 + }; + + enum stack_op + { + START, + VAR, + OPER, + F_ERROR + }; + + /* create input truth tables */ + std::vector inputs_tts( tt.num_vars() ); + assert( tt.num_vars() < 256 ); + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + auto var = tt.construct(); + create_nth_var( var, static_cast( i ) ); + inputs_tts[i] = var; + } + + std::stack symbols; + std::stack truth_tables; + + /* check brackets */ + unsigned nbrackets = 0; + for ( char const& c : expression ) + { + if ( c == '(' ) + { + ++nbrackets; + } + else if ( c == ')' ) + { + --nbrackets; + } + } + + if ( nbrackets != 0 ) + { + std::cerr << "[e] different number of opening and closing brackets.\n"; + return false; + } + + stack_op flag = START; + auto temp_tt = tt.construct(); + for ( auto i = 0u; i < expression.length(); ++i ) + { + char const c = expression.at( i ); + switch ( c ) + { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + + case '0': + if ( flag == VAR ) + { + std::cerr << "[e] symbol before constant.\n"; + flag = F_ERROR; + break; + } + truth_tables.push( tt.construct() ); + flag = VAR; + break; + + case '1': + if ( flag == VAR ) + { + std::cerr << "[e] symbol before constant.\n"; + flag = F_ERROR; + break; + } + truth_tables.push( ~tt.construct() ); + flag = VAR; + break; + + case '!': + if ( flag == VAR ) + { + /* assuming an AND op */ + symbols.push( AND ); + flag = OPER; + } + symbols.push( NEG ); + break; + + case '\'': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before negation.\n"; + flag = F_ERROR; + break; + } + temp_tt = truth_tables.top(); + truth_tables.pop(); + truth_tables.push( ~temp_tt ); + break; + + case '*': + case '&': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before binary operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( AND ); + flag = OPER; + break; + + case '+': + case '|': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before binary operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( OR ); + flag = OPER; + break; + + case '^': + if ( flag != VAR ) + { + std::cerr << "[e] no variable specified before binary operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( XOR ); + flag = OPER; + break; + + case '(': + if ( flag == VAR ) + { + /* assuming an AND op */ + symbols.push( AND ); + } + symbols.push( MARK ); + flag = START; + break; + + case ')': + if ( symbols.size() != 0 ) + { + while ( 1 ) + { + stack_symbols oper = symbols.top(); + symbols.pop(); + if ( oper == MARK ) + break; + + if ( !detail::formula_execute_operation( truth_tables, oper ) ) + { + std::cerr << "[e] unknown operation.\n"; + flag = F_ERROR; + break; + } + } + } + if ( flag != F_ERROR ) + { + flag = VAR; + } + break; + + default: + /* read variable name */ + auto j = 1u; + while ( i + j < expression.length() && + expression[i + j] != ' ' && expression[i + j] != '\t' && expression[i + j] != '\r' && + expression[i + j] != '\n' && expression[i + j] != '*' && expression[i + j] != '&' && + expression[i + j] != '+' && expression[i + j] != '|' && expression[i + j] != '^' && + expression[i + j] != '\'' && expression[i + j] != ')' ) + { + if ( expression[i + j] == '!' || expression[i + j] == '(' ) + { + std::cerr << "[e] negation sign or open bracket inside variable name.\n"; + flag = F_ERROR; + break; + } + ++j; + } + + uint32_t var_index = 0; + bool match = false; + for ( auto const& v : var_names ) + { + if ( expression.compare( i, j, v ) == 0 ) + { + match = true; + break; + } + ++var_index; + } + + if ( !match ) + { + std::cerr << "[e] cannot find variable " << expression.substr( i, j ) << " in variables list.\n"; + flag = F_ERROR; + break; + } + + if ( flag == VAR ) + { + symbols.push( AND ); + } + + /* increase pointer index */ + i += j - 1; + + truth_tables.push( inputs_tts[var_index] ); + + flag = VAR; + break; + } + + if ( flag == F_ERROR ) + { + break; + } + else if ( flag == START ) + { + continue; + } + else if ( flag == VAR ) + { + /* check if there are negations */ + while ( 1 ) + { + if ( symbols.size() == 0 ) + { + break; + } + auto oper = symbols.top(); + if ( oper == NEG ) + { + temp_tt = truth_tables.top(); + truth_tables.pop(); + truth_tables.push( ~temp_tt ); + symbols.pop(); + } + else + { + break; + } + } + } + else if ( flag == OPER ) + { + while ( 1 ) + { + /* execute the ops with the a higher priority than the last op */ + if ( symbols.size() == 1 ) + { + break; + } + auto op1 = symbols.top(); + symbols.pop(); + auto op2 = symbols.top(); + if ( op2 >= op1 ) + { + symbols.pop(); + /* execute previous op */ + if ( !detail::formula_execute_operation( truth_tables, op2 ) ) + { + std::cerr << "[e] unknown operation.\n"; + flag = F_ERROR; + break; + } + symbols.push( op1 ); + } + else + { + /* push operations back */ + symbols.push( op1 ); + break; + } + } + } + } + + if ( flag != F_ERROR ) + { + /* last operation if present */ + while ( symbols.size() > 0 ) + { + if ( !detail::formula_execute_operation( truth_tables, symbols.top() ) ) + { + std::cerr << "[e] unknown operation.\n"; + flag = F_ERROR; + break; + } + symbols.pop(); + } + + /* assign truth table */ + tt = truth_tables.top(); + } + + return true; +} + +/*! \brief Creates function where on-set corresponds to prime numbers + + This creates a function in which \f$f(x) = 1\f$, if and only if \f$x\f$ is + a prime number in its integer representation. The function only works for + truth tables with at most 10 variables. The number of variables is determined + from the truth table. + + \param tt Truth table +*/ +template::value>> +void create_prime( TT& tt ) +{ + if ( tt.num_vars() > 10 ) + return; + + clear( tt ); + auto p = detail::primes; + + while ( *p < tt.num_bits() ) + { + set_bit( tt, *p++ ); + } +} + +} // namespace kitty diff --git a/third-party/mockturtle/lib/kitty/kitty/cube.hpp b/third-party/mockturtle/lib/kitty/kitty/cube.hpp new file mode 100644 index 00000000000..d634ea50e54 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/cube.hpp @@ -0,0 +1,333 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cube.hpp + \brief A cube data structure for up to 32 variables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "hash.hpp" +#include "detail/mscfix.hpp" + +namespace kitty +{ +class cube +{ +public: + /*! \brief Constructs the empty cube + + Represents the one-cube + */ + cube() : _value( 0 ) {} /* NOLINT */ + + /*! \brief Constructs a cube from bits and mask + + For a valid cube and to be consistent in the ternary values, we + assume that whenever a bit in the care bitmask is set to 0, also + the polarity bitmask must be 0. + + \param bits Polarity bitmask of variables (0: negative, 1: positive) + \param mask Care bitmask of variables (1: part of cube, 0: not part of cube) + */ + cube( uint32_t bits, uint32_t mask ) : _bits( bits ), _mask( mask ) {} /* NOLINT */ + + /*! \brief Constructs a cube from a string + + Each character corresponds to one literal in the cube. Only up to first 32 + characters of the string will be considered, since this data structure + cannot represent cubes with more than 32 literals. A '1' in the string + corresponds to a postive literal, a '0' corresponds to a negative literal. + All other characters represent don't care, but it is customary to use '-'. + + \param str String representing a cube + */ + // cppcheck-suppress noExplicitConstructor + cube( const std::string& str ) /* NOLINT */ + { + _bits = _mask = 0u; + + auto p = str.begin(); + if ( p == str.end() ) + { + return; + } + + for ( uint64_t i = 1; i <= ( uint64_t( 1u ) << 32u ); i <<= 1 ) + { + switch ( *p ) + { + default: /* don't care */ + break; + case '1': + _bits |= i; + /* no break on purpose, jump to 0 and set mask */ + case '0': + _mask |= i; + break; + } + + if ( ++p == str.end() ) + { + return; + } + } + } + + /*! \brief Returns number of literals */ + inline int num_literals() const + { + return __builtin_popcount( _mask ); + } + + /*! \brief Returns the difference to another cube */ + inline int difference( const cube& that ) const + { + return ( _bits ^ that._bits ) | ( _mask ^ that._mask ); + } + + /*! \brief Returns the distance to another cube */ + inline int distance( const cube& that ) const + { + return __builtin_popcount( difference( that ) ); + } + + /*! \brief Checks whether two cubes are equivalent */ + inline bool operator==( const cube& that ) const + { + return _value == that._value; + } + + /*! \brief Checks whether two cubes are not equivalent */ + inline bool operator!=( const cube& that ) const + { + return _value != that._value; + } + + /*! \brief Default comparison operator */ + inline bool operator<( const cube& that ) const + { + return _value < that._value; + } + + /*! \brief Returns the negated cube */ + inline cube operator~() const + { + return { ~_bits, _mask }; + } + + /*! \brief Merges two cubes of distance-1 */ + inline cube merge( const cube& that ) const + { + const auto d = difference( that ); + return { _bits ^ ( ~that._bits & d ), _mask ^ ( that._mask & d ) }; + } + + /*! \brief Adds literal to cube */ + inline void add_literal( uint8_t var_index, bool polarity = true ) + { + set_mask( var_index ); + + if ( polarity ) + { + set_bit( var_index ); + } + else + { + clear_bit( var_index ); + } + } + + /*! \brief Removes literal from cube */ + inline void remove_literal( uint8_t var_index ) + { + clear_mask( var_index ); + clear_bit( var_index ); + } + + /*! \brief Constructs the elementary cube representing a single variable */ + static cube nth_var_cube( uint8_t var_index ) + { + const auto _bits = uint32_t( 1 ) << var_index; + return { _bits, _bits }; + } + + /*! \brief Constructs the elementary cube containing the first k positive literals */ + static cube pos_cube( uint8_t k ) + { + const uint32_t _bits = ( uint64_t( 1 ) << k ) - 1; + return { _bits, _bits }; + } + + /*! \brief Constructs the elementary cube containing the first k negative literals */ + static cube neg_cube( uint8_t k ) + { + const uint32_t _bits = ( uint64_t( 1 ) << k ) - 1; + return { 0u, _bits }; + } + + /*! \brief Prints a cube */ + inline void print( unsigned length = 32u, std::ostream& os = std::cout ) const + { + for ( auto i = 0u; i < length; ++i ) + { + os << ( get_mask( i ) ? ( get_bit( i ) ? '1' : '0' ) : '-' ); + } + } + + /*! \brief Gets bit at index */ + inline bool get_bit( uint8_t index ) const + { + return ( ( _bits >> index ) & 1 ) != 0; + } + + /*! \brief Gets mask at index */ + inline bool get_mask( uint8_t index ) const + { + return ( ( _mask >> index ) & 1 ) != 0; + } + + /*! \brief Sets bit at index */ + inline void set_bit( uint8_t index ) + { + _bits |= ( 1 << index ); + } + + /*! \brief Sets mask at index */ + inline void set_mask( uint8_t index ) + { + _mask |= ( 1 << index ); + } + + /*! \brief Clears bit at index */ + inline void clear_bit( uint8_t index ) + { + _bits &= ~( 1 << index ); + } + + /*! \brief Clears mask at index */ + inline void clear_mask( uint8_t index ) + { + _mask &= ~( 1 << index ); + } + + /*! \brief Flips bit at index */ + inline void flip_bit( uint8_t index ) + { + _bits ^= ( 1 << index ); + } + + /*! \brief Flips mask at index */ + inline void flip_mask( uint8_t index ) + { + _mask ^= ( 1 << index ); + } + + /*! \brief Iterates over all minterms in the cube + * + * The callback function takes a cube as input, which is actually + * a minterm (i.e., all variables are set), and returns a boolean. + * The loop terminates when the callback returns false. + * + * \param length Number of variables in the cube + * \param fn Callback function on each minterm + */ + template + void foreach_minterm( uint8_t length, Fn&& fn ) const + { + foreach_minterm_rec( *this, length, fn ); + } + + template + bool foreach_minterm_rec( cube const& c, uint8_t prev_index, Fn&& fn ) const + { + if ( prev_index == 0 ) + { + return fn( c ); + } + + uint8_t index = prev_index - 1; + if ( !get_mask( index ) ) + { + cube c0 = c; + c0.set_mask( index ); + if ( !foreach_minterm_rec( c0, index, fn ) ) + { + return false; + } + c0.set_bit( index ); + return foreach_minterm_rec( c0, index, fn ); + } + else + { + return foreach_minterm_rec( c, index, fn ); + } + } + + /* cube data */ + union + { + struct + { + uint32_t _bits; + uint32_t _mask; + }; + uint64_t _value; + }; +}; + +/*! \brief Prints all cubes in a vector + + \param cubes Vector of cubes + \param length Number of variables in each cube + \param os Output stream +*/ +inline void print_cubes( const std::vector& cubes, unsigned length = 32u, std::ostream& os = std::cout ) +{ + for ( const auto& cube : cubes ) + { + cube.print( length, os ); + os << '\n'; + } + + os << std::flush; +} + +template<> +struct hash +{ + std::size_t operator()( const cube& c ) const + { + return std::hash{}( c._value ); + } +}; +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/decomposition.hpp b/third-party/mockturtle/lib/kitty/kitty/decomposition.hpp new file mode 100644 index 00000000000..fa368d98fdd --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/decomposition.hpp @@ -0,0 +1,956 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file decomposition.hpp + \brief Check decomposition properties (and perform decomposition) of a function + + \author Mahyar Emami (mahyar.emami@epfl.ch) + \author Mathias Soeken + \author Eleonora Testa +*/ + +#pragma once + +#include +#include + +#include "constructors.hpp" +#include "operations.hpp" +#include "implicant.hpp" +#include "traits.hpp" + +namespace kitty +{ + +enum class top_decomposition +{ + none, + and_, + or_, + lt_, + le_, + xor_ +}; + +enum class bottom_decomposition +{ + none, + and_, + or_, + lt_, + le_, + xor_ +}; + +enum class bi_decomposition +{ + none, + and_, + or_, + xor_, + weak_and_, + weak_or_ +}; + +/*! \brief Checks, whether function is top disjoint decomposable + + \verbatim embed:rst + Checks whether the input function ``tt`` can be represented by the function + :math:`f = g(h(X_1), a)`, where :math:`a \notin X_1`. The return value + is :math:`g`: + + * ``top_decomposition::and_``: :math:`g = a \land h(X_1)` + * ``top_decomposition::or_``: :math:`g = a \lor h(X_1)` + * ``top_decomposition::lt_``: :math:`g = \bar a \land h(X_1)` + * ``top_decomposition::le_``: :math:`g = \bar a \lor h(X_1)` + * ``top_decomposition::xor_``: :math:`g = a \oplus h(X_1)` + * ``top_decomposition::none``: decomposition does not exist + + The function can return the remainder function :math:`h`, whic will not depend + on :math:`a`. + \endverbatim + + \param tt Input function \f$f\f$ + \param var_index Variable \f$a\f$ + \param func If not ``null`` and decomposition exists, its value is assigned the remainder \f$h\f$ + \param allow_xor Set to false to disable XOR decomposition +*/ +template +top_decomposition is_top_decomposable( const TT& tt, uint32_t var_index, TT* func = nullptr, bool allow_xor = true ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + auto var = tt.construct(); + kitty::create_nth_var( var, var_index ); + + if ( implies( tt, var ) ) + { + if ( func ) + { + *func = cofactor1( tt, var_index ); + } + return top_decomposition::and_; + } + else if ( implies( var, tt ) ) + { + if ( func ) + { + *func = cofactor0( tt, var_index ); + } + return top_decomposition::or_; + } + else if ( implies( tt, ~var ) ) + { + if ( func ) + { + *func = cofactor0( tt, var_index ); + } + return top_decomposition::lt_; + } + else if ( implies( ~var, tt ) ) + { + if ( func ) + { + *func = cofactor1( tt, var_index ); + } + return top_decomposition::le_; + } + + if ( allow_xor ) + { + /* try XOR */ + const auto co0 = cofactor0( tt, var_index ); + const auto co1 = cofactor1( tt, var_index ); + + if ( equal( co0, ~co1 ) ) + { + if ( func ) + { + *func = co0; + } + return top_decomposition::xor_; + } + } + + return top_decomposition::none; +} + +/*! \brief Checks, whether function is bottom disjoint decomposable + + \verbatim embed:rst + Checks whether the input function ``tt`` can be represented by the function + :math:`f = h(X_1, g(a, b))`, where :math:`a, b \notin X_1`. The return value + is :math:`g`: + + * ``bottom_decomposition::and_``: :math:`g = a \land b` + * ``bottom_decomposition::or_``: :math:`g = a \lor b` + * ``bottom_decomposition::lt_``: :math:`g = \bar a \land b` + * ``bottom_decomposition::le_``: :math:`g = \bar a \lor b` + * ``bottom_decomposition::xor_``: :math:`g = a \oplus b` + * ``bottom_decomposition::none``: decomposition does not exist + + The function can return the remainder function :math:`h` in where :math:`g` + is substituted by :math:`a`. The remainder function will not depend on + :math:`b`. + \endverbatim + + \param tt Input function \f$f\f$ + \param var_index1 Variable \f$a\f$ + \param var_index2 Variable \f$b\f$ + \param func If not ``null`` and decomposition exists, its value is assigned the remainder \f$h\f$ + \param allow_xor Set to false to disable XOR decomposition +*/ +template +bottom_decomposition is_bottom_decomposable( const TT& tt, uint32_t var_index1, uint32_t var_index2, TT* func = nullptr, bool allow_xor = true ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = cofactor0( tt, var_index1 ); + const auto tt1 = cofactor1( tt, var_index1 ); + + const auto tt00 = cofactor0( tt0, var_index2 ); + const auto tt01 = cofactor1( tt0, var_index2 ); + const auto tt10 = cofactor0( tt1, var_index2 ); + const auto tt11 = cofactor1( tt1, var_index2 ); + + const auto eq01 = equal( tt00, tt01 ); + const auto eq02 = equal( tt00, tt10 ); + const auto eq03 = equal( tt00, tt11 ); + const auto eq12 = equal( tt01, tt10 ); + const auto eq13 = equal( tt01, tt11 ); + const auto eq23 = equal( tt10, tt11 ); + + const auto num_pairs = + static_cast( eq01 ) + + static_cast( eq02 ) + + static_cast( eq03 ) + + static_cast( eq12 ) + + static_cast( eq13 ) + + static_cast( eq23 ); + + if ( num_pairs != 2u && num_pairs != 3 ) + { + return bottom_decomposition::none; + } + + if ( !eq01 && !eq02 && !eq03 ) // 00 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt11, tt00 ); + } + return bottom_decomposition::or_; + } + else if ( !eq01 && !eq12 && !eq13 ) // 01 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt01, tt10 ); + } + return bottom_decomposition::lt_; + } + else if ( !eq02 && !eq12 && !eq23 ) // 10 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt01, tt10 ); + } + return bottom_decomposition::le_; + } + else if ( !eq03 && !eq13 && !eq23 ) // 11 is different + { + if ( func ) + { + *func = mux_var( var_index1, tt11, tt00 ); + } + return bottom_decomposition::and_; + } + else if ( allow_xor ) // XOR + { + if ( func ) + { + *func = mux_var( var_index1, tt01, tt00 ); + } + return bottom_decomposition::xor_; + } + + return bottom_decomposition::none; +} + +/*! \cond PRIVATE */ +namespace detail +{ + +template +TT exist_set( const TT& tt, const std::vector& set ) +{ + auto exist = tt; + for ( auto x = 0u; x < set.size(); x++ ) + { + auto ra_0 = cofactor0( exist, set[x] ); + auto ra_1 = cofactor1( exist, set[x] ); + exist = binary_or( ra_0, ra_1 ); + } + return exist; +} + +template +TT select_one_cube( const TT& q ) +{ + auto m = q.construct(); + auto minterms = kitty::get_minterms( q ); + const uint64_t min = minterms[0]; + set_bit( m, min ); + for ( auto i = 0u; i < q.num_vars(); i++ ) + { + std::vector h( 1, i ); + auto m_p = exist_set( m, h ); + if ( binary_and( m_p, q ) == m_p ) + { + m = m_p; + } + } + return m; +} + +template +bool check_or_decomp( const TT& tt, const TT& dc, const std::vector& i, const std::vector& j ) +{ + auto q = binary_and( tt, dc ); + auto r = binary_and( ~tt, dc ); + auto p = binary_and( q, binary_and( exist_set( r, i ), exist_set( r, j ) ) ); + + return is_const0( p ); +} + +template +std::pair> check_xor_decomp( const TT& tt, const TT& dc, const std::vector& i, const std::vector& j ) +{ + auto q = binary_and( tt, dc ); + auto r = binary_and( ~tt, dc ); + + auto qa = tt.construct(); + auto ra = tt.construct(); + auto qb = tt.construct(); + auto rb = tt.construct(); + + std::vector q_and_rs{ qa, ra, qb, rb }; + while ( !is_const0( q ) ) + { + auto cube = select_one_cube( q ); + qa = binary_or( qa, exist_set( cube, j ) ); + while ( !is_const0( binary_or( qa, ra ) ) ) + { + qb = exist_set( binary_or( binary_and( q, ra ), binary_and( r, qa ) ), i ); + rb = exist_set( binary_or( binary_and( q, qa ), binary_and( r, ra ) ), i ); + if ( !is_const0( binary_and( qb, rb ) ) ) + { + return { false, q_and_rs }; + } + q = binary_and( q, ~binary_or( qa, ra ) ); + r = binary_and( r, ~binary_or( qa, ra ) ); + + q_and_rs[0] = binary_or( q_and_rs[0], qa ); + q_and_rs[1] = binary_or( q_and_rs[1], ra ); + + qa = exist_set( binary_or( binary_and( q, rb ), binary_and( r, qb ) ), j ); + ra = exist_set( binary_or( binary_and( q, qb ), binary_and( r, rb ) ), j ); + if ( !is_const0( binary_and( qa, ra ) ) ) + { + return { false, q_and_rs }; + } + q = binary_and( q, ~binary_or( qb, rb ) ); + r = binary_and( r, ~binary_or( qb, rb ) ); + + q_and_rs[2] = binary_or( q_and_rs[2], qb ); + q_and_rs[3] = binary_or( q_and_rs[3], rb ); + } + } + if ( !is_const0( r ) ) + { + q_and_rs[1] = binary_or( q_and_rs[1], exist_set( r, j ) ); + q_and_rs[3] = binary_or( q_and_rs[3], exist_set( r, i ) ); + } + return { true, q_and_rs }; +} + +template +bool check_weak_decomp( const TT& tt, const TT& dc, const std::vector& i ) +{ + auto p = exist_set( binary_and( ~tt, dc ), i ); + return !is_const0( binary_and( binary_and( tt, dc ), ~p ) ); +} + +template +std::pair, std::vector> find_initial_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector var_a_vect, var_b_vect; + for ( auto& i : s ) + { + for ( auto& j : s ) + { + if ( j == i ) + { + continue; + } + std::vector var_a_p( 1, i ); + std::vector var_b_p( 1, j ); + if ( check_or_decomp( tt, dc, var_a_p, var_b_p ) ) + { + var_a_vect.push_back( i ); + var_b_vect.push_back( j ); + return { var_a_vect, var_b_vect }; + } + } + } + return { var_a_vect, var_b_vect }; +} + +template +std::tuple, std::vector, std::vector> find_initial_xor( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector var_a_vect, var_b_vect; + std::vector q_and_r; + + for ( auto& i : s ) + { + for ( auto& j : s ) + { + if ( j == i ) + { + continue; + } + std::vector var_a_p( 1, i ); + std::vector var_b_p( 1, j ); + auto f = check_xor_decomp( tt, dc, var_a_p, var_b_p ); + if ( f.first ) + { + var_a_vect.push_back( i ); + var_b_vect.push_back( j ); + q_and_r = f.second; + return std::make_tuple( var_a_vect, var_b_vect, q_and_r ); + } + } + } + return std::make_tuple( var_a_vect, var_b_vect, q_and_r ); +} + +template +std::pair, std::vector> find_initial_weak_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector var_a_vect, var_b_vect; + for ( auto& i : s ) + { + std::vector var_a_p( 1, i ); + var_a_p.push_back( i ); + if ( check_weak_decomp( tt, dc, var_a_p ) ) + { + var_a_vect.push_back( i ); + var_b_vect.push_back( i ); + return { var_a_vect, var_b_vect }; + } + } + return { var_a_vect, var_b_vect }; +} + +template +std::pair, std::vector> group_variables_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector xa, xb; + auto var_x = find_initial_or( tt, dc, s ); + xa = var_x.first; + xb = var_x.second; + if ( ( xa.size() == 0 ) && ( xb.size() == 0 ) ) + { + return var_x; + } + for ( auto h : s ) + { + auto it = std::find( xa.begin(), xa.end(), h ); + if ( it != xa.end() ) + { + continue; + } + it = std::find( xb.begin(), xb.end(), h ); + if ( it != xb.end() ) + { + continue; + } + + if ( xa.size() <= xb.size() ) + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + if ( check_or_decomp( tt, dc, xa_p, xb ) ) + { + xa.push_back( h ); + } + else if ( check_or_decomp( tt, dc, xa, xb_p ) ) + { + xb.push_back( h ); + } + } + else + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + if ( check_or_decomp( tt, dc, xa, xb_p ) ) + { + xb.push_back( h ); + } + else if ( check_or_decomp( tt, dc, xa_p, xb ) ) + { + xa.push_back( h ); + } + } + } + return { xa, xb }; +} + +template +std::pair, std::vector> group_variables_weak_or( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector xa, xb; + auto var_x = find_initial_weak_or( tt, dc, s ); + xa = var_x.first; + xb = var_x.second; + if ( ( xa.size() == 0 ) && ( xb.size() == 0 ) ) + { + return var_x; + } + for ( auto h : s ) + { + auto it = std::find( xa.begin(), xa.end(), h ); + if ( it != xa.end() ) + { + continue; + } + auto xa_p = xa; + xa_p.push_back( h ); + if ( check_weak_decomp( tt, dc, xa_p ) ) + { + xa.push_back( h ); + } + } + return { xa, xb }; +} + +template +std::tuple, std::vector, std::vector> group_variables_xor( const TT& tt, const TT& dc, const std::vector& s ) +{ + std::vector xa, xb; + std::vector q_and_r; + auto var_x = find_initial_xor( tt, dc, s ); + xa = std::get<0>( var_x ); + xb = std::get<1>( var_x ); + q_and_r = std::get<2>( var_x ); + if ( ( xa.size() == 0 ) && ( xb.size() == 0 ) ) + { + return var_x; + } + for ( auto h : s ) + { + auto it = std::find( xa.begin(), xa.end(), h ); + if ( it != xa.end() ) + { + continue; + } + it = std::find( xb.begin(), xb.end(), h ); + if ( it != xb.end() ) + { + continue; + } + + if ( xa.size() <= xb.size() ) + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + auto f = check_xor_decomp( tt, dc, xa_p, xb ); + auto f2 = check_xor_decomp( tt, dc, xa, xb_p ); + if ( f.first ) + { + xa.push_back( h ); + q_and_r = f.second; + } + else if ( f2.first ) + { + xb.push_back( h ); + q_and_r = f2.second; + } + } + else + { + auto xa_p = xa; + auto xb_p = xb; + xa_p.push_back( h ); + xb_p.push_back( h ); + auto f = check_xor_decomp( tt, dc, xa_p, xb ); + auto f2 = check_xor_decomp( tt, dc, xa, xb_p ); + if ( f2.first ) + { + xb.push_back( h ); + q_and_r = f2.second; + } + else if ( f.first ) + { + xa.push_back( h ); + q_and_r = f.second; + } + } + } + return std::make_tuple( xa, xb, q_and_r ); +} + +inline std::tuple, std::vector, bi_decomposition> best_variable_grouping( const std::pair, std::vector>& x_or, const std::pair, std::vector>& x_and, const std::pair, std::vector>& x_xor, bool xor_cost ) +{ + if ( xor_cost ) + { + if ( ( x_xor.first.size() != 0 ) && ( x_xor.second.size() != 0 ) ) + { + return std::make_tuple( x_xor.first, x_xor.second, bi_decomposition::xor_ ); + } + } + + int diff_or = static_cast( x_or.first.size() ) - static_cast( x_or.second.size() ); + if ( ( x_or.first.size() == 0 ) || ( x_or.second.size() == 0 ) ) + { + diff_or = 100; + } + if ( diff_or < 0 ) + { + diff_or = -diff_or; + } + + int diff_and = static_cast( x_and.first.size() ) - static_cast( x_and.second.size() ); + if ( ( x_and.first.size() == 0 ) || ( x_and.second.size() == 0 ) ) + { + diff_and = 100; + } + if ( diff_and < 0 ) + { + diff_and = -diff_and; + } + + int diff_xor = static_cast( x_xor.first.size() ) - static_cast( x_xor.second.size() ); + if ( ( x_xor.first.size() == 0 ) || ( x_xor.second.size() == 0 ) ) + { + diff_xor = 100; + } + if ( diff_xor < 0 ) + { + diff_xor = -diff_xor; + } + if ( ( diff_or == 100 ) && ( diff_and == 100 ) && ( diff_xor == 100 ) ) + { + return std::make_tuple( x_or.first, x_or.second, bi_decomposition::none ); + } + if ( ( diff_xor <= diff_and ) && ( diff_xor <= diff_or ) ) + { + return std::make_tuple( x_xor.first, x_xor.second, bi_decomposition::xor_ ); + } + else if ( ( diff_and <= diff_xor ) && ( diff_and <= diff_or ) ) + { + return std::make_tuple( x_and.first, x_and.second, bi_decomposition::and_ ); + } + else // if ( ( diff_xor <= diff_or ) && ( diff_xor <= diff_and ) ) + { + return std::make_tuple( x_or.first, x_or.second, bi_decomposition::or_ ); + } +} + +template +std::vector derive_b( const TT& tt, const TT& dc, const std::tuple, std::vector, bi_decomposition>& x_best, const TT& fa ) +{ + std::vector qb; + if ( ( std::get<2>( x_best ) == bi_decomposition::or_ ) || ( std::get<2>( x_best ) == bi_decomposition::weak_or_ ) ) + { + auto rb = exist_set( binary_and( ~tt, dc ), std::get<0>( x_best ) ); + qb.push_back( exist_set( binary_and( binary_and( tt, dc ), ~fa ), std::get<0>( x_best ) ) ); + assert( !is_const0( rb ) ); + qb.push_back( binary_or( qb[0], rb ) ); + return qb; + } + else // (( std::get<2>( x_best ) == bi_decomposition::and_) || ( std::get<2>( x_best ) == bi_decomposition::weak_and_) ) + { + qb.push_back( exist_set( binary_and( tt, dc ), std::get<0>( x_best ) ) ); + auto rb = exist_set( binary_and( binary_and( ~tt, dc ), ~fa ), std::get<0>( x_best ) ); + qb.push_back( binary_or( qb[0], rb ) ); + assert( !is_const0( qb[0] ) ); + return qb; + } +} + +template +std::vector derive_a( const TT& tt, const TT& dc, const std::tuple, std::vector, bi_decomposition>& x_best ) +{ + std::vector qa; + if ( std::get<2>( x_best ) == bi_decomposition::or_ ) + { + qa.push_back( exist_set( binary_and( binary_and( tt, dc ), exist_set( binary_and( ~tt, dc ), std::get<0>( x_best ) ) ), std::get<1>( x_best ) ) ); + auto ra = exist_set( binary_and( ~tt, dc ), std::get<1>( x_best ) ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } + else if ( std::get<2>( x_best ) == bi_decomposition::and_ ) + { + auto ra = exist_set( binary_and( binary_and( ~tt, dc ), exist_set( binary_and( tt, dc ), std::get<0>( x_best ) ) ), std::get<1>( x_best ) ); + qa.push_back( exist_set( binary_and( tt, dc ), std::get<1>( x_best ) ) ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } + else if ( std::get<2>( x_best ) == bi_decomposition::weak_or_ ) + { + qa.push_back( binary_and( binary_and( tt, dc ), exist_set( binary_and( ~tt, dc ), std::get<0>( x_best ) ) ) ); + auto ra = binary_and( ~tt, dc ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } + else // if ( std::get<2>( x_best ) == bi_decomposition::weak_and_ ) + { + qa.push_back( binary_and( tt, dc ) ); + auto ra = binary_and( binary_and( ~tt, dc ), exist_set( binary_and( tt, dc ), std::get<0>( x_best ) ) ); + qa.push_back( binary_or( qa[0], ra ) ); + return qa; + } +} + +template +std::tuple> is_bi_decomposable( const TT& tt, const TT& dc, bool cost ) +{ + + auto fa = tt.construct(); + auto fb = tt.construct(); + std::vector qa, qb; + + if ( is_const0( dc ) ) + { + return std::make_tuple( dc, bi_decomposition::none, qa ); + } + + std::vector support; + for ( auto x = 0u; x < tt.num_vars(); x++ ) + { + if ( has_var( binary_and( tt, dc ), x ) ) + { + support.push_back( x ); + } + } + + if ( support.size() <= 1 ) + { + return std::make_tuple( tt, bi_decomposition::none, qa ); + } + + auto x_or = detail::group_variables_or( tt, dc, support ); + auto x_and = detail::group_variables_or( ~tt, dc, support ); + auto x_xor = detail::group_variables_xor( tt, dc, support ); + auto x_best = detail::best_variable_grouping( x_or, x_and, std::make_pair( std::get<0>( x_xor ), std::get<1>( x_xor ) ), cost ); + + if ( std::get<2>( x_best ) == bi_decomposition::none ) + { + x_or = detail::group_variables_weak_or( tt, dc, support ); + if ( x_or.first.size() == 0 ) + { + x_and = detail::group_variables_weak_or( ~tt, dc, support ); + x_best = make_tuple( x_and.first, x_and.second, bi_decomposition::weak_and_ ); + } + else + x_best = make_tuple( x_or.first, x_or.second, bi_decomposition::weak_or_ ); + } + else if ( std::get<2>( x_best ) == bi_decomposition::xor_ ) + { + fa = std::get<2>( x_xor )[0]; + fb = std::get<2>( x_xor )[2]; + + qa = std::get<2>( x_xor ); + qa[1] = binary_or( qa[1], qa[0] ); + qa[3] = binary_or( qa[3], qa[2] ); + return std::make_tuple( binary_xor( fa, fb ), bi_decomposition::xor_, qa ); + } + + qa = detail::derive_a( tt, dc, x_best ); + if ( ( std::get<2>( x_best ) == bi_decomposition::or_ ) || ( std::get<2>( x_best ) == bi_decomposition::weak_or_ ) ) + { + qb = detail::derive_b( tt, dc, x_best, qa[0] ); + } + else + { + qb = detail::derive_b( tt, dc, x_best, binary_and( ~qa[0], qa[1] ) ); + } + + fa = qa[0]; + fb = qb[0]; + + qa.push_back( qb[0] ); + qa.push_back( qb[1] ); + + if ( ( std::get<2>( x_best ) == bi_decomposition::and_ ) || ( std::get<2>( x_best ) == bi_decomposition::weak_and_ ) ) + { + return std::make_tuple( binary_and( fa, fb ), std::get<2>( x_best ), qa ); + } + else + { + return std::make_tuple( binary_or( fa, fb ), std::get<2>( x_best ), qa ); + } +} + +} /* namespace detail */ +/* \endcond */ + +/*! \brief Checks whether a function is bi-decomposable. + + \verbatim embed:rst + Checks whether an incompletely specified function (ISF) ``tt`` can be represented by the function + :math:`f = h(l(X_1, X_3), g(X_2, X_3))`. + It returns a tuple of: + 1. :math:'f', which is a completely specified Boolean function compatible with the input ISF + 2. :math:'h', which is the type of decomposition (and, or, xor, weak and, weak or) + 3. :math:'l' and :math:'g', given as ISF (ON-set and DC-set) + + The algorithm is inspired by "An Algorithm for Bi-Decomposition of Logic Functions" by A. Mishchenko et al. + presented in DAC 2001. + + \endverbatim + + \param tt ON-set of the input function \f$f\f$ + \param dc DC-set of the input function \f$f\f$ +*/ + +template +std::tuple> is_bi_decomposable( const TT& tt, const TT& dc ) +{ + return detail::is_bi_decomposable( tt, dc, false ); +} + +/*! \brief Checks whether a function is bi-decomposable using XOR as preferred operation. + + \verbatim embed:rst + Checks whether an incompletely specified function (ISF) ``tt`` can be represented by the function + :math:`f = h(l(X_1, X_3), g(X_2, X_3))`. + It returns a tuple of: + 1. :math:'f', which is a completely specified Boolean function compatible with the input ISF + 2. :math:'h', which is the type of decomposition (and, or, xor, weak and, weak or) + 3. :math:'l' and :math:'g', given as ISF (ON-set and DC-set) + + The algorithm is inspired by "An Algorithm for Bi-Decomposition of Logic Functions" by A. Mishchenko et al. + presented in DAC 2001. + + The cost is changed to add more XOR gates compared to AND/OR gates. This cost function is motivated by + minimizing the number of AND gates in XAGs for cryptography and security applications. For these applications + XOR gates are "free". + + \endverbatim + + \param tt ON-set of the input function \f$f\f$ + \param dc DC-set of the input function \f$f\f$ +*/ + +template +std::tuple> is_bi_decomposable_mc( const TT& tt, const TT& dc ) +{ + return detail::is_bi_decomposable( tt, dc, true ); +} + +namespace detail +{ + +/*! \brief A helper function to enumerate missing indices. + + \param ys_index A list of already selected indices + \param max_index The maximum value for an index + \return Remaining indices +*/ +inline std::vector enumerate_zs_index( const std::vector& ys_index, uint32_t max_index ) +{ + std::vector zs_index; + for ( uint32_t i = 0u; i <= max_index; ++i ) + { + if ( std::find( ys_index.begin(), ys_index.end(), i ) == ys_index.end() ) + { + zs_index.push_back( i ); + } + } + + return zs_index; +} + +} // namespace detail + +/*! \brief Checks, whether a function is Ashenhurst decomposable. + + Given functions f(.), g(.), and h(.) and a partition + on arguments into z and y. This function determines whether + f(x) is decomposable into g(z, h(y)) where x = union(z,y) and + intersect(z, y) = null. + This function does not check for permutation of variables given by + zs_index and ys_index. The elements in these vectors are treated as ordered + values. + + \param tt The function to the check the decomposition on (function f) + \param zs_index The ordered set of indices of vector x (input to f) that + are the inputs to outer_func (g). + \param ys_index The ordered set of indices of vector x (input to f) that are + input to the inner_func (h). + \param outer_func The outer decomposition function (function g). + \param inner_func The inner decomposition function (function h). + \return true if the given decomposition is a valid one, false otherwise. +*/ +template +bool is_ashenhurst_decomposable( const TTf& tt, + const std::vector& zs_index, + const std::vector& ys_index, + const TTg& outer_func, + const TTh& inner_func ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector y_vars; + std::vector z_vars; + + for ( const auto idx : ys_index ) + { + auto var = tt.construct(); + create_nth_var( var, idx ); + y_vars.push_back( var ); + } + for ( const auto idx : zs_index ) + { + auto var = tt.construct(); + create_nth_var( var, idx ); + z_vars.push_back( var ); + } + auto h = compose_truth_table( inner_func, y_vars ); + z_vars.push_back( h ); + auto f = compose_truth_table( outer_func, z_vars ); + return equal( f, tt ); +} + +/*! \brief Finds all of the possible Ashenhurst decompositions of a function + given an input partitioning. + + \param tt The function to find all of its decompositions + \param ys_index Indices indicating the partitioning of inputs + \param decomposition A vector of decomposition pairs. This serves as a return + return container. + \return Returns the number of possible decompositions. +*/ +template +uint32_t ashenhurst_decomposition( const TTf& tt, const std::vector& ys_index, std::vector>& decomposition ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector zs_index = detail::enumerate_zs_index( ys_index, tt.num_vars() - 1 ); + decomposition.clear(); + + // TODO: this does not work for dynamic_truth_table (number of variables not known) + TTg g; + do + { + TTh h; + do + { + if ( is_ashenhurst_decomposable( tt, zs_index, ys_index, g, h ) ) + { + decomposition.emplace_back( g, h ); + } + next_inplace( h ); + } while ( !is_const0( h ) ); + next_inplace( g ); + } while ( !is_const0( g ) ); + return static_cast( decomposition.size() ); +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/constants.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/constants.hpp new file mode 100644 index 00000000000..8f57d04f45c --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/constants.hpp @@ -0,0 +1,196 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file constants.hpp + \brief Collects several important constants + + \author Mathias Soeken +*/ + +/*! \cond PRIVATE */ +#pragma once + +#include +#include + +namespace kitty +{ + +namespace detail +{ + +static constexpr uint64_t projections[] = { + UINT64_C( 0xaaaaaaaaaaaaaaaa ), + UINT64_C( 0xcccccccccccccccc ), + UINT64_C( 0xf0f0f0f0f0f0f0f0 ), + UINT64_C( 0xff00ff00ff00ff00 ), + UINT64_C( 0xffff0000ffff0000 ), + UINT64_C( 0xffffffff00000000 ) }; + +static constexpr uint64_t projections_neg[] = { + UINT64_C( 0x5555555555555555 ), + UINT64_C( 0x3333333333333333 ), + UINT64_C( 0x0f0f0f0f0f0f0f0f ), + UINT64_C( 0x00ff00ff00ff00ff ), + UINT64_C( 0x0000ffff0000ffff ), + UINT64_C( 0x00000000ffffffff ) }; + +static constexpr uint64_t masks[] = { + UINT64_C( 0x0000000000000001 ), + UINT64_C( 0x0000000000000003 ), + UINT64_C( 0x000000000000000f ), + UINT64_C( 0x00000000000000ff ), + UINT64_C( 0x000000000000ffff ), + UINT64_C( 0x00000000ffffffff ), + UINT64_C( 0xffffffffffffffff ) }; + +static constexpr uint64_t permutation_masks[][3] = { + { UINT64_C( 0x9999999999999999 ), UINT64_C( 0x2222222222222222 ), UINT64_C( 0x4444444444444444 ) }, + { UINT64_C( 0xc3c3c3c3c3c3c3c3 ), UINT64_C( 0x0c0c0c0c0c0c0c0c ), UINT64_C( 0x3030303030303030 ) }, + { UINT64_C( 0xf00ff00ff00ff00f ), UINT64_C( 0x00f000f000f000f0 ), UINT64_C( 0x0f000f000f000f00 ) }, + { UINT64_C( 0xff0000ffff0000ff ), UINT64_C( 0x0000ff000000ff00 ), UINT64_C( 0x00ff000000ff0000 ) }, + { UINT64_C( 0xffff00000000ffff ), UINT64_C( 0x00000000ffff0000 ), UINT64_C( 0x0000ffff00000000 ) } }; + +static constexpr uint64_t ppermutation_masks[][6][3] = { + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x9999999999999999 ), UINT64_C( 0x2222222222222222 ), UINT64_C( 0x4444444444444444 ) }, + { UINT64_C( 0xa5a5a5a5a5a5a5a5 ), UINT64_C( 0x0a0a0a0a0a0a0a0a ), UINT64_C( 0x5050505050505050 ) }, + { UINT64_C( 0xaa55aa55aa55aa55 ), UINT64_C( 0x00aa00aa00aa00aa ), UINT64_C( 0x5500550055005500 ) }, + { UINT64_C( 0xaaaa5555aaaa5555 ), UINT64_C( 0x0000aaaa0000aaaa ), UINT64_C( 0x5555000055550000 ) }, + { UINT64_C( 0xaaaaaaaa55555555 ), UINT64_C( 0x00000000aaaaaaaa ), UINT64_C( 0x5555555500000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xc3c3c3c3c3c3c3c3 ), UINT64_C( 0x0c0c0c0c0c0c0c0c ), UINT64_C( 0x3030303030303030 ) }, + { UINT64_C( 0xcc33cc33cc33cc33 ), UINT64_C( 0x00cc00cc00cc00cc ), UINT64_C( 0x3300330033003300 ) }, + { UINT64_C( 0xcccc3333cccc3333 ), UINT64_C( 0x0000cccc0000cccc ), UINT64_C( 0x3333000033330000 ) }, + { UINT64_C( 0xcccccccc33333333 ), UINT64_C( 0x00000000cccccccc ), UINT64_C( 0x3333333300000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xf00ff00ff00ff00f ), UINT64_C( 0x00f000f000f000f0 ), UINT64_C( 0x0f000f000f000f00 ) }, + { UINT64_C( 0xf0f00f0ff0f00f0f ), UINT64_C( 0x0000f0f00000f0f0 ), UINT64_C( 0x0f0f00000f0f0000 ) }, + { UINT64_C( 0xf0f0f0f00f0f0f0f ), UINT64_C( 0x00000000f0f0f0f0 ), UINT64_C( 0x0f0f0f0f00000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xff0000ffff0000ff ), UINT64_C( 0x0000ff000000ff00 ), UINT64_C( 0x00ff000000ff0000 ) }, + { UINT64_C( 0xff00ff0000ff00ff ), UINT64_C( 0x00000000ff00ff00 ), UINT64_C( 0x00ff00ff00000000 ) } }, + { { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ), UINT64_C( 0x0000000000000000 ) }, + { UINT64_C( 0xffff00000000ffff ), UINT64_C( 0x00000000ffff0000 ), UINT64_C( 0x0000ffff00000000 ) } } }; + +static std::vector onehots[] = { + { UINT64_C( 0x1 ) }, + { UINT64_C( 0x1 ), UINT64_C( 0x2 ) }, + { UINT64_C( 0x1 ), UINT64_C( 0x6 ), UINT64_C( 0x8 ) }, + { UINT64_C( 0x01 ), UINT64_C( 0x16 ), UINT64_C( 0x68 ), UINT64_C( 0x80 ) }, + { UINT64_C( 0x0001 ), UINT64_C( 0x0116 ), UINT64_C( 0x1668 ), UINT64_C( 0x6880 ), UINT64_C( 0x8000 ) }, + { UINT64_C( 0x00000001 ), UINT64_C( 0x00010116 ), UINT64_C( 0x01161668 ), UINT64_C( 0x16686880 ), UINT64_C( 0x68808000 ), UINT64_C( 0x80000000 ) }, + { UINT64_C( 0x0000000000000001 ), + UINT64_C( 0x0000000100010116 ), + UINT64_C( 0x0001011601161668 ), + UINT64_C( 0x0116166816686880 ), + UINT64_C( 0x1668688068808000 ), + UINT64_C( 0x6880800080000000 ), + UINT64_C( 0x8000000000000000 ) } }; + +static constexpr int32_t hex_to_int[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +/*! adjacent swap sequences (from 2 to 7 variables) */ +static std::vector swaps[] = { { 0 }, + { 1, 0, 1, 0, 1 }, + { 2, 1, 0, 2, 0, 1, 2, 0, 2, 1, 0, 2, 0, 1, 2, 0, 2, 1, 0, 2, 0, 1, 2 }, + { 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3, 0, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3, 0, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3 }, + { 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4 }, + { 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, + 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, + 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, + 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, + 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, + 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, + 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 2, 5, 4, 3, 2, 1, 0, 5, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 4, 5, 4, 3, 2, 1, 0, 1, 0, 1, 2, 3, 4, 5, 1, 5, 4, 3, 2, 1, 0, 3, 0, 1, 2, 3, 4, 5, 3, 5, 4, 3, 2, 1, 0, 5, + 0, 1, 2, 3, 4, 5 } }; + +/*! flip sequences (from 2 to 6 variables) */ +static std::vector flips[] = { { 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 } }; + +static constexpr uint8_t de_bruijn64[] = { 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5 }; + +static constexpr uint16_t primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, + 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, + 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, + 167, 173, 179, 181, 191, 193, 197, 199, + 211, 223, 227, 229, 233, 239, 241, 251, + 257, 263, 269, 271, 277, 281, 283, 293, + 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, + 401, 409, 419, 421, 431, 433, 439, 443, + 449, 457, 461, 463, 467, 479, 487, 491, + 499, 503, 509, 521, 523, 541, 547, 557, + 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, + 653, 659, 661, 673, 677, 683, 691, 701, + 709, 719, 727, 733, 739, 743, 751, 757, + 761, 769, 773, 787, 797, 809, 811, 821, + 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, + 937, 941, 947, 953, 967, 971, 977, 983, + 991, 997, 1009, 1013, 1019, 1021, 1031 }; + +static constexpr int8_t log2[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, + -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 6 }; + +} // namespace detail +} // namespace kitty + /*! \endcond */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/mscfix.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/mscfix.hpp new file mode 100644 index 00000000000..9453fa6d39c --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/mscfix.hpp @@ -0,0 +1,40 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mscfix.hpp + \brief Fixes some compatibility issues with MS VC compilers + + \author Mathias Soeken +*/ + +#pragma once + +// Use Windows popcount version where appropriate +#ifdef _MSC_VER +#include +#define __builtin_popcount __popcnt +#define __builtin_popcountll __popcnt64 +#endif \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/shift.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/shift.hpp new file mode 100644 index 00000000000..18e5ae37761 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/shift.hpp @@ -0,0 +1,1002 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file shift.hpp + \brief Shift helper functions + + \author Mathias Soeken +*/ + +/*! \cond PRIVATE */ +#pragma once + +#include +#include + +namespace kitty +{ +namespace detail +{ + +// This code has been auto-generated by Nikolaj Bjorner +// https://github.com/NikolajBjorner/z3/blob/17a256eca1a375f023bb98c3e4e728845d92dfb2/src/ast/expr_network.cpp#L22 +inline uint64_t compute_shift( uint64_t x, unsigned code ) +{ + switch ( code ) + { +#define v_x0 ( x & 1 ) +#define v_x1 v_x0 + case 1: + return v_x1; +#define v_x2 ( v_x1 | ( v_x1 << 1 ) ) + case 2: + return v_x2; +#define v_x3 ( x & 3 ) +#define v_x4 v_x3 + case 3: + return v_x4; +#define v_x5 ( v_x2 | ( v_x2 << 2 ) ) + case 4: + return v_x5; +#define v_x6 ( v_x4 | ( v_x4 << 2 ) ) + case 5: + return v_x6; +#define v_x7 ( x & 2 ) +#define v_x8 ( v_x7 << 1 ) +#define v_x9 ( v_x8 | ( v_x8 << 1 ) ) +#define v_x10 ( v_x2 | v_x9 ) + case 6: + return v_x10; +#define v_x11 ( x & 15 ) +#define v_x12 v_x11 + case 7: + return v_x12; +#define v_x13 ( v_x5 | ( v_x5 << 4 ) ) + case 8: + return v_x13; +#define v_x14 ( v_x6 | ( v_x6 << 4 ) ) + case 9: + return v_x14; +#define v_x15 ( v_x10 | ( v_x10 << 4 ) ) + case 10: + return v_x15; +#define v_x16 ( v_x12 | ( v_x12 << 4 ) ) + case 11: + return v_x16; +#define v_x17 ( v_x7 << 3 ) +#define v_x18 ( v_x17 | ( v_x17 << 1 ) ) +#define v_x19 ( v_x18 | ( v_x18 << 2 ) ) +#define v_x20 ( v_x5 | v_x19 ) + case 12: + return v_x20; +#define v_x21 ( x & 12 ) +#define v_x22 ( v_x21 << 2 ) +#define v_x23 ( v_x22 | ( v_x22 << 2 ) ) +#define v_x24 ( v_x6 | v_x23 ) + case 13: + return v_x24; +#define v_x25 ( x & 4 ) +#define v_x26 ( v_x25 << 2 ) +#define v_x27 ( v_x26 | ( v_x26 << 1 ) ) +#define v_x28 ( x & 8 ) +#define v_x29 ( v_x28 << 3 ) +#define v_x30 ( v_x29 | ( v_x29 << 1 ) ) +#define v_x31 ( v_x27 | v_x30 ) +#define v_x32 ( v_x10 | v_x31 ) + case 14: + return v_x32; +#define v_x33 ( x & 255 ) +#define v_x34 v_x33 + case 15: + return v_x34; +#define v_x35 ( v_x13 | ( v_x13 << 8 ) ) + case 16: + return v_x35; +#define v_x36 ( v_x14 | ( v_x14 << 8 ) ) + case 17: + return v_x36; +#define v_x37 ( v_x15 | ( v_x15 << 8 ) ) + case 18: + return v_x37; +#define v_x38 ( v_x16 | ( v_x16 << 8 ) ) + case 19: + return v_x38; +#define v_x39 ( v_x20 | ( v_x20 << 8 ) ) + case 20: + return v_x39; +#define v_x40 ( v_x24 | ( v_x24 << 8 ) ) + case 21: + return v_x40; +#define v_x41 ( v_x32 | ( v_x32 << 8 ) ) + case 22: + return v_x41; +#define v_x42 ( v_x34 | ( v_x34 << 8 ) ) + case 23: + return v_x42; +#define v_x43 ( v_x7 << 7 ) +#define v_x44 ( v_x43 | ( v_x43 << 1 ) ) +#define v_x45 ( v_x44 | ( v_x44 << 2 ) ) +#define v_x46 ( v_x45 | ( v_x45 << 4 ) ) +#define v_x47 ( v_x13 | v_x46 ) + case 24: + return v_x47; +#define v_x48 ( v_x21 << 6 ) +#define v_x49 ( v_x48 | ( v_x48 << 2 ) ) +#define v_x50 ( v_x49 | ( v_x49 << 4 ) ) +#define v_x51 ( v_x14 | v_x50 ) + case 25: + return v_x51; +#define v_x52 ( v_x25 << 6 ) +#define v_x53 ( v_x52 | ( v_x52 << 1 ) ) +#define v_x54 ( v_x28 << 7 ) +#define v_x55 ( v_x54 | ( v_x54 << 1 ) ) +#define v_x56 ( v_x53 | v_x55 ) +#define v_x57 ( v_x56 | ( v_x56 << 4 ) ) +#define v_x58 ( v_x15 | v_x57 ) + case 26: + return v_x58; +#define v_x59 ( x & 240 ) +#define v_x60 ( v_x59 << 4 ) +#define v_x61 ( v_x60 | ( v_x60 << 4 ) ) +#define v_x62 ( v_x16 | v_x61 ) + case 27: + return v_x62; +#define v_x63 ( v_x53 | ( v_x53 << 2 ) ) +#define v_x64 ( v_x28 << 9 ) +#define v_x65 ( v_x64 | ( v_x64 << 1 ) ) +#define v_x66 ( v_x65 | ( v_x65 << 2 ) ) +#define v_x67 ( v_x63 | v_x66 ) +#define v_x68 ( v_x20 | v_x67 ) + case 28: + return v_x68; +#define v_x69 ( x & 48 ) +#define v_x70 ( v_x69 << 4 ) +#define v_x71 ( v_x70 | ( v_x70 << 2 ) ) +#define v_x72 ( x & 192 ) +#define v_x73 ( v_x72 << 6 ) +#define v_x74 ( v_x73 | ( v_x73 << 2 ) ) +#define v_x75 ( v_x71 | v_x74 ) +#define v_x76 ( v_x24 | v_x75 ) + case 29: + return v_x76; +#define v_x77 ( x & 16 ) +#define v_x78 ( v_x77 << 4 ) +#define v_x79 ( v_x78 | ( v_x78 << 1 ) ) +#define v_x80 ( x & 32 ) +#define v_x81 ( v_x80 << 5 ) +#define v_x82 ( v_x81 | ( v_x81 << 1 ) ) +#define v_x83 ( v_x79 | v_x82 ) +#define v_x84 ( x & 64 ) +#define v_x85 ( v_x84 << 6 ) +#define v_x86 ( v_x85 | ( v_x85 << 1 ) ) +#define v_x87 ( x & 128 ) +#define v_x88 ( v_x87 << 7 ) +#define v_x89 ( v_x88 | ( v_x88 << 1 ) ) +#define v_x90 ( v_x86 | v_x89 ) +#define v_x91 ( v_x83 | v_x90 ) +#define v_x92 ( v_x32 | v_x91 ) + case 30: + return v_x92; +#define v_x93 ( x & 65535 ) +#define v_x94 v_x93 + case 31: + return v_x94; +#define v_x95 ( v_x35 | ( v_x35 << 16 ) ) + case 32: + return v_x95; +#define v_x96 ( v_x36 | ( v_x36 << 16 ) ) + case 33: + return v_x96; +#define v_x97 ( v_x37 | ( v_x37 << 16 ) ) + case 34: + return v_x97; +#define v_x98 ( v_x38 | ( v_x38 << 16 ) ) + case 35: + return v_x98; +#define v_x99 ( v_x39 | ( v_x39 << 16 ) ) + case 36: + return v_x99; +#define v_x100 ( v_x40 | ( v_x40 << 16 ) ) + case 37: + return v_x100; +#define v_x101 ( v_x41 | ( v_x41 << 16 ) ) + case 38: + return v_x101; +#define v_x102 ( v_x42 | ( v_x42 << 16 ) ) + case 39: + return v_x102; +#define v_x103 ( v_x47 | ( v_x47 << 16 ) ) + case 40: + return v_x103; +#define v_x104 ( v_x51 | ( v_x51 << 16 ) ) + case 41: + return v_x104; +#define v_x105 ( v_x58 | ( v_x58 << 16 ) ) + case 42: + return v_x105; +#define v_x106 ( v_x62 | ( v_x62 << 16 ) ) + case 43: + return v_x106; +#define v_x107 ( v_x68 | ( v_x68 << 16 ) ) + case 44: + return v_x107; +#define v_x108 ( v_x76 | ( v_x76 << 16 ) ) + case 45: + return v_x108; +#define v_x109 ( v_x92 | ( v_x92 << 16 ) ) + case 46: + return v_x109; +#define v_x110 ( v_x94 | ( v_x94 << 16 ) ) + case 47: + return v_x110; +#define v_x111 ( v_x7 << 15 ) +#define v_x112 ( v_x111 | ( v_x111 << 1 ) ) +#define v_x113 ( v_x112 | ( v_x112 << 2 ) ) +#define v_x114 ( v_x113 | ( v_x113 << 4 ) ) +#define v_x115 ( v_x114 | ( v_x114 << 8 ) ) +#define v_x116 ( v_x35 | v_x115 ) + case 48: + return v_x116; +#define v_x117 ( v_x21 << 14 ) +#define v_x118 ( v_x117 | ( v_x117 << 2 ) ) +#define v_x119 ( v_x118 | ( v_x118 << 4 ) ) +#define v_x120 ( v_x119 | ( v_x119 << 8 ) ) +#define v_x121 ( v_x36 | v_x120 ) + case 49: + return v_x121; +#define v_x122 ( v_x25 << 14 ) +#define v_x123 ( v_x122 | ( v_x122 << 1 ) ) +#define v_x124 ( v_x28 << 15 ) +#define v_x125 ( v_x124 | ( v_x124 << 1 ) ) +#define v_x126 ( v_x123 | v_x125 ) +#define v_x127 ( v_x126 | ( v_x126 << 4 ) ) +#define v_x128 ( v_x127 | ( v_x127 << 8 ) ) +#define v_x129 ( v_x37 | v_x128 ) + case 50: + return v_x129; +#define v_x130 ( v_x59 << 12 ) +#define v_x131 ( v_x130 | ( v_x130 << 4 ) ) +#define v_x132 ( v_x131 | ( v_x131 << 8 ) ) +#define v_x133 ( v_x38 | v_x132 ) + case 51: + return v_x133; +#define v_x134 ( v_x123 | ( v_x123 << 2 ) ) +#define v_x135 ( v_x28 << 17 ) +#define v_x136 ( v_x135 | ( v_x135 << 1 ) ) +#define v_x137 ( v_x136 | ( v_x136 << 2 ) ) +#define v_x138 ( v_x134 | v_x137 ) +#define v_x139 ( v_x138 | ( v_x138 << 8 ) ) +#define v_x140 ( v_x39 | v_x139 ) + case 52: + return v_x140; +#define v_x141 ( v_x69 << 12 ) +#define v_x142 ( v_x141 | ( v_x141 << 2 ) ) +#define v_x143 ( v_x72 << 14 ) +#define v_x144 ( v_x143 | ( v_x143 << 2 ) ) +#define v_x145 ( v_x142 | v_x144 ) +#define v_x146 ( v_x145 | ( v_x145 << 8 ) ) +#define v_x147 ( v_x40 | v_x146 ) + case 53: + return v_x147; +#define v_x148 ( v_x77 << 12 ) +#define v_x149 ( v_x148 | ( v_x148 << 1 ) ) +#define v_x150 ( v_x80 << 13 ) +#define v_x151 ( v_x150 | ( v_x150 << 1 ) ) +#define v_x152 ( v_x149 | v_x151 ) +#define v_x153 ( v_x84 << 14 ) +#define v_x154 ( v_x153 | ( v_x153 << 1 ) ) +#define v_x155 ( v_x87 << 15 ) +#define v_x156 ( v_x155 | ( v_x155 << 1 ) ) +#define v_x157 ( v_x154 | v_x156 ) +#define v_x158 ( v_x152 | v_x157 ) +#define v_x159 ( v_x158 | ( v_x158 << 8 ) ) +#define v_x160 ( v_x41 | v_x159 ) + case 54: + return v_x160; +#define v_x161 ( x & 65280 ) +#define v_x162 ( v_x161 << 8 ) +#define v_x163 ( v_x162 | ( v_x162 << 8 ) ) +#define v_x164 ( v_x42 | v_x163 ) + case 55: + return v_x164; +#define v_x165 ( v_x134 | ( v_x134 << 4 ) ) +#define v_x166 ( v_x28 << 21 ) +#define v_x167 ( v_x166 | ( v_x166 << 1 ) ) +#define v_x168 ( v_x167 | ( v_x167 << 2 ) ) +#define v_x169 ( v_x168 | ( v_x168 << 4 ) ) +#define v_x170 ( v_x165 | v_x169 ) +#define v_x171 ( v_x47 | v_x170 ) + case 56: + return v_x171; +#define v_x172 ( v_x142 | ( v_x142 << 4 ) ) +#define v_x173 ( v_x72 << 18 ) +#define v_x174 ( v_x173 | ( v_x173 << 2 ) ) +#define v_x175 ( v_x174 | ( v_x174 << 4 ) ) +#define v_x176 ( v_x172 | v_x175 ) +#define v_x177 ( v_x51 | v_x176 ) + case 57: + return v_x177; +#define v_x178 ( v_x152 | ( v_x152 << 4 ) ) +#define v_x179 ( v_x84 << 18 ) +#define v_x180 ( v_x179 | ( v_x179 << 1 ) ) +#define v_x181 ( v_x87 << 19 ) +#define v_x182 ( v_x181 | ( v_x181 << 1 ) ) +#define v_x183 ( v_x180 | v_x182 ) +#define v_x184 ( v_x183 | ( v_x183 << 4 ) ) +#define v_x185 ( v_x178 | v_x184 ) +#define v_x186 ( v_x58 | v_x185 ) + case 58: + return v_x186; +#define v_x187 ( x & 3840 ) +#define v_x188 ( v_x187 << 8 ) +#define v_x189 ( v_x188 | ( v_x188 << 4 ) ) +#define v_x190 ( x & 61440 ) +#define v_x191 ( v_x190 << 12 ) +#define v_x192 ( v_x191 | ( v_x191 << 4 ) ) +#define v_x193 ( v_x189 | v_x192 ) +#define v_x194 ( v_x62 | v_x193 ) + case 59: + return v_x194; +#define v_x195 ( v_x149 | ( v_x149 << 2 ) ) +#define v_x196 ( v_x80 << 15 ) +#define v_x197 ( v_x196 | ( v_x196 << 1 ) ) +#define v_x198 ( v_x197 | ( v_x197 << 2 ) ) +#define v_x199 ( v_x195 | v_x198 ) +#define v_x200 ( v_x180 | ( v_x180 << 2 ) ) +#define v_x201 ( v_x87 << 21 ) +#define v_x202 ( v_x201 | ( v_x201 << 1 ) ) +#define v_x203 ( v_x202 | ( v_x202 << 2 ) ) +#define v_x204 ( v_x200 | v_x203 ) +#define v_x205 ( v_x199 | v_x204 ) +#define v_x206 ( v_x68 | v_x205 ) + case 60: + return v_x206; +#define v_x207 ( x & 768 ) +#define v_x208 ( v_x207 << 8 ) +#define v_x209 ( v_x208 | ( v_x208 << 2 ) ) +#define v_x210 ( x & 3072 ) +#define v_x211 ( v_x210 << 10 ) +#define v_x212 ( v_x211 | ( v_x211 << 2 ) ) +#define v_x213 ( v_x209 | v_x212 ) +#define v_x214 ( x & 12288 ) +#define v_x215 ( v_x214 << 12 ) +#define v_x216 ( v_x215 | ( v_x215 << 2 ) ) +#define v_x217 ( x & 49152 ) +#define v_x218 ( v_x217 << 14 ) +#define v_x219 ( v_x218 | ( v_x218 << 2 ) ) +#define v_x220 ( v_x216 | v_x219 ) +#define v_x221 ( v_x213 | v_x220 ) +#define v_x222 ( v_x76 | v_x221 ) + case 61: + return v_x222; +#define v_x223 ( x & 256 ) +#define v_x224 ( v_x223 << 8 ) +#define v_x225 ( v_x224 | ( v_x224 << 1 ) ) +#define v_x226 ( x & 512 ) +#define v_x227 ( v_x226 << 9 ) +#define v_x228 ( v_x227 | ( v_x227 << 1 ) ) +#define v_x229 ( v_x225 | v_x228 ) +#define v_x230 ( x & 1024 ) +#define v_x231 ( v_x230 << 10 ) +#define v_x232 ( v_x231 | ( v_x231 << 1 ) ) +#define v_x233 ( x & 2048 ) +#define v_x234 ( v_x233 << 11 ) +#define v_x235 ( v_x234 | ( v_x234 << 1 ) ) +#define v_x236 ( v_x232 | v_x235 ) +#define v_x237 ( v_x229 | v_x236 ) +#define v_x238 ( x & 4096 ) +#define v_x239 ( v_x238 << 12 ) +#define v_x240 ( v_x239 | ( v_x239 << 1 ) ) +#define v_x241 ( x & 8192 ) +#define v_x242 ( v_x241 << 13 ) +#define v_x243 ( v_x242 | ( v_x242 << 1 ) ) +#define v_x244 ( v_x240 | v_x243 ) +#define v_x245 ( x & 16384 ) +#define v_x246 ( v_x245 << 14 ) +#define v_x247 ( v_x246 | ( v_x246 << 1 ) ) +#define v_x248 ( x & 32768 ) +#define v_x249 ( v_x248 << 15 ) +#define v_x250 ( v_x249 | ( v_x249 << 1 ) ) +#define v_x251 ( v_x247 | v_x250 ) +#define v_x252 ( v_x244 | v_x251 ) +#define v_x253 ( v_x237 | v_x252 ) +#define v_x254 ( v_x92 | v_x253 ) + case 62: + return v_x254; +#define v_x255 ( x & 4294967295 ) +#define v_x256 v_x255 + case 63: + return v_x256; +#define v_x257 ( v_x95 | ( v_x95 << 32 ) ) + case 64: + return v_x257; +#define v_x258 ( v_x96 | ( v_x96 << 32 ) ) + case 65: + return v_x258; +#define v_x259 ( v_x97 | ( v_x97 << 32 ) ) + case 66: + return v_x259; +#define v_x260 ( v_x98 | ( v_x98 << 32 ) ) + case 67: + return v_x260; +#define v_x261 ( v_x99 | ( v_x99 << 32 ) ) + case 68: + return v_x261; +#define v_x262 ( v_x100 | ( v_x100 << 32 ) ) + case 69: + return v_x262; +#define v_x263 ( v_x101 | ( v_x101 << 32 ) ) + case 70: + return v_x263; +#define v_x264 ( v_x102 | ( v_x102 << 32 ) ) + case 71: + return v_x264; +#define v_x265 ( v_x103 | ( v_x103 << 32 ) ) + case 72: + return v_x265; +#define v_x266 ( v_x104 | ( v_x104 << 32 ) ) + case 73: + return v_x266; +#define v_x267 ( v_x105 | ( v_x105 << 32 ) ) + case 74: + return v_x267; +#define v_x268 ( v_x106 | ( v_x106 << 32 ) ) + case 75: + return v_x268; +#define v_x269 ( v_x107 | ( v_x107 << 32 ) ) + case 76: + return v_x269; +#define v_x270 ( v_x108 | ( v_x108 << 32 ) ) + case 77: + return v_x270; +#define v_x271 ( v_x109 | ( v_x109 << 32 ) ) + case 78: + return v_x271; +#define v_x272 ( v_x110 | ( v_x110 << 32 ) ) + case 79: + return v_x272; +#define v_x273 ( v_x116 | ( v_x116 << 32 ) ) + case 80: + return v_x273; +#define v_x274 ( v_x121 | ( v_x121 << 32 ) ) + case 81: + return v_x274; +#define v_x275 ( v_x129 | ( v_x129 << 32 ) ) + case 82: + return v_x275; +#define v_x276 ( v_x133 | ( v_x133 << 32 ) ) + case 83: + return v_x276; +#define v_x277 ( v_x140 | ( v_x140 << 32 ) ) + case 84: + return v_x277; +#define v_x278 ( v_x147 | ( v_x147 << 32 ) ) + case 85: + return v_x278; +#define v_x279 ( v_x160 | ( v_x160 << 32 ) ) + case 86: + return v_x279; +#define v_x280 ( v_x164 | ( v_x164 << 32 ) ) + case 87: + return v_x280; +#define v_x281 ( v_x171 | ( v_x171 << 32 ) ) + case 88: + return v_x281; +#define v_x282 ( v_x177 | ( v_x177 << 32 ) ) + case 89: + return v_x282; +#define v_x283 ( v_x186 | ( v_x186 << 32 ) ) + case 90: + return v_x283; +#define v_x284 ( v_x194 | ( v_x194 << 32 ) ) + case 91: + return v_x284; +#define v_x285 ( v_x206 | ( v_x206 << 32 ) ) + case 92: + return v_x285; +#define v_x286 ( v_x222 | ( v_x222 << 32 ) ) + case 93: + return v_x286; +#define v_x287 ( v_x254 | ( v_x254 << 32 ) ) + case 94: + return v_x287; +#define v_x288 ( v_x256 | ( v_x256 << 32 ) ) + case 95: + return v_x288; +#define v_x289 ( v_x7 << 31 ) +#define v_x290 ( v_x289 | ( v_x289 << 1 ) ) +#define v_x291 ( v_x290 | ( v_x290 << 2 ) ) +#define v_x292 ( v_x291 | ( v_x291 << 4 ) ) +#define v_x293 ( v_x292 | ( v_x292 << 8 ) ) +#define v_x294 ( v_x293 | ( v_x293 << 16 ) ) +#define v_x295 ( v_x95 | v_x294 ) + case 96: + return v_x295; +#define v_x296 ( v_x21 << 30 ) +#define v_x297 ( v_x296 | ( v_x296 << 2 ) ) +#define v_x298 ( v_x297 | ( v_x297 << 4 ) ) +#define v_x299 ( v_x298 | ( v_x298 << 8 ) ) +#define v_x300 ( v_x299 | ( v_x299 << 16 ) ) +#define v_x301 ( v_x96 | v_x300 ) + case 97: + return v_x301; +#define v_x302 ( v_x25 << 30 ) +#define v_x303 ( v_x302 | ( v_x302 << 1 ) ) +#define v_x304 ( v_x28 << 31 ) +#define v_x305 ( v_x304 | ( v_x304 << 1 ) ) +#define v_x306 ( v_x303 | v_x305 ) +#define v_x307 ( v_x306 | ( v_x306 << 4 ) ) +#define v_x308 ( v_x307 | ( v_x307 << 8 ) ) +#define v_x309 ( v_x308 | ( v_x308 << 16 ) ) +#define v_x310 ( v_x97 | v_x309 ) + case 98: + return v_x310; +#define v_x311 ( v_x59 << 28 ) +#define v_x312 ( v_x311 | ( v_x311 << 4 ) ) +#define v_x313 ( v_x312 | ( v_x312 << 8 ) ) +#define v_x314 ( v_x313 | ( v_x313 << 16 ) ) +#define v_x315 ( v_x98 | v_x314 ) + case 99: + return v_x315; +#define v_x316 ( v_x303 | ( v_x303 << 2 ) ) +#define v_x317 ( v_x28 << 33 ) +#define v_x318 ( v_x317 | ( v_x317 << 1 ) ) +#define v_x319 ( v_x318 | ( v_x318 << 2 ) ) +#define v_x320 ( v_x316 | v_x319 ) +#define v_x321 ( v_x320 | ( v_x320 << 8 ) ) +#define v_x322 ( v_x321 | ( v_x321 << 16 ) ) +#define v_x323 ( v_x99 | v_x322 ) + case 100: + return v_x323; +#define v_x324 ( v_x69 << 28 ) +#define v_x325 ( v_x324 | ( v_x324 << 2 ) ) +#define v_x326 ( v_x72 << 30 ) +#define v_x327 ( v_x326 | ( v_x326 << 2 ) ) +#define v_x328 ( v_x325 | v_x327 ) +#define v_x329 ( v_x328 | ( v_x328 << 8 ) ) +#define v_x330 ( v_x329 | ( v_x329 << 16 ) ) +#define v_x331 ( v_x100 | v_x330 ) + case 101: + return v_x331; +#define v_x332 ( v_x77 << 28 ) +#define v_x333 ( v_x332 | ( v_x332 << 1 ) ) +#define v_x334 ( v_x80 << 29 ) +#define v_x335 ( v_x334 | ( v_x334 << 1 ) ) +#define v_x336 ( v_x333 | v_x335 ) +#define v_x337 ( v_x84 << 30 ) +#define v_x338 ( v_x337 | ( v_x337 << 1 ) ) +#define v_x339 ( v_x87 << 31 ) +#define v_x340 ( v_x339 | ( v_x339 << 1 ) ) +#define v_x341 ( v_x338 | v_x340 ) +#define v_x342 ( v_x336 | v_x341 ) +#define v_x343 ( v_x342 | ( v_x342 << 8 ) ) +#define v_x344 ( v_x343 | ( v_x343 << 16 ) ) +#define v_x345 ( v_x101 | v_x344 ) + case 102: + return v_x345; +#define v_x346 ( v_x161 << 24 ) +#define v_x347 ( v_x346 | ( v_x346 << 8 ) ) +#define v_x348 ( v_x347 | ( v_x347 << 16 ) ) +#define v_x349 ( v_x102 | v_x348 ) + case 103: + return v_x349; +#define v_x350 ( v_x316 | ( v_x316 << 4 ) ) +#define v_x351 ( v_x28 << 37 ) +#define v_x352 ( v_x351 | ( v_x351 << 1 ) ) +#define v_x353 ( v_x352 | ( v_x352 << 2 ) ) +#define v_x354 ( v_x353 | ( v_x353 << 4 ) ) +#define v_x355 ( v_x350 | v_x354 ) +#define v_x356 ( v_x355 | ( v_x355 << 16 ) ) +#define v_x357 ( v_x103 | v_x356 ) + case 104: + return v_x357; +#define v_x358 ( v_x325 | ( v_x325 << 4 ) ) +#define v_x359 ( v_x72 << 34 ) +#define v_x360 ( v_x359 | ( v_x359 << 2 ) ) +#define v_x361 ( v_x360 | ( v_x360 << 4 ) ) +#define v_x362 ( v_x358 | v_x361 ) +#define v_x363 ( v_x362 | ( v_x362 << 16 ) ) +#define v_x364 ( v_x104 | v_x363 ) + case 105: + return v_x364; +#define v_x365 ( v_x336 | ( v_x336 << 4 ) ) +#define v_x366 ( v_x84 << 34 ) +#define v_x367 ( v_x366 | ( v_x366 << 1 ) ) +#define v_x368 ( v_x87 << 35 ) +#define v_x369 ( v_x368 | ( v_x368 << 1 ) ) +#define v_x370 ( v_x367 | v_x369 ) +#define v_x371 ( v_x370 | ( v_x370 << 4 ) ) +#define v_x372 ( v_x365 | v_x371 ) +#define v_x373 ( v_x372 | ( v_x372 << 16 ) ) +#define v_x374 ( v_x105 | v_x373 ) + case 106: + return v_x374; +#define v_x375 ( v_x187 << 24 ) +#define v_x376 ( v_x375 | ( v_x375 << 4 ) ) +#define v_x377 ( v_x190 << 28 ) +#define v_x378 ( v_x377 | ( v_x377 << 4 ) ) +#define v_x379 ( v_x376 | v_x378 ) +#define v_x380 ( v_x379 | ( v_x379 << 16 ) ) +#define v_x381 ( v_x106 | v_x380 ) + case 107: + return v_x381; +#define v_x382 ( v_x333 | ( v_x333 << 2 ) ) +#define v_x383 ( v_x80 << 31 ) +#define v_x384 ( v_x383 | ( v_x383 << 1 ) ) +#define v_x385 ( v_x384 | ( v_x384 << 2 ) ) +#define v_x386 ( v_x382 | v_x385 ) +#define v_x387 ( v_x367 | ( v_x367 << 2 ) ) +#define v_x388 ( v_x87 << 37 ) +#define v_x389 ( v_x388 | ( v_x388 << 1 ) ) +#define v_x390 ( v_x389 | ( v_x389 << 2 ) ) +#define v_x391 ( v_x387 | v_x390 ) +#define v_x392 ( v_x386 | v_x391 ) +#define v_x393 ( v_x392 | ( v_x392 << 16 ) ) +#define v_x394 ( v_x107 | v_x393 ) + case 108: + return v_x394; +#define v_x395 ( v_x207 << 24 ) +#define v_x396 ( v_x395 | ( v_x395 << 2 ) ) +#define v_x397 ( v_x210 << 26 ) +#define v_x398 ( v_x397 | ( v_x397 << 2 ) ) +#define v_x399 ( v_x396 | v_x398 ) +#define v_x400 ( v_x214 << 28 ) +#define v_x401 ( v_x400 | ( v_x400 << 2 ) ) +#define v_x402 ( v_x217 << 30 ) +#define v_x403 ( v_x402 | ( v_x402 << 2 ) ) +#define v_x404 ( v_x401 | v_x403 ) +#define v_x405 ( v_x399 | v_x404 ) +#define v_x406 ( v_x405 | ( v_x405 << 16 ) ) +#define v_x407 ( v_x108 | v_x406 ) + case 109: + return v_x407; +#define v_x408 ( v_x223 << 24 ) +#define v_x409 ( v_x408 | ( v_x408 << 1 ) ) +#define v_x410 ( v_x226 << 25 ) +#define v_x411 ( v_x410 | ( v_x410 << 1 ) ) +#define v_x412 ( v_x409 | v_x411 ) +#define v_x413 ( v_x230 << 26 ) +#define v_x414 ( v_x413 | ( v_x413 << 1 ) ) +#define v_x415 ( v_x233 << 27 ) +#define v_x416 ( v_x415 | ( v_x415 << 1 ) ) +#define v_x417 ( v_x414 | v_x416 ) +#define v_x418 ( v_x412 | v_x417 ) +#define v_x419 ( v_x238 << 28 ) +#define v_x420 ( v_x419 | ( v_x419 << 1 ) ) +#define v_x421 ( v_x241 << 29 ) +#define v_x422 ( v_x421 | ( v_x421 << 1 ) ) +#define v_x423 ( v_x420 | v_x422 ) +#define v_x424 ( v_x245 << 30 ) +#define v_x425 ( v_x424 | ( v_x424 << 1 ) ) +#define v_x426 ( v_x248 << 31 ) +#define v_x427 ( v_x426 | ( v_x426 << 1 ) ) +#define v_x428 ( v_x425 | v_x427 ) +#define v_x429 ( v_x423 | v_x428 ) +#define v_x430 ( v_x418 | v_x429 ) +#define v_x431 ( v_x430 | ( v_x430 << 16 ) ) +#define v_x432 ( v_x109 | v_x431 ) + case 110: + return v_x432; +#define v_x433 ( x & 4294901760 ) +#define v_x434 ( v_x433 << 16 ) +#define v_x435 ( v_x434 | ( v_x434 << 16 ) ) +#define v_x436 ( v_x110 | v_x435 ) + case 111: + return v_x436; +#define v_x437 ( v_x350 | ( v_x350 << 8 ) ) +#define v_x438 ( v_x28 << 45 ) +#define v_x439 ( v_x438 | ( v_x438 << 1 ) ) +#define v_x440 ( v_x439 | ( v_x439 << 2 ) ) +#define v_x441 ( v_x440 | ( v_x440 << 4 ) ) +#define v_x442 ( v_x441 | ( v_x441 << 8 ) ) +#define v_x443 ( v_x437 | v_x442 ) +#define v_x444 ( v_x116 | v_x443 ) + case 112: + return v_x444; +#define v_x445 ( v_x358 | ( v_x358 << 8 ) ) +#define v_x446 ( v_x72 << 42 ) +#define v_x447 ( v_x446 | ( v_x446 << 2 ) ) +#define v_x448 ( v_x447 | ( v_x447 << 4 ) ) +#define v_x449 ( v_x448 | ( v_x448 << 8 ) ) +#define v_x450 ( v_x445 | v_x449 ) +#define v_x451 ( v_x121 | v_x450 ) + case 113: + return v_x451; +#define v_x452 ( v_x365 | ( v_x365 << 8 ) ) +#define v_x453 ( v_x84 << 42 ) +#define v_x454 ( v_x453 | ( v_x453 << 1 ) ) +#define v_x455 ( v_x87 << 43 ) +#define v_x456 ( v_x455 | ( v_x455 << 1 ) ) +#define v_x457 ( v_x454 | v_x456 ) +#define v_x458 ( v_x457 | ( v_x457 << 4 ) ) +#define v_x459 ( v_x458 | ( v_x458 << 8 ) ) +#define v_x460 ( v_x452 | v_x459 ) +#define v_x461 ( v_x129 | v_x460 ) + case 114: + return v_x461; +#define v_x462 ( v_x376 | ( v_x376 << 8 ) ) +#define v_x463 ( v_x190 << 36 ) +#define v_x464 ( v_x463 | ( v_x463 << 4 ) ) +#define v_x465 ( v_x464 | ( v_x464 << 8 ) ) +#define v_x466 ( v_x462 | v_x465 ) +#define v_x467 ( v_x133 | v_x466 ) + case 115: + return v_x467; +#define v_x468 ( v_x386 | ( v_x386 << 8 ) ) +#define v_x469 ( v_x454 | ( v_x454 << 2 ) ) +#define v_x470 ( v_x87 << 45 ) +#define v_x471 ( v_x470 | ( v_x470 << 1 ) ) +#define v_x472 ( v_x471 | ( v_x471 << 2 ) ) +#define v_x473 ( v_x469 | v_x472 ) +#define v_x474 ( v_x473 | ( v_x473 << 8 ) ) +#define v_x475 ( v_x468 | v_x474 ) +#define v_x476 ( v_x140 | v_x475 ) + case 116: + return v_x476; +#define v_x477 ( v_x399 | ( v_x399 << 8 ) ) +#define v_x478 ( v_x214 << 36 ) +#define v_x479 ( v_x478 | ( v_x478 << 2 ) ) +#define v_x480 ( v_x217 << 38 ) +#define v_x481 ( v_x480 | ( v_x480 << 2 ) ) +#define v_x482 ( v_x479 | v_x481 ) +#define v_x483 ( v_x482 | ( v_x482 << 8 ) ) +#define v_x484 ( v_x477 | v_x483 ) +#define v_x485 ( v_x147 | v_x484 ) + case 117: + return v_x485; +#define v_x486 ( v_x418 | ( v_x418 << 8 ) ) +#define v_x487 ( v_x238 << 36 ) +#define v_x488 ( v_x487 | ( v_x487 << 1 ) ) +#define v_x489 ( v_x241 << 37 ) +#define v_x490 ( v_x489 | ( v_x489 << 1 ) ) +#define v_x491 ( v_x488 | v_x490 ) +#define v_x492 ( v_x245 << 38 ) +#define v_x493 ( v_x492 | ( v_x492 << 1 ) ) +#define v_x494 ( v_x248 << 39 ) +#define v_x495 ( v_x494 | ( v_x494 << 1 ) ) +#define v_x496 ( v_x493 | v_x495 ) +#define v_x497 ( v_x491 | v_x496 ) +#define v_x498 ( v_x497 | ( v_x497 << 8 ) ) +#define v_x499 ( v_x486 | v_x498 ) +#define v_x500 ( v_x160 | v_x499 ) + case 118: + return v_x500; +#define v_x501 ( x & 16711680 ) +#define v_x502 ( v_x501 << 16 ) +#define v_x503 ( v_x502 | ( v_x502 << 8 ) ) +#define v_x504 ( x & 4278190080 ) +#define v_x505 ( v_x504 << 24 ) +#define v_x506 ( v_x505 | ( v_x505 << 8 ) ) +#define v_x507 ( v_x503 | v_x506 ) +#define v_x508 ( v_x164 | v_x507 ) + case 119: + return v_x508; +#define v_x509 ( v_x382 | ( v_x382 << 4 ) ) +#define v_x510 ( v_x80 << 35 ) +#define v_x511 ( v_x510 | ( v_x510 << 1 ) ) +#define v_x512 ( v_x511 | ( v_x511 << 2 ) ) +#define v_x513 ( v_x512 | ( v_x512 << 4 ) ) +#define v_x514 ( v_x509 | v_x513 ) +#define v_x515 ( v_x469 | ( v_x469 << 4 ) ) +#define v_x516 ( v_x87 << 49 ) +#define v_x517 ( v_x516 | ( v_x516 << 1 ) ) +#define v_x518 ( v_x517 | ( v_x517 << 2 ) ) +#define v_x519 ( v_x518 | ( v_x518 << 4 ) ) +#define v_x520 ( v_x515 | v_x519 ) +#define v_x521 ( v_x514 | v_x520 ) +#define v_x522 ( v_x171 | v_x521 ) + case 120: + return v_x522; +#define v_x523 ( v_x396 | ( v_x396 << 4 ) ) +#define v_x524 ( v_x210 << 30 ) +#define v_x525 ( v_x524 | ( v_x524 << 2 ) ) +#define v_x526 ( v_x525 | ( v_x525 << 4 ) ) +#define v_x527 ( v_x523 | v_x526 ) +#define v_x528 ( v_x479 | ( v_x479 << 4 ) ) +#define v_x529 ( v_x217 << 42 ) +#define v_x530 ( v_x529 | ( v_x529 << 2 ) ) +#define v_x531 ( v_x530 | ( v_x530 << 4 ) ) +#define v_x532 ( v_x528 | v_x531 ) +#define v_x533 ( v_x527 | v_x532 ) +#define v_x534 ( v_x177 | v_x533 ) + case 121: + return v_x534; +#define v_x535 ( v_x412 | ( v_x412 << 4 ) ) +#define v_x536 ( v_x230 << 30 ) +#define v_x537 ( v_x536 | ( v_x536 << 1 ) ) +#define v_x538 ( v_x233 << 31 ) +#define v_x539 ( v_x538 | ( v_x538 << 1 ) ) +#define v_x540 ( v_x537 | v_x539 ) +#define v_x541 ( v_x540 | ( v_x540 << 4 ) ) +#define v_x542 ( v_x535 | v_x541 ) +#define v_x543 ( v_x491 | ( v_x491 << 4 ) ) +#define v_x544 ( v_x245 << 42 ) +#define v_x545 ( v_x544 | ( v_x544 << 1 ) ) +#define v_x546 ( v_x248 << 43 ) +#define v_x547 ( v_x546 | ( v_x546 << 1 ) ) +#define v_x548 ( v_x545 | v_x547 ) +#define v_x549 ( v_x548 | ( v_x548 << 4 ) ) +#define v_x550 ( v_x543 | v_x549 ) +#define v_x551 ( v_x542 | v_x550 ) +#define v_x552 ( v_x186 | v_x551 ) + case 122: + return v_x552; +#define v_x553 ( x & 983040 ) +#define v_x554 ( v_x553 << 16 ) +#define v_x555 ( v_x554 | ( v_x554 << 4 ) ) +#define v_x556 ( x & 15728640 ) +#define v_x557 ( v_x556 << 20 ) +#define v_x558 ( v_x557 | ( v_x557 << 4 ) ) +#define v_x559 ( v_x555 | v_x558 ) +#define v_x560 ( x & 251658240 ) +#define v_x561 ( v_x560 << 24 ) +#define v_x562 ( v_x561 | ( v_x561 << 4 ) ) +#define v_x563 ( x & 4026531840 ) +#define v_x564 ( v_x563 << 28 ) +#define v_x565 ( v_x564 | ( v_x564 << 4 ) ) +#define v_x566 ( v_x562 | v_x565 ) +#define v_x567 ( v_x559 | v_x566 ) +#define v_x568 ( v_x194 | v_x567 ) + case 123: + return v_x568; +#define v_x569 ( v_x409 | ( v_x409 << 2 ) ) +#define v_x570 ( v_x226 << 27 ) +#define v_x571 ( v_x570 | ( v_x570 << 1 ) ) +#define v_x572 ( v_x571 | ( v_x571 << 2 ) ) +#define v_x573 ( v_x569 | v_x572 ) +#define v_x574 ( v_x537 | ( v_x537 << 2 ) ) +#define v_x575 ( v_x233 << 33 ) +#define v_x576 ( v_x575 | ( v_x575 << 1 ) ) +#define v_x577 ( v_x576 | ( v_x576 << 2 ) ) +#define v_x578 ( v_x574 | v_x577 ) +#define v_x579 ( v_x573 | v_x578 ) +#define v_x580 ( v_x488 | ( v_x488 << 2 ) ) +#define v_x581 ( v_x241 << 39 ) +#define v_x582 ( v_x581 | ( v_x581 << 1 ) ) +#define v_x583 ( v_x582 | ( v_x582 << 2 ) ) +#define v_x584 ( v_x580 | v_x583 ) +#define v_x585 ( v_x545 | ( v_x545 << 2 ) ) +#define v_x586 ( v_x248 << 45 ) +#define v_x587 ( v_x586 | ( v_x586 << 1 ) ) +#define v_x588 ( v_x587 | ( v_x587 << 2 ) ) +#define v_x589 ( v_x585 | v_x588 ) +#define v_x590 ( v_x584 | v_x589 ) +#define v_x591 ( v_x579 | v_x590 ) +#define v_x592 ( v_x206 | v_x591 ) + case 124: + return v_x592; +#define v_x593 ( x & 196608 ) +#define v_x594 ( v_x593 << 16 ) +#define v_x595 ( v_x594 | ( v_x594 << 2 ) ) +#define v_x596 ( x & 786432 ) +#define v_x597 ( v_x596 << 18 ) +#define v_x598 ( v_x597 | ( v_x597 << 2 ) ) +#define v_x599 ( v_x595 | v_x598 ) +#define v_x600 ( x & 3145728 ) +#define v_x601 ( v_x600 << 20 ) +#define v_x602 ( v_x601 | ( v_x601 << 2 ) ) +#define v_x603 ( x & 12582912 ) +#define v_x604 ( v_x603 << 22 ) +#define v_x605 ( v_x604 | ( v_x604 << 2 ) ) +#define v_x606 ( v_x602 | v_x605 ) +#define v_x607 ( v_x599 | v_x606 ) +#define v_x608 ( x & 50331648 ) +#define v_x609 ( v_x608 << 24 ) +#define v_x610 ( v_x609 | ( v_x609 << 2 ) ) +#define v_x611 ( x & 201326592 ) +#define v_x612 ( v_x611 << 26 ) +#define v_x613 ( v_x612 | ( v_x612 << 2 ) ) +#define v_x614 ( v_x610 | v_x613 ) +#define v_x615 ( x & 805306368 ) +#define v_x616 ( v_x615 << 28 ) +#define v_x617 ( v_x616 | ( v_x616 << 2 ) ) +#define v_x618 ( x & 3221225472 ) +#define v_x619 ( v_x618 << 30 ) +#define v_x620 ( v_x619 | ( v_x619 << 2 ) ) +#define v_x621 ( v_x617 | v_x620 ) +#define v_x622 ( v_x614 | v_x621 ) +#define v_x623 ( v_x607 | v_x622 ) +#define v_x624 ( v_x222 | v_x623 ) + case 125: + return v_x624; +#define v_x625 ( x & 65536 ) +#define v_x626 ( v_x625 << 16 ) +#define v_x627 ( v_x626 | ( v_x626 << 1 ) ) +#define v_x628 ( x & 131072 ) +#define v_x629 ( v_x628 << 17 ) +#define v_x630 ( v_x629 | ( v_x629 << 1 ) ) +#define v_x631 ( v_x627 | v_x630 ) +#define v_x632 ( x & 262144 ) +#define v_x633 ( v_x632 << 18 ) +#define v_x634 ( v_x633 | ( v_x633 << 1 ) ) +#define v_x635 ( x & 524288 ) +#define v_x636 ( v_x635 << 19 ) +#define v_x637 ( v_x636 | ( v_x636 << 1 ) ) +#define v_x638 ( v_x634 | v_x637 ) +#define v_x639 ( v_x631 | v_x638 ) +#define v_x640 ( x & 1048576 ) +#define v_x641 ( v_x640 << 20 ) +#define v_x642 ( v_x641 | ( v_x641 << 1 ) ) +#define v_x643 ( x & 2097152 ) +#define v_x644 ( v_x643 << 21 ) +#define v_x645 ( v_x644 | ( v_x644 << 1 ) ) +#define v_x646 ( v_x642 | v_x645 ) +#define v_x647 ( x & 4194304 ) +#define v_x648 ( v_x647 << 22 ) +#define v_x649 ( v_x648 | ( v_x648 << 1 ) ) +#define v_x650 ( x & 8388608 ) +#define v_x651 ( v_x650 << 23 ) +#define v_x652 ( v_x651 | ( v_x651 << 1 ) ) +#define v_x653 ( v_x649 | v_x652 ) +#define v_x654 ( v_x646 | v_x653 ) +#define v_x655 ( v_x639 | v_x654 ) +#define v_x656 ( x & 16777216 ) +#define v_x657 ( v_x656 << 24 ) +#define v_x658 ( v_x657 | ( v_x657 << 1 ) ) +#define v_x659 ( x & 33554432 ) +#define v_x660 ( v_x659 << 25 ) +#define v_x661 ( v_x660 | ( v_x660 << 1 ) ) +#define v_x662 ( v_x658 | v_x661 ) +#define v_x663 ( x & 67108864 ) +#define v_x664 ( v_x663 << 26 ) +#define v_x665 ( v_x664 | ( v_x664 << 1 ) ) +#define v_x666 ( x & 134217728 ) +#define v_x667 ( v_x666 << 27 ) +#define v_x668 ( v_x667 | ( v_x667 << 1 ) ) +#define v_x669 ( v_x665 | v_x668 ) +#define v_x670 ( v_x662 | v_x669 ) +#define v_x671 ( x & 268435456 ) +#define v_x672 ( v_x671 << 28 ) +#define v_x673 ( v_x672 | ( v_x672 << 1 ) ) +#define v_x674 ( x & 536870912 ) +#define v_x675 ( v_x674 << 29 ) +#define v_x676 ( v_x675 | ( v_x675 << 1 ) ) +#define v_x677 ( v_x673 | v_x676 ) +#define v_x678 ( x & 1073741824 ) +#define v_x679 ( v_x678 << 30 ) +#define v_x680 ( v_x679 | ( v_x679 << 1 ) ) +#define v_x681 ( x & 2147483648 ) +#define v_x682 ( v_x681 << 31 ) +#define v_x683 ( v_x682 | ( v_x682 << 1 ) ) +#define v_x684 ( v_x680 | v_x683 ) +#define v_x685 ( v_x677 | v_x684 ) +#define v_x686 ( v_x670 | v_x685 ) +#define v_x687 ( v_x655 | v_x686 ) +#define v_x688 ( v_x254 | v_x687 ) + case 126: + return v_x688; + case 127: + return x; + default: + assert( false ); + return 0; + } +} + +} // namespace detail +} // namespace kitty + +/*! \endcond */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/detail/utils.hpp b/third-party/mockturtle/lib/kitty/kitty/detail/utils.hpp new file mode 100644 index 00000000000..907ab583755 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/detail/utils.hpp @@ -0,0 +1,85 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file utils.hpp + \brief Helper functions + + \author Mathias Soeken +*/ + +/*! \cond PRIVATE */ +#pragma once + +#include +#include + +namespace kitty +{ + +namespace detail +{ + +/* string utils are from https://stackoverflow.com/a/217605 */ +inline void ltrim( std::string& s ) +{ + s.erase( s.begin(), std::find_if( s.begin(), s.end(), []( int ch ) + { return std::isspace( ch ) == 0; } ) ); +} + +inline void rtrim( std::string& s ) +{ + s.erase( std::find_if( s.rbegin(), s.rend(), []( int ch ) + { return std::isspace( ch ) == 0; } ) + .base(), + s.end() ); +} + +inline void trim( std::string& s ) +{ + ltrim( s ); + rtrim( s ); +} + +inline std::string ltrim_copy( std::string s ) +{ + ltrim( s ); + return s; +} + +inline std::string rtrim_copy( std::string s ) +{ + rtrim( s ); + return s; +} + +inline std::string trim_copy( std::string s ) +{ + trim( s ); + return s; +} +} /* namespace detail */ +} /* namespace kitty */ + /*! \endcond */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/dynamic_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/dynamic_truth_table.hpp new file mode 100644 index 00000000000..021bc8822f7 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/dynamic_truth_table.hpp @@ -0,0 +1,191 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dynamic_truth_table.hpp + \brief Implements dynamic_truth_table + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! Truth table in which number of variables is known at runtime. + */ +struct dynamic_truth_table +{ + /*! Standard constructor. + + The number of variables provided to the truth table can be + computed at runtime. However, once the truth table is constructed + its number of variables cannot change anymore. + + The constructor computes the number of blocks and resizes the + vector accordingly. + + \param num_vars Number of variables + */ + explicit dynamic_truth_table( uint32_t num_vars ) + : _bits( ( num_vars <= 6 ) ? 1u : ( 1u << ( num_vars - 6 ) ) ), + _num_vars( num_vars ) + { + } + + /*! Empty constructor. + + Creates an empty truth table. It has 0 variables, but no bits, i.e., it is + different from a truth table for the constant function. This constructor is + only used for convenience, if algorithms require the existence of default + constructable classes. + */ + dynamic_truth_table() : _num_vars( 0 ) {} + + /*! Constructs a new dynamic truth table instance with the same number of variables. */ + inline dynamic_truth_table construct() const + { + return dynamic_truth_table( _num_vars ); + } + + /*! Returns number of variables. + */ + inline auto num_vars() const noexcept { return _num_vars; } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return _bits.size(); } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return uint64_t( 1 ) << _num_vars; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend() const noexcept { return _bits.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. The truth + table type has to be complete. The vector of bits is resized accordingly. + + \param other Other truth table + */ + template::value && is_complete_truth_table::value>> + dynamic_truth_table& operator=( const TT& other ) + { + _bits.resize( other.num_blocks() ); + std::copy( other.begin(), other.end(), begin() ); + _num_vars = other.num_vars(); + + if ( _num_vars < 6 ) + { + mask_bits(); + } + + return *this; + } + + /*! Masks the number of valid truth table bits. + + If the truth table has less than 6 variables, it may not use all + the bits. This operation makes sure to zero out all non-valid + bits. + */ + inline void mask_bits() noexcept + { + if ( _num_vars < 6 ) + { + _bits[0u] &= detail::masks[_num_vars]; + } + } + + /*! \cond PRIVATE */ +public: /* fields */ + std::vector _bits; + uint32_t _num_vars; + /*! \endcond */ +}; + +template<> +struct is_truth_table : std::true_type +{ +}; + +template<> +struct is_complete_truth_table : std::true_type +{ +}; + +template<> +struct is_completely_specified_truth_table : std::true_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/hash.hpp b/third-party/mockturtle/lib/kitty/kitty/hash.hpp new file mode 100644 index 00000000000..684f3d2dd57 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/hash.hpp @@ -0,0 +1,95 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file hash.hpp + \brief Implements hash functions for truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "static_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Hash function for 64-bit word */ +inline std::size_t hash_block( uint64_t word ) +{ + /* from boost::hash_detail::hash_value_unsigned */ + return word ^ ( word + ( word << 6 ) + ( word >> 2 ) ); +} + +/*! \brief Combines two hash values */ +inline void hash_combine( std::size_t& seed, std::size_t other ) +{ + /* from boost::hash_detail::hash_combine_impl */ + const uint64_t m = UINT64_C( 0xc6a4a7935bd1e995 ); + const int r = 47; + + other *= m; + other ^= other >> r; + other *= m; + + seed ^= other; + seed *= m; + + seed += 0xe6546b64; +} + +/*! \brief Computes hash values for truth tables */ +template +struct hash +{ + std::size_t operator()( const TT& tt ) const + { + auto it = std::begin( tt._bits ); + auto seed = hash_block( *it++ ); + + while ( it != std::end( tt._bits ) ) + { + hash_combine( seed, hash_block( *it++ ) ); + } + + return seed; + } +}; + +/*! \cond PRIVATE */ +template +struct hash> +{ + inline std::size_t operator()( const static_truth_table& tt ) const + { + return hash_block( tt._bits ); + } +}; +/*! \endcond */ +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/implicant.hpp b/third-party/mockturtle/lib/kitty/kitty/implicant.hpp new file mode 100644 index 00000000000..4667ac24561 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/implicant.hpp @@ -0,0 +1,244 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file implicant.hpp + \brief Find implicants and prime implicants + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "algorithm.hpp" +#include "cube.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \brief Computes all minterms + + \param tt Truth table +*/ +template +std::vector get_minterms( const TT& tt ) +{ + std::vector m; + m.reserve( count_ones( tt ) ); + for_each_one_bit( tt, [&m]( auto index ) + { m.emplace_back( static_cast( index ) ); } ); + return m; +} + +/*! \cond PRIVATE */ +template +inline std::vector> get_jbuddies( Iterator begin, Iterator end, uint32_t j ) +{ + std::vector> buddies; + + auto mask = uint32_t( 1 ) << j; + auto k = begin; + auto kk = begin; + + while ( true ) + { + k = std::find_if( k, end, [mask]( auto m ) + { return ( m & mask ) == 0; } ); + if ( k == end ) + break; + + if ( kk <= k ) + { + kk = k + 1; + } + + kk = std::find_if( kk, end, [mask, &k]( auto m ) + { return m >= ( *k | mask ); } ); + if ( kk == end ) + break; + + if ( ( *k ^ *kk ) >= ( mask << 1 ) ) + { + k = kk; + continue; + } + + if ( *kk == ( *k | mask ) ) + { + buddies.emplace_back( k, kk ); + } + + ++k; + } + + return buddies; +} +/*! \endcond */ + +/*! \brief Computes all j-buddies in a list of minterms + + Computes all pairs \f$(k, k')\f$ such that \f$k < k'\f$ and the two minterms + at indexes \f$k\f$ and \f$k'\f$ only differ in bit \f$j\f$. + + This algorithm is described by Knuth in Exercise TAOCP 7.1.1-29. + + \param minterms Vector of minterms + \param j Bit position +*/ +inline std::vector::const_iterator, std::vector::const_iterator>> get_jbuddies( const std::vector& minterms, uint32_t j ) +{ + return get_jbuddies( minterms.begin(), minterms.end(), j ); +} + +/*! \brief Computes all prime implicants (from minterms) + + This algorithm computes all prime implicants for a list of minterms. The + running time is at most proportional to \f$mn\f$, where \f$m\f$ is the number + of minterms and \f$n\f$ is the number of variables. + + The algorithm is described in Exercise TAOCP 7.1.1-30 by Knuth and is inspired + by the algorithm described in [E. Morreale, IEEE Trans. EC 16(5), 1967, + 611–620]. + + \param minterms Vector of minterms (as integer values) + \param num_vars Number of variables +*/ +inline std::vector get_prime_implicants_morreale( const std::vector& minterms, unsigned num_vars ) +{ + std::vector cubes; + + const auto n = num_vars; + const auto m = minterms.size(); + + std::vector tags( 2 * m + n, 0 ); + std::vector stack( 2 * m + n, 0 ); + + uint32_t mask = ( 1 << n ) - 1; + uint32_t A{}; + + /* P1 */ + + /* Update tags using j-buddy algorithm */ + for ( auto j = 0u; j < n; ++j ) + { + for ( const auto& p : get_jbuddies( minterms, j ) ) + { + const auto k = std::distance( minterms.begin(), p.first ); + const auto kk = std::distance( minterms.begin(), p.second ); + + tags[k] |= ( 1 << j ); + tags[kk] |= ( 1 << j ); + } + } + + auto t = 0u; + for ( auto s = 0u; s < m; ++s ) + { + if ( tags[s] == 0u ) + { + cubes.emplace_back( minterms[s], mask ); + } + else + { + stack[t] = minterms[s]; + tags[t] = tags[s]; + t++; + } + } + + stack.push_back( 0 ); + + while ( true ) + { + /* P2 */ + auto j = 0u; + if ( stack[t] == t ) + { + while ( j < n && ( ( A >> j ) & 1 ) == 0 ) + { + ++j; + } + } + + while ( j < n && ( ( A >> j ) & 1 ) != 0 ) + { + t = stack[t] - 1; + A &= ~( 1 << j ); + ++j; + } + + if ( j >= n ) + { + /* terminate */ + return cubes; + } + + A |= ( 1 << j ); + + /* P3 */ + const auto r = t; + const auto s = stack.begin() + stack[t]; + + for ( const auto& p : get_jbuddies( s, stack.begin() + r, j ) ) + { + const auto k = std::distance( stack.begin(), p.first ); + const auto kk = std::distance( stack.begin(), p.second ); + const auto x = tags[k] & tags[kk] & ~( 1 << j ); + + if ( x == 0 ) + { + cubes.emplace_back( stack[k], ~A & mask ); + } + else + { + ++t; + stack[t] = stack[k]; + tags[t] = x; + } + } + + ++t; + stack[t] = r + 1; + } +} + +/*! \brief Computes all prime implicants (from truth table) + + Computes minterms from truth table and calls overloaded function. + + \param tt Truth table +*/ +template +std::vector get_prime_implicants_morreale( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + return get_prime_implicants_morreale( get_minterms( tt ), tt.num_vars() ); +} +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/isop.hpp b/third-party/mockturtle/lib/kitty/kitty/isop.hpp new file mode 100644 index 00000000000..fe09dc324d2 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/isop.hpp @@ -0,0 +1,130 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file isop.hpp + \brief Implements methods to compute irredundant sum-of-products (ISOP) representations + + \author Mathias Soeken +*/ + +#pragma once + +#include "cube.hpp" +#include "operations.hpp" +#include "operators.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \cond PRIVATE */ +namespace detail +{ +template +TT isop_rec( const TT& tt, const TT& dc, uint8_t var_index, std::vector& cubes ) +{ + assert( var_index <= tt.num_vars() ); + assert( is_const0( tt & ~dc ) ); + + if ( is_const0( tt ) ) + { + return tt; + } + + if ( is_const0( ~dc ) ) + { + cubes.emplace_back(); /* add empty cube */ + return dc; + } + + assert( var_index > 0 ); + + int var = var_index - 1; + for ( ; var >= 0; --var ) + { + if ( has_var( tt, var ) || has_var( dc, var ) ) + { + break; + } + } + + assert( var >= 0 ); + + /* co-factor */ + const auto tt0 = cofactor0( tt, var ); + const auto tt1 = cofactor1( tt, var ); + const auto dc0 = cofactor0( dc, var ); + const auto dc1 = cofactor1( dc, var ); + + const auto beg0 = cubes.size(); + const auto res0 = isop_rec( tt0 & ~dc1, dc0, var, cubes ); + const auto end0 = cubes.size(); + const auto res1 = isop_rec( tt1 & ~dc0, dc1, var, cubes ); + const auto end1 = cubes.size(); + auto res2 = isop_rec( ( tt0 & ~res0 ) | ( tt1 & ~res1 ), dc0 & dc1, var, cubes ); + + auto var0 = tt.construct(); + create_nth_var( var0, var, true ); + auto var1 = tt.construct(); + create_nth_var( var1, var ); + res2 |= ( res0 & var0 ) | ( res1 & var1 ); + + for ( auto c = beg0; c < end0; ++c ) + { + cubes[c].add_literal( var, false ); + } + for ( auto c = end0; c < end1; ++c ) + { + cubes[c].add_literal( var, true ); + } + + assert( is_const0( tt & ~res2 ) ); + assert( is_const0( res2 & ~dc ) ); + + return res2; +} +} /* namespace detail */ +/* \endcond */ + +/*! \brief Computes ISOP representation + + Computes the irredundant sum-of-products representation using the + Minato-Morreale algorithm [S. Minato, IEEE Trans. CAD 15(4), 1996, + 377-384]. + + \param tt Truth table +*/ +template +inline std::vector isop( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + std::vector cubes; + detail::isop_rec( tt, tt, tt.num_vars(), cubes ); + return cubes; +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/karnaugh_map.hpp b/third-party/mockturtle/lib/kitty/kitty/karnaugh_map.hpp new file mode 100644 index 00000000000..0c6936f448f --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/karnaugh_map.hpp @@ -0,0 +1,188 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file karnaugh_map.hpp + \brief karnaugh maps visualizer + + \author Gianluca Radi +*/ + +#pragma once + +#include "bit_operations.hpp" +#include "constructors.hpp" +#include "operators.hpp" +#include +#include +#include +#include +#include + +namespace kitty +{ +template +class karnaugh_map +{ +public: + /*! \brief Destroys K-map + */ + karnaugh_map() = delete; + + /*! \brief Construncts K-map from truth table + \param tt truth table + */ + karnaugh_map( TT tt ) : truth_table( tt ) + { + uint64_t num_var = log2( (double)tt.num_bits() ); + vars_col = num_var >> 1; + vars_row = num_var - vars_col; + col_seq = compute_seq_1ham_dist( vars_col ); + row_seq = compute_seq_1ham_dist( vars_row ); + } + + /*! \brief Prints K-map + \param os output stream (default = cout) + */ + + void print( std::ostream& os = std::cout ) + { + print_space( vars_col, os ); + os << " "; + for ( const auto& i : row_seq ) + { + os << binary( i, vars_row ); + print_space( vars_row, os ); + os << " "; + } + os << std::endl + << std::endl; + + for ( const auto& j : col_seq ) + { + os << binary( j, vars_col ); + os << " "; + for ( const auto& i : row_seq ) + { + uint8_t middle_space = 0; + if ( vars_row > 2 ) + middle_space = vars_row / 2; + print_space( middle_space, os ); + if ( is_dont_care( truth_table, ( j << vars_row ) + i ) ) + os << "-"; + else + { + if ( is_dont_know( truth_table, ( j << vars_row ) + i ) ) + os << "x"; + else + os << get_bit( truth_table, ( j << vars_row ) + i ); + } + print_space( ( vars_row << 1 ) - 1 - middle_space, os ); + os << " "; + } + os << std::endl + << std::endl; + } + } + + /*! \brief Get sequence of values for the least significant variables + */ + std::vector get_row_seq() const { return row_seq; } + + /*! \brief Get sequence of values for the most significant variables + */ + std::vector get_col_seq() const { return col_seq; } + +private: + TT truth_table; + unsigned vars_row; + unsigned vars_col; + std::vector row_seq; + std::vector col_seq; + + /*! \brief Converts unsigned into binary string + \param n unsigned to convert + \param max_var number of bits of the string + */ + std::string binary( uint8_t n, uint8_t max_var ) + { + std::string result; + uint8_t count = 0u; + + do + { + result.insert( result.begin(), '0' + ( n & 1 ) ); + count++; + } while ( n >>= 1 ); + + for ( uint8_t i = 0; i < max_var - count; i++ ) + { + result.insert( result.begin(), '0' ); + } + + return result; + } + + /*! \brief Prints white space. + \param val number of white space to print + \param os output stream + */ + void print_space( uint8_t val, std::ostream& os ) + { + for ( uint8_t i = 0; i < val; i++ ) + { + os << " "; + } + } + + /*! \brief Computes sequence of values with unitary Hamming distance for a + certain number of values + + For example, for 2 as the number of variables, the sequence would be = {0, + 1, 3, 2} (= {00, 01, 11, 10}) + + \param num_var number of variables + */ + std::vector compute_seq_1ham_dist( uint8_t num_var ) + { + if ( num_var == 1 ) + { + return { 0, 1 }; + } + else + { + std::vector res = compute_seq_1ham_dist( num_var - 1 ); + std::vector res_rev( res.rbegin(), res.rend() ); + for ( auto& i : res_rev ) + { + i += ( 1 << ( num_var - 1 ) ); + } + res.insert( res.end(), res_rev.begin(), res_rev.end() ); + return res; + } + } +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/npn.hpp b/third-party/mockturtle/lib/kitty/kitty/npn.hpp new file mode 100644 index 00000000000..3b18300b78a --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/npn.hpp @@ -0,0 +1,1327 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file npn.hpp + \brief Implements NPN canonization algorithms + + \author Alessandro Tempia Calvino + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "detail/constants.hpp" +#include "operators.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! \cond PRIVATE */ + +namespace detail +{ +template +void exact_npn_canonization_null_callback( const TT& tt ) +{ + (void)tt; +} +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Exact P canonization + + Given a truth table, this function finds the lexicographically smallest truth + table in its P class, called P representative. Two functions are in the + same P class, if one can obtain one from the other by input permutation. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole P class. + + The function returns a NPN configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the P representative + - input negations and output negation, which is 0 in this case + - input permutation to apply + + \param tt The truth table + \param fn Callback for each visited truth table in the class (default does nothing) + \return NPN configuration +*/ +template )> +std::tuple> exact_p_canonization( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, 0u, std::vector{} ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, 0u, std::vector{ 0 } ); + } + + assert( num_vars >= 2 && num_vars <= 7 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + + int best_swap = -1; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_swap = static_cast( i ); + tmin = t1; + } + } + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto i = 0; i <= best_swap; ++i ) + { + const auto pos = swaps[i]; + std::swap( perm[pos], perm[pos + 1] ); + } + + return std::make_tuple( tmin, 0u, perm ); +} + +/*! \brief Exact NPN canonization + + Given a truth table, this function finds the lexicographically smallest truth + table in its NPN class, called NPN representative. Two functions are in the + same NPN class, if one can obtain one from the other by input negation, input + permutation, and output negation. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole NPN class. + + The function returns a NPN configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the NPN representative + - input negations and output negation, output negation is stored as bit *n*, + where *n* is the number of variables in `tt` + - input permutation to apply + + \param tt The truth table (with at most 6 variables) + \param fn Callback for each visited truth table in the class (default does nothing) + \return NPN configuration +*/ +template )> +std::tuple> exact_npn_canonization( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + const auto bit = get_bit( tt, 0 ); + return std::make_tuple( unary_not_if( tt, bit ), static_cast( bit ), std::vector{} ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + const auto bit1 = get_bit( tt, 1 ); + return std::make_tuple( unary_not_if( tt, bit1 ), static_cast( bit1 << 1 ), std::vector{ 0 } ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt, t2 = ~tt; + auto tmin = std::min( t1, t2 ); + auto invo = tmin == t2; + + fn( t1 ); + fn( t2 ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + int best_swap = -1; + int best_flip = -1; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + swap_adjacent_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + best_swap = static_cast( i ); + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + swap_adjacent_inplace( t1, 0 ); + flip_inplace( t1, pos ); + swap_adjacent_inplace( t2, 0 ); + flip_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + best_swap = -1; + best_flip = static_cast( j ); + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + swap_adjacent_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + best_swap = static_cast( i ); + best_flip = static_cast( j ); + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + } + } + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto i = 0; i <= best_swap; ++i ) + { + const auto pos = swaps[i]; + std::swap( perm[pos], perm[pos + 1] ); + } + + uint32_t phase = uint32_t( invo ) << num_vars; + for ( auto i = 0; i <= best_flip; ++i ) + { + phase ^= 1 << flips[i]; + } + + return std::make_tuple( tmin, phase, perm ); +} + +/*! \brief Exact N canonization + + Given a truth table, this function finds the lexicographically smallest truth + table in its N class, called N representative. Two functions are in the + same N class, if one can obtain one from the other by input negations. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole N class. + + The function returns a N configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the N representative + - input negations that lead to the representative + + \param tt The truth table + \param fn Callback for each visited truth table in the class (default does nothing) + \return N configurations +*/ +template )> +std::tuple exact_n_canonization( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, 0 ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, 0 ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& flips = detail::flips[num_vars - 2u]; + int best_flip = -1; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_flip = static_cast( j ); + tmin = t1; + } + } + + uint32_t phase = 0; + for ( auto i = 0; i <= best_flip; ++i ) + { + phase ^= 1 << flips[i]; + } + + return std::make_tuple( tmin, phase ); +} + +/*! \brief Exact N canonization given a support size + + Given a truth table, this function finds the lexicographically smallest truth + table in its N class, called N representative. Two functions are in the + same N class, if one can obtain one from the other by input negations. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole N class. + + The function returns a N configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the N representative + - input negations that lead to the representative + + \param tt The truth table + \param support_size Support size used for the canonization + \param fn Callback for each visited truth table in the class (default does nothing) + \return N configurations +*/ +template )> +std::tuple exact_n_canonization_support( const TT& tt, uint32_t support_size, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + assert( support_size <= tt.num_vars() ); + + const auto num_vars = support_size; + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, 0 ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, 0 ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& flips = detail::flips[num_vars - 2u]; + int best_flip = -1; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_flip = static_cast( j ); + tmin = t1; + } + } + + uint32_t phase = 0; + for ( auto i = 0; i <= best_flip; ++i ) + { + phase ^= 1 << flips[i]; + } + + return std::make_tuple( tmin, phase ); +} + +/*! \brief Flip-swap NPN heuristic + + This algorithm will iteratively try to reduce the numeric value of the truth + table by first inverting each input, then inverting the output, and then + swapping each pair of inputs. Every improvement is accepted, the algorithm + stops, if no more improvement can be achieved. + + The function returns a NPN configuration which contains the + necessary transformations to obtain the representative. It is a + tuple of + + - the NPN representative + - input negations and output negation, output negation is stored as + bit *n*, where *n* is the number of variables in `tt` + - input permutation to apply + + \param tt Truth table + \return NPN configuration +*/ +template +std::tuple> flip_swap_npn_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* initialize permutation and phase */ + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase{ 0u }; + + auto npn = tt; + auto improvement = true; + + while ( improvement ) + { + improvement = false; + + /* input inversion */ + for ( auto i = 0u; i < num_vars; ++i ) + { + const auto flipped = flip( npn, i ); + if ( flipped < npn ) + { + npn = flipped; + phase ^= 1 << perm[i]; + improvement = true; + } + } + + /* output inversion */ + const auto flipped = ~npn; + if ( flipped < npn ) + { + npn = flipped; + phase ^= 1 << num_vars; + improvement = true; + } + + /* permute inputs */ + for ( auto d = 1u; d < num_vars - 1; ++d ) + { + for ( auto i = 0u; i < num_vars - d; ++i ) + { + auto j = i + d; + + const auto permuted = swap( npn, i, j ); + if ( permuted < npn ) + { + npn = permuted; + std::swap( perm[i], perm[j] ); + + improvement = true; + } + } + } + } + + return std::make_tuple( npn, phase, perm ); +} + +/*! \cond PRIVATE */ +namespace detail +{ + +template +void sifting_npn_canonization_loop( TT& npn, uint32_t& phase, std::vector& perm ) +{ + auto improvement = true; + auto forward = true; + + const auto n = npn.num_vars(); + + while ( improvement ) + { + improvement = false; + + for ( int i = forward ? 0 : n - 2; forward ? i < static_cast( n - 1 ) : i >= 0; forward ? ++i : --i ) + { + auto local_improvement = false; + for ( auto k = 1u; k < 8u; ++k ) + { + if ( k % 4u == 0u ) + { + const auto next_t = swap( npn, i, i + 1 ); + if ( next_t < npn ) + { + npn = next_t; + std::swap( perm[i], perm[i + 1] ); + local_improvement = true; + } + } + else if ( k % 2u == 0u ) + { + const auto next_t = flip( npn, i + 1 ); + if ( next_t < npn ) + { + npn = next_t; + phase ^= 1 << perm[i + 1]; + local_improvement = true; + } + } + else + { + const auto next_t = flip( npn, i ); + if ( next_t < npn ) + { + npn = next_t; + phase ^= 1 << perm[i]; + local_improvement = true; + } + } + } + + if ( local_improvement ) + { + improvement = true; + } + } + + forward = !forward; + } +} +template +void sifting_p_canonization_loop( TT& p, uint32_t& phase, std::vector& perm ) +{ + (void)phase; + auto improvement = true; + auto forward = true; + + const auto n = p.num_vars(); + + while ( improvement ) + { + improvement = false; + + for ( int i = forward ? 0 : n - 2; forward ? i < static_cast( n - 1 ) : i >= 0; forward ? ++i : --i ) + { + auto local_improvement = false; + + const auto next_t = swap( p, i, i + 1 ); + if ( next_t < p ) + { + p = next_t; + std::swap( perm[i], perm[i + 1] ); + local_improvement = true; + } + + if ( local_improvement ) + { + improvement = true; + } + } + forward = !forward; + } +} +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Sifting NPN heuristic + + The algorithm will always consider two adjacent variables and try all possible + transformations on these two. It will try once in forward direction and once + in backward direction. It will try for the regular function and inverted + function. + + The function returns a NPN configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the NPN representative + - input negations and output negation, output negation is stored as bit *n*, + where *n* is the number of variables in `tt` + - input permutation to apply + + \param tt Truth table + \return NPN configuration +*/ +template +std::tuple> sifting_npn_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* initialize permutation and phase */ + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + uint32_t phase{ 0u }; + + if ( num_vars < 2 ) + { + return std::make_tuple( tt, phase, perm ); + } + + auto npn = tt; + + detail::sifting_npn_canonization_loop( npn, phase, perm ); + + const auto best_perm = perm; + const auto best_phase = phase; + const auto best_npn = npn; + + npn = ~tt; + phase = 1 << num_vars; + std::iota( perm.begin(), perm.end(), 0u ); + + detail::sifting_npn_canonization_loop( npn, phase, perm ); + + if ( best_npn < npn ) + { + perm = best_perm; + phase = best_phase; + npn = best_npn; + } + + return std::make_tuple( npn, phase, perm ); +} + +/*! \brief Sifting P heuristic + + The algorithm will always consider two adjacent variables and try all possible + transformations on these two. It will try once in forward direction and once + in backward direction. It will try for the regular function. + + The function returns a P configuration which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the P representative + - input negations and output negation, which is 0 in this case + - input permutation to apply + + \param tt Truth table + \return NPN configuration +*/ +template +std::tuple> sifting_p_canonization( const TT& tt ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* initialize permutation and phase */ + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + uint32_t phase{ 0u }; + + if ( num_vars < 2u ) + { + return std::make_tuple( tt, phase, perm ); + } + + auto npn = tt; + + detail::sifting_p_canonization_loop( npn, phase, perm ); + + return std::make_tuple( npn, phase, perm ); +} + +/*! \brief Exact NPN enumeration + + Given a truth table, this function enumerates all the functions in its + NPN class. Two functions are in the same NP class, if one can be obtained + from the other by input negation, input permutation, and output negation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - NPN-enumerated truth table + - input and output negations + - input permutation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the NP class +*/ +template +void exact_npn_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, 0u, std::vector{} ); + fn( ~tt, 1u, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, 0u, std::vector{ 0 } ); + fn( ~tt, 2u, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase = 0; + + fn( t1, phase, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + fn( ~t1, phase | ( 1u << num_vars ), perm ); + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + swap_adjacent_inplace( t1, 0 ); + flip_inplace( t1, pos ); + + std::swap( perm[0], perm[1] ); + phase ^= 1 << perm[pos]; + + fn( t1, phase, perm ); + fn( ~t1, phase | ( 1u << num_vars ), perm ); + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + fn( ~t1, phase | ( 1u << num_vars ), perm ); + } + } +} + +/*! \brief Exact NP enumeration + + Given a truth table, this function enumerates all the functions in its + NP class. Two functions are in the same NP class, if one can be obtained + from the other by input negation and input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - NP-enumerated truth table + - input negations + - input permutation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the NP class +*/ +template +void exact_np_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, 0u, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, 0u, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase = 0; + + fn( t1, phase, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + swap_adjacent_inplace( t1, 0 ); + flip_inplace( t1, pos ); + + std::swap( perm[0], perm[1] ); + phase ^= 1 << perm[pos]; + + fn( t1, phase, perm ); + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + } +} + +/*! \brief Exact multi NP enumeration + + Given multiple truth tables, this function enumerates all the functions in their + NP class. Two functions are in the same NP class, if one can be obtained + from the other by input negation and input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - NP-enumerated truth tables + - input negations + - input permutation to apply + + \param tts Truth tables + \param fn Callback for each enumerated truth table in the NP class +*/ +template +void exact_multi_np_enumeration( const std::vector& tts, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + assert( tts.size() > 0 ); + + const auto num_vars = tts[0].num_vars(); + + for ( auto i = 0; i < tts.size(); ++i ) + assert( tts[i].num_vars() == num_vars ); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tts, 0u, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tts, 0u, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tts; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + uint32_t phase = 0; + + fn( t1, phase, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + + for ( auto& tt : t1 ) + swap_adjacent_inplace( tt, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + + for ( auto& tt : t1 ) + { + swap_adjacent_inplace( tt, 0 ); + flip_inplace( tt, pos ); + } + + std::swap( perm[0], perm[1] ); + phase ^= 1 << perm[pos]; + + fn( t1, phase, perm ); + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + + for ( auto& tt : t1 ) + swap_adjacent_inplace( tt, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, phase, perm ); + } + } +} + +/*! \brief Exact P enumeration + + Given a truth table, this function enumerates all the functions in its + P class. Two functions are in the same P class, if one can be obtained + from the other by input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - P-enumerated truth table + - input permutation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the P class +*/ +template +void exact_p_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + fn( t1, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, perm ); + } +} + +/*! \brief Exact multi P enumeration + + Given multiple truth tables, this function enumerates all the functions in their + P class. Two functions are in the same P class, if one can be obtained + from the other by input permutation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - P-enumerated truth tables + - input permutation to apply + + \param tt Truth tables + \param fn Callback for each enumerated truth table in the P class +*/ +template +void exact_multi_p_enumeration( const std::vector& tts, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + assert( tts.size() > 0 ); + + const auto num_vars = tts[0].num_vars(); + + for ( auto i = 0; i < tts.size(); ++i ) + assert( tts[i].num_vars == num_vars ); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tts, std::vector{} ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tts, std::vector{ 0 } ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tts; + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + fn( t1, perm ); + + const auto& swaps = detail::swaps[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + + for ( auto& tt : t1 ) + swap_adjacent_inplace( tt, pos ); + + std::swap( perm[pos], perm[pos + 1] ); + + fn( t1, perm ); + } +} + +/*! \brief Exact N enumeration + + Given a truth table, this function enumerates all the functions in its + N class. Two functions are in the same N class, if one can be obtained + from the other by input negation. + + The function takes a callback as second parameter which is called for + every enumerated function. The callback should take as parameters: + - N-enumerated truth table + - input negation to apply + + \param tt Truth table + \param fn Callback for each enumerated truth table in the N class +*/ +template +void exact_n_enumeration( const TT& tt, Callback&& fn ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + fn( tt, 0 ); + return; + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + fn( tt, 0 ); + return; + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + fn( t1, 0 ); + + const auto& flips = detail::flips[num_vars - 2u]; + uint32_t phase = 0; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + phase ^= 1 << pos; + + fn( t1, phase ); + } +} + +/*! \brief Exact N canonization complete + + Given a truth table, this function finds the lexicographically smallest truth + table in its N class, called N representative. Two functions are in the + same N class, if one can obtain one from the other by input negations. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole N class. + + The function returns all the N configurations which contains the necessary + transformations to obtain the representative. It is a tuple of + + - the N representative + - a vector of all input negations that lead to the representative + + \param tt The truth table + \param fn Callback for each visited truth table in the class (default does nothing) + \return N configurations +*/ +template )> +std::tuple> exact_n_canonization_complete( const TT& tt, Callback&& fn = detail::exact_npn_canonization_null_callback ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + return std::make_tuple( tt, std::vector{ 0 } ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + return std::make_tuple( tt, std::vector{ 0 } ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt; + auto tmin = t1; + + fn( t1 ); + + const auto& flips = detail::flips[num_vars - 2u]; + + std::vector best_flip{ -1 }; + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + flip_inplace( t1, pos ); + + fn( t1 ); + + if ( t1 < tmin ) + { + best_flip.erase( best_flip.begin() + 1, best_flip.end() ); + best_flip[0] = static_cast( j ); + tmin = t1; + } + else if ( t1 == tmin ) + { + best_flip.push_back( static_cast( j ) ); + } + } + + std::vector phases( best_flip.size() ); + uint32_t phase = 0; + int cnt = 0; + for ( auto i = 0u; i < best_flip.size(); ++i ) + { + auto flip = best_flip[i]; + for ( ; cnt <= flip; ++cnt ) + { + phase ^= 1 << flips[cnt]; + } + phases[i] = phase; + } + + return std::make_tuple( tmin, phases ); +} + +/*! \brief Obtain truth table from NPN configuration + + Given an NPN configuration, which contains a representative + function, input/output negations, and input permutations this + function computes the original truth table. + + \param config NPN configuration +*/ +template +TT create_from_npn_config( const std::tuple>& config ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto& from = std::get<0>( config ); + const auto& phase = std::get<1>( config ); + auto perm = std::get<2>( config ); + const auto num_vars = from.num_vars(); + + /* is output complemented? */ + auto res = ( ( phase >> num_vars ) & 1 ) ? ~from : from; + + /* input permutations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( perm[i] == i ) + { + continue; + } + + int k = i; + while ( perm[k] != i ) + { + ++k; + } + + swap_inplace( res, i, k ); + std::swap( perm[i], perm[k] ); + } + + /* input complementations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( ( phase >> i ) & 1 ) + { + flip_inplace( res, i ); + } + } + + return res; +} + +/*! \brief Obtain truth table applying a NPN configuration + + Given an NPN configuration composed of input/output negations, + and input permutations this function applies the transformation + to the input truth table. This function can be used to obtain + the NPN representative function given the NPN transformation. + This function is the inverse of `create_from_npn_config`. + + \param from truth table + \param phase input/output negations to apply + \param perm input permutations to apply +*/ +template +TT apply_npn_transformation( TT const& from, uint32_t phase, std::vector const& perm ) +{ + static_assert( is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + /* transpose the permutation vector */ + std::vector perm_transposed( perm.size() ); + for ( auto i = 0; i < perm.size(); ++i ) + perm_transposed[perm[i]] = i; + + const auto num_vars = from.num_vars(); + + /* is output complemented? */ + auto res = ( ( phase >> num_vars ) & 1 ) ? ~from : from; + + /* input complementations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( ( phase >> i ) & 1 ) + { + flip_inplace( res, i ); + } + } + + /* input permutations */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( perm_transposed[i] == i ) + { + continue; + } + + int k = i; + while ( perm_transposed[k] != i ) + { + ++k; + } + + swap_inplace( res, i, k ); + std::swap( perm_transposed[i], perm_transposed[k] ); + } + + return res; +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/operations.hpp b/third-party/mockturtle/lib/kitty/kitty/operations.hpp new file mode 100644 index 00000000000..db70571844e --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/operations.hpp @@ -0,0 +1,2489 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file operations.hpp + \brief Implements several operations on truth tables + + \author Mathias Soeken + \author Mahyar Emami (mahyar.emami@epfl.ch) +*/ + +#pragma once + +#include "algorithm.hpp" +#include "detail/shift.hpp" +#include "dynamic_truth_table.hpp" +#include "partial_truth_table.hpp" +#include "quaternary_truth_table.hpp" +#include "static_truth_table.hpp" +#include "ternary_truth_table.hpp" +#include "traits.hpp" + +#include +#include +#include +#include +#include + +namespace kitty +{ + +/* forward declarations */ +/*! \cond PRIVATE */ +template +TT create( unsigned num_vars ); + +template<> +dynamic_truth_table create( unsigned num_vars ); +/*! \endcond */ + +/*! \brief Inverts all bits in a truth table */ +template +inline TT unary_not( const TT& tt ) +{ + return unary_operation( tt, []( auto a ) { return ~a; } ); +} + +template +inline ternary_truth_table unary_not( const ternary_truth_table& tt ) +{ + return ternary_truth_table( ~( tt._bits ) & tt._care, tt._care ); +} + +template +inline quaternary_truth_table unary_not( const quaternary_truth_table& tt ) +{ + return quaternary_truth_table( tt._offset, tt._onset ); +} + +/*! Inverts all bits in a truth table, based on a condition */ +template +inline TT unary_not_if( const TT& tt, bool cond ) +{ +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + const auto mask = -static_cast( cond ); +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + return unary_operation( tt, [mask]( auto a ) { return a ^ mask; } ); +} + +/*! \brief Bitwise AND of two truth tables */ +template + +inline TT binary_and( const TT& first, const TT& second ) +{ + return binary_operation( first, second, std::bit_and<>() ); +} + +/*! \brief Bitwise AND of two ternary truth tables + * + * Computation rules: + * - `0 & 0 = 0 & 1 = 1 & 0 = 0` + * - `1 & 1 = 1` + * - `0 & - = - & 0 = 0` + * - `1 & x = x & 1 = x & - = - & x = x & x = x` + */ +template +inline ternary_truth_table binary_and( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + auto const op_bits = []( auto b1, auto c1, auto b2, auto c2 ) { + (void)c1; + (void)c2; + return b1 & b2; + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + return ( c1 & c2 ) | ( ~b1 & c1 ) | ( ~b2 & c2 ); + }; + + return ternary_truth_table( quaternary_operation( first._bits, first._care, second._bits, second._care, op_bits ), + quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ) ); +} + +/*! \brief Bitwise OR of two quaternary truth tables + * + * Computation rules: + * - `0 & 0 = 0 & 1 = 1 & 0 = 0` + * - `1 & 1 = 1` + * - `0 & - = - & 0 = x & 0 = 0 & x = 0` + * - `1 & - = - & 1 = - & - = -` + * - `1 & x = x & 1 = x & - = - & x = x & x = x` + */ +template +inline quaternary_truth_table binary_and( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + auto const op_on = []( auto a, auto b ) { + return a & b; + }; + auto const op_off = []( auto a, auto b, auto c, auto d ) { + return ( ~a & b ) | ( b & c ) | ( a & d ) | ( ~b & ~c & d ); + }; + + return quaternary_truth_table( binary_operation( first._onset, second._onset, op_on ), + quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_off ) ); +} + +/*! \brief Bitwise OR of two truth tables */ +template +inline TT binary_or( const TT& first, const TT& second ) +{ + return binary_operation( first, second, std::bit_or<>() ); +} + +/*! \brief Bitwise OR of two ternary truth tables + * + * Computation rules: + * - `0 | 0 = 0` + * - `0 | 1 = 1 | 0 = 1 | 1 = 1` + * - `1 | - = - | 1 = 1` + * - `0 | - = - | 0 = - | - = -` + */ +template +inline ternary_truth_table binary_or( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + auto const op_bits = []( auto b1, auto c1, auto b2, auto c2 ) { + (void)c1; + (void)c2; + return b1 | b2; + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + return b1 | b2 | ( c1 & c2 ); + }; + + return ternary_truth_table( quaternary_operation( first._bits, first._care, second._bits, second._care, op_bits ), + quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ) ); +} + +/*! \brief Bitwise OR of two quaternary truth tables + * + * Computation rules: + * - `0 | 0 = 0` + * - `0 | 1 = 1 | 0 = 1 | 1 = 1` + * - `1 | - = - | 1 = x | 1 = 1 | x = 1` + * - `0 | - = - | 0 = - | - = -` + * - `0 | x = x | 0 = x | - = - | x = x | x = x` + */ +template +inline quaternary_truth_table binary_or( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + auto const op_on = []( auto a, auto b, auto c, auto d ) { + return ( a & ~b ) | ( b & c ) | ( c & ~d ) | ( a & b & d ); + }; + auto const op_off = []( auto a, auto b ) { + return a & b; + }; + + return quaternary_truth_table( quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_on ), + binary_operation( first._offset, second._offset, op_off ) ); +} + +/*! \brief Bitwise XOR of two truth tables */ +template +inline TT binary_xor( const TT& first, const TT& second ) +{ + return binary_operation( first, second, std::bit_xor<>() ); +} + +/*! \brief Bitwise XOR of two ternary truth tables + * + * Computation rules: + * - `0 ^ 0 = 1 ^ 1 = 0` + * - `0 ^ 1 = 1 ^ 0 = 1` + * - `0 ^ - = - ^ 0 = 1 ^ - = - ^ 1 = - ^ - = -` + */ +template +inline ternary_truth_table binary_xor( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + auto const op_bits = []( auto b1, auto c1, auto b2, auto c2 ) { + return ( b1 ^ b2 ) & ( c1 & c2 ); + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + (void)b1; + (void)b2; + return c1 & c2; + }; + + return ternary_truth_table( quaternary_operation( first._bits, first._care, second._bits, second._care, op_bits ), + quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ) ); +} + +/*! \brief Bitwise XOR of two ternary truth tables + * + * Computation rules: + * - `0 ^ 0 = 1 ^ 1 = 0` + * - `0 ^ 1 = 1 ^ 0 = 1` + * - `0 ^ - = - ^ 0 = 1 ^ - = - ^ 1 = - ^ - = -` + * - `0 ^ x = x ^ 0 = 1 ^ x = x ^ 1 = - ^ x = x ^ - = x ^ x = x` + */ +template +inline quaternary_truth_table binary_xor( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + auto const op_on = []( auto a, auto b, auto c, auto d ) { + return ( b & c ) | ( a & d ); + }; + auto const op_off = []( auto a, auto b, auto c, auto d ) { + return ( a & c ) | ( b & d ); + }; + + return quaternary_truth_table( quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_on ), + quaternary_operation( first._onset, first._offset, second._onset, second._offset, op_off ) ); +} + +/*! \brief Ternary majority of three truth tables */ +template +inline TT ternary_majority( const TT& first, const TT& second, const TT& third ) +{ + return ternary_operation( first, second, third, []( auto a, auto b, auto c ) { return ( a & ( b ^ c ) ) ^ ( b & c ); } ); +} + +/*! \brief Ternary majority of three truth tables */ +template +inline ternary_truth_table ternary_majority( const ternary_truth_table& first, const ternary_truth_table& second, const ternary_truth_table& third ) +{ + auto const op_bits = []( auto a, auto b, auto c ) { + return ( a & ( b ^ c ) ) ^ ( b & c ); + }; + auto const op_care = []( auto b1, auto c1, auto b2, auto c2 ) { + return ( b1 & c1 & b2 & c2 ) | ( ( ~b1 ) & c1 & ( ~b2 ) & c2 ); + }; + TT care12 = quaternary_operation( first._bits, first._care, second._bits, second._care, op_care ); + TT care23 = quaternary_operation( second._bits, second._care, third._bits, third._care, op_care ); + TT care13 = quaternary_operation( first._bits, first._care, third._bits, third._care, op_care ); + auto const ternary_or = []( auto a, auto b, auto c ) { + return a | b | c; + }; + return ternary_truth_table( ternary_operation( first._bits, second._bits, third._bits, op_bits ), + ternary_operation( care12, care23, care13, ternary_or ) ); +} + +/*! \brief Ternary majority of three quaternary truth tables + * + * Computation rules: + * When there are two 0, 1, or x the result is 0, 1, x + * - ternary_majority(0, 1, -) = ternary_majority(0, -, -) = ternary_majority(1, -, -) = - + * - ternary_majority(0, 1, x) = ternary_majority(0, x, -) = ternary_majority(1, x, -) = ternary_majority(x, -, -) = x + * - ternary_majority is commutative + */ +template +inline quaternary_truth_table ternary_majority( quaternary_truth_table& first, const quaternary_truth_table& second, const quaternary_truth_table& third ) +{ + return ( ( first & second ) | ( second & third ) ) | ( first & third ); +} + +/*! \brief Performs ternary if-then-else of three truth tables + + \param first Truth table for condition + \param second Truth table for then-case + \param third Truth table for else-case + */ +template +inline TT ternary_ite( const TT& first, const TT& second, const TT& third ) +{ + return ternary_operation( first, second, third, []( auto a, auto b, auto c ) { return ( a & b ) ^ ( ~a & c ); } ); +} + +template +inline ternary_truth_table ternary_ite( const ternary_truth_table& first, const ternary_truth_table& second, const ternary_truth_table& third ) +{ + auto const op_bits = []( auto a, auto b, auto c ) { + return ( a & b ) ^ ( ~a & c ); + }; + auto const op_care1 = []( auto b2, auto c2, auto b3, auto c3 ) { + return ( ( ~b2 ) & c2 & b3 ) | ( b2 & c2 & ( ~b3 ) ) | ( c2 & ( ~c3 ) ) | ( c3 & ( ~c2 ) ) | ( ( ~c3 ) & ( ~c2 ) ); + }; + auto const op_care2 = []( auto b1, auto c1, auto c2, auto c3 ) { + return c1 & ( ( ( ~c2 ) & b1 ) | ( ( ~b1 ) & ( ~c3 ) ) | ( ( ~c3 ) & ( ~c2 ) ) ); + }; + TT care1 = quaternary_operation( second._bits, second._care, third._bits, third._care, op_care1 ); + TT care2 = quaternary_operation( first._bits, first._care, second._care, third._care, op_care2 ); + auto const final_op = []( auto c1, auto res1, auto res2 ) { + return ~( ( ( ~c1 ) & res1 ) | res2 ); + }; + return ternary_truth_table( ternary_operation( first._bits, second._bits, third._bits, op_bits ), + ternary_operation( first._care, care1, care2, final_op ) ); +} + +/*! \brief Performs ternary if-then-else of three quaternary truth tables + + In general, each time the result would depend from a x, x is the result. + For example, ternary_ite( -, 0, x ) = x + However, ternary_ite( x, 0, 0 ) = ternary_ite( -, 0, 0 ) = 0 + ternary_ite( x, 1, 1 ) = ternary_ite( -, 1, 1 ) = 1 + ternary_ite( x, -, - ) = ternary_ite( -, -, - ) = - + + \param first Truth table for condition + \param second Truth table for then-case + \param third Truth table for else-case + */ +template +inline quaternary_truth_table ternary_ite( const quaternary_truth_table& first, const quaternary_truth_table& second, const quaternary_truth_table& third ) +{ + auto const op_1 = []( auto a, auto b, auto c, auto d ) { + return ( a & b ) & ( ( c & d ) | ( ~c & ~d ) ); + }; + auto const op_2 = []( auto a, auto b, auto c ) { + return ( a & b & c ); + }; + auto const op_3 = []( auto a, auto b, auto c ) { + return ( a & b & ~c ); + }; + auto const or_4 = []( auto a, auto b, auto c, auto d ) { + return ( a | b | c | d ); + }; + auto const or_3 = []( auto a, auto b, auto c ) { + return ( a | b | c ); + }; + TT on1 = ternary_operation( first._offset, third._onset, first._onset, op_3 ); + TT on2 = ternary_operation( first._onset, second._onset, first._offset, op_3 ); + TT on3 = ternary_operation( first._offset, second._onset, third._onset, op_2 ); + TT on4 = ternary_operation( first._offset, second._offset, third._onset, op_2 ); + TT on5 = ternary_operation( first._onset, second._onset, third._offset, op_2 ); + TT on6 = quaternary_operation( second._onset, third._onset, second._offset, third._offset, op_1 ); + TT on7 = quaternary_operation( on1, on2, on3, on4, or_4 ); + TT res_onset = ternary_operation( on7, on5, on6, or_3 ); + + TT off1 = ternary_operation( third._offset, first._offset, first._onset, op_3 ); + TT off2 = ternary_operation( first._onset, second._offset, first._offset, op_3 ); + TT off3 = ternary_operation( first._offset, second._offset, third._offset, op_2 ); + TT off4 = ternary_operation( first._offset, second._onset, third._offset, op_2 ); + TT off5 = ternary_operation( first._onset, second._offset, third._onset, op_2 ); + TT off6 = quaternary_operation( second._offset, third._offset, second._onset, third._onset, op_1 ); + TT off7 = quaternary_operation( off1, off2, off3, off4, or_4 ); + TT res_offset = ternary_operation( off7, off5, off6, or_3 ); + + return quaternary_truth_table( res_onset, res_offset ); +} + +/*! \brief Muxes two truth tables based on a variable + + \param var_index Variable index + \param then_ Truth table for the then-case + \param else_ Truth table for the else-case +*/ +template +inline TT mux_var( uint8_t var_index, const TT& then_, const TT& else_ ) +{ + if ( var_index < 6u ) + { + return binary_operation( then_, else_, + [&]( auto a, auto b ) { return ( a & detail::projections[var_index] ) | + ( b & detail::projections_neg[var_index] ); } ); + } + else + { + const auto step = 1u << ( var_index - 6u ); + auto j = 0u; + auto res = then_.construct(); + + std::transform( then_.begin(), then_.end(), else_.begin(), res.begin(), + [&]( auto a, auto b ) { + return ( j++ % ( 2 * step ) ) < step ? b : a; + } ); + + return res; + } +} + +/*! \brief Muxes two ternary truth tables based on a variable + + * Values that are not taken by the projections become 0s. + + \param var_index Variable index + \param then_ Truth table for the then-case + \param else_ Truth table for the else-case +*/ +template +inline ternary_truth_table mux_var( uint8_t var_index, const ternary_truth_table& then_, const ternary_truth_table& else_ ) +{ + auto const projection = [&var_index]( auto a ) { + return ( a & detail::projections[var_index] ); + }; + auto const projection_care = [&var_index]( auto a ) { + return ( ( a & detail::projections[var_index] ) | detail::projections_neg[var_index] ); + }; + auto const projection_neg = [&var_index]( auto a ) { + return ( a & detail::projections_neg[var_index] ); + }; + auto const projection_neg_care = [&var_index]( auto a ) { + return ( ( a & detail::projections_neg[var_index] ) | detail::projections[var_index] ); + }; + ternary_truth_table then_new( unary_operation( then_._bits, projection ), unary_operation( then_._care, projection_care ) ); + ternary_truth_table else_new( unary_operation( else_._bits, projection_neg ), unary_operation( else_._care, projection_neg_care ) ); + return then_new | else_new; +} + +/*! \brief Muxes two ternary truth tables based on a variable + + * Values that are not taken by the projections become 0s. + + \param var_index Variable index + \param then_ Truth table for the then-case + \param else_ Truth table for the else-case +*/ +template +inline quaternary_truth_table mux_var( uint8_t var_index, const quaternary_truth_table& then_, const quaternary_truth_table& else_ ) +{ + auto const projection = [&var_index]( auto a ) { + return ( a & detail::projections[var_index] ); + }; + auto const projection_off = [&var_index]( auto a ) { + return ( ( a & detail::projections[var_index] ) | detail::projections_neg[var_index] ); + }; + auto const projection_neg = [&var_index]( auto a ) { + return ( a & detail::projections_neg[var_index] ); + }; + auto const projection_neg_off = [&var_index]( auto a ) { + return ( ( a & detail::projections_neg[var_index] ) | detail::projections[var_index] ); + }; + quaternary_truth_table then_new( unary_operation( then_._onset, projection ), unary_operation( then_._offset, projection_off ) ); + quaternary_truth_table else_new( unary_operation( else_._onset, projection_neg ), unary_operation( else_._offset, projection_neg_off ) ); + return then_new | else_new; +} + +/*! \brief Checks whether two truth tables are equal + + \param first First truth table + \param second Second truth table +*/ +template +inline bool equal( const TT& first, const TT& second ) +{ + if ( first.num_vars() != second.num_vars() ) + { + return false; + } + + return binary_predicate( first, second, std::equal_to<>() ); +} + +/*! \cond PRIVATE */ +inline bool equal( const partial_truth_table& first, const partial_truth_table& second ) +{ + if ( first.num_bits() != second.num_bits() ) + { + return false; + } + + return binary_predicate( first, second, std::equal_to<>() ); +} /*! \endcond */ + +/*! \brief Checks whether two incompletely specified truth tables are equal + + The template parameter UseDCs allows us to decide if to check for possible assignment + of the don't cares to achieve equality: + - UseDCs = false : Checks if both the careset and the onset coincide + - UseDCs = true : Checks if there is an assignment of the don't cares making the functions equal. + + \param first First truth table + \param second Second truth table +*/ +template +inline bool equal( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + if constexpr ( UseDCs ) + { + const auto care_mask = first._care & second._care; + return equal( first._bits & care_mask, second._bits & care_mask ); + } + else + { + return equal( first._bits, second._bits ) && equal( first._care, second._care ); + } +} + +template +inline bool equal( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return equal( first._onset, second._onset ) && equal( first._offset, second._offset ); +} + +/*! \brief Checks if first truth table implies a second truth table + + \param first First truth table + \param second Second truth table +*/ +template +inline bool implies( const TT& first, const TT& second ) +{ + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( a & ~b ) == 0u; } ); +} + +/*! \brief Checks if first ternary truth table implies a second ternary truth table + + \param first First truth table + \param second Second truth table +*/ +template +inline bool implies( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return is_const0( first & ~second ); +} + +/*! \brief Checks if first quaternary truth table implies a second quaternary truth table + + \param first First truth table + \param second Second truth table +*/ +template +inline bool implies( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return is_const0( first & ~second ); +} + +/*! \brief Checks whether a truth table is lexicographically smaller than another + + Comparison is initiated from most-significant bit. + \param first First truth table + \param second Second truth table +*/ +template +inline bool less_than( const TT& first, const TT& second ) +{ + return std::lexicographical_compare( first._bits.rbegin(), first._bits.rend(), + second._bits.rbegin(), second._bits.rend() ); +} + +/*! \cond PRIVATE */ +template +inline bool less_than( const static_truth_table& first, const static_truth_table& second ) +{ + return first._bits < second._bits; +} +/*! \endcond */ + +/*! \brief Checks whether a ternary truth table is lexicographically smaller than another + + Comparison is initiated from most-significant bit and don't cares are considered as 0. + \param first First truth table + \param second Second truth table +*/ +template +inline bool less_than( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return first._bits < second._bits; +} + +/*! \brief Checks whether a quaternary truth table is lexicographically smaller than another + + Comparison is initiated from most-significant bit + Don't cares are considered as 0 and don't knows are considered as 1. + \param first First truth table + \param second Second truth table +*/ +template +inline bool less_than( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return first._onset < second._onset; +} + +/*! \brief Checks whether truth table is contant 0 + + \param tt Truth table +*/ +template +inline bool is_const0( const TT& tt ) +{ + return std::all_of( std::begin( tt._bits ), std::end( tt._bits ), []( uint64_t word ) { return word == 0; } ); +} + +/*! \cond PRIVATE */ +template +inline bool is_const0( const static_truth_table& tt ) +{ + return tt._bits == 0; +} + +/*! \brief Checks whether a ternary truth table is contant 0 + + \param tt Truth table +*/ +template +inline bool is_const0( const ternary_truth_table& tt ) +{ + if constexpr ( UseDCs ) + { + return is_const0( tt._bits & tt._care ); + } + else + { + return is_const0( tt._bits | ~tt._care ); + } +} + +/*! \brief Checks whether a quaternary truth table is constant composed by only - and 0. + + \param tt Truth table +*/ +template +inline bool is_const0( const quaternary_truth_table& tt ) +{ + return is_const0( ~tt._offset ); +} + +/*! \endcond */ + +/*! \brief Checks whether the intersection of two truth tables is empty + + \param first First truth table + \param second Second truth table + \param polarity1 Polarity of the first truth table + \param polarity2 Polarity of the second truth table +*/ +template::value>> +bool intersection_is_empty( const TT& first, const TT& second ) +{ + if constexpr ( polarity1 && polarity2 ) + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( a & b ) == 0u; } ); + else if constexpr ( !polarity1 && polarity2 ) + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( ~a & b ) == 0u; } ); + else if constexpr ( polarity1 && !polarity2 ) + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( a & ~b ) == 0u; } ); + else // !polarity1 && !polarity2 + return binary_predicate( first, second, []( uint64_t a, uint64_t b ) { return ( ~a & ~b ) == 0u; } ); +} + +/*! \brief Checks whether the intersection of three truth tables is empty + + \param first First truth table + \param second Second truth table + \param third Third truth table + \param polarity1 Polarity of the first truth table + \param polarity2 Polarity of the second truth table + \param polarity3 Polarity of the first truth table +*/ +template::value>> +bool intersection_is_empty( const TT& first, const TT& second, const TT& third ) +{ + if constexpr ( polarity1 && polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & b & c ) == 0u; } ); + else if constexpr ( !polarity1 && polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & b & c ) == 0u; } ); + else if constexpr ( polarity1 && !polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & ~b & c ) == 0u; } ); + else if constexpr ( polarity1 && polarity2 && !polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & b & ~c ) == 0u; } ); + else if constexpr ( !polarity1 && !polarity2 && polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & ~b & c ) == 0u; } ); + else if constexpr ( polarity1 && !polarity2 && !polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( a & ~b & ~c ) == 0u; } ); + else if constexpr ( !polarity1 && polarity2 && !polarity3 ) + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & b & ~c ) == 0u; } ); + else // !polarity1 && !polarity2 && !polarity3 + return ternary_predicate( first, second, third, []( uint64_t a, uint64_t b, uint64_t c ) { return ( ~a & ~b & ~c ) == 0u; } ); +} + +/*! \brief Checks whether truth table depends on given variable index + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var( const TT& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + return std::any_of( std::begin( tt._bits ), std::end( tt._bits ), + [var_index]( uint64_t word ) { return ( ( word >> ( uint64_t( 1 ) << var_index ) ) & detail::projections_neg[var_index] ) != + ( word & detail::projections_neg[var_index] ); } ); + } + + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + if ( tt._bits[i + j] != tt._bits[i + j + step] ) + { + return true; + } + } + } + return false; +} + +/*! \cond PRIVATE */ +template +bool has_var( const static_truth_table& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + return ( ( tt._bits >> ( 1 << var_index ) ) & detail::projections_neg[var_index] ) != + ( tt._bits & detail::projections_neg[var_index] ); +} +/*! \endcond */ + +/*! \brief Checks whether a quaternary truth table depends on given variable index. + This function returns false if the truth table potentially does not depend + on the variable (due to don't cares) and returns true if the truth table potentially + depends on the variable (due to don't knows). + + For example, has_var( -01-, 0 ) = has_var( -01-, 1 ) = false. + In the first case the truth table is considered to be 0011 and + in the second case the truth table is considered to be 1010. + + Yet, has_var( x01-, 0 ) = has_var( x01-, 1 ) = true. + In the first case we cannot assume x = 0 and + in the second case we cannot assume x = 1. + + Finally, has_var( x0-0, 1 ) = true, since the don't care should be + equal to an unknown value. + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var( const quaternary_truth_table& tt, uint8_t var_index ) +{ + auto const compare_func = []( auto a, auto b, auto c, auto d ) { + return ( ~a & ~b & ~c & ~d ) | ( b & d ) | ( a & c ); + }; + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + auto const projection = [&var_index]( auto a ) { + return ( a & detail::projections[var_index] ); + }; + auto const projection_neg = [&var_index]( auto a ) { + return ( a & detail::projections_neg[var_index] ); + }; + auto proj_pos_on = unary_operation( tt._onset, projection ); + auto proj_pos_off = unary_operation( tt._offset, projection ); + auto proj_neg_on = unary_operation( tt._onset, projection_neg ) << ( 1 << var_index ); + auto proj_neg_off = unary_operation( tt._offset, projection_neg ) << ( 1 << var_index ); + return !is_const0( ~quaternary_operation( proj_pos_on, proj_pos_off, proj_neg_on, proj_neg_off, compare_func ) ); + } + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + auto tta = create( tt._onset.num_vars() ); + auto ttb = create( tt._onset.num_vars() ); + auto ttc = create( tt._onset.num_vars() ); + auto ttd = create( tt._onset.num_vars() ); + tta._bits[0] = get_block( tt._onset, i + j ); + ttb._bits[0] = get_block( tt._offset, i + j ); + ttc._bits[0] = get_block( tt._onset, i + j + step ); + ttd._bits[0] = get_block( tt._offset, i + j + step ); + if ( !is_const0( ~quaternary_operation( tta, ttb, ttc, ttd, compare_func ) ) ) + { + return true; + } + } + } + return false; +} + +/*! \brief Checks whether a ternary truth table depends on given variable index.\ + + When the template parameter UseDCs is false, don't cares are treated like zeros. + When the template parameter UseDCs is true, this function returns: + - true if the onset shows that the function depends on the variable. + - false if a don't cares assignments makes the function independent of the variable. + + For example, let the hexadecimal representation of the onset be 0xF0000000, and + the hexadecimal representation of the careset be 0xF0000000. This function is + independent of the variable 2, with projection function 0xF0F0F0F0 for the following + onset, careset pair ( 0xFF000000, 0xFF000000 ). + + Reassigning the careset and the onset is essential when checking if an incompletely + specified function depends on multiple variables, since different variables might + require different don't cares assignments to achieve indendence on different variables. + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + if constexpr ( UseDCs ) + { + ternary_truth_table tt0 = tt; + ternary_truth_table tt1 = tt; + cofactor0_inplace( tt0, var_index ); + cofactor1_inplace( tt1, var_index ); + const TT diff = tt0._bits ^ tt1._bits; + const TT mask = tt0._care & tt1._care; + if ( kitty::count_ones( diff & mask ) > 0 ) + return true; + /* Adjust the careset and the onset to avoid contradictions. */ + tt._care |= ( ~mask ) & diff; + tt._bits = tt0._bits | tt1._bits; + return false; + } + else + { + return has_var( tt._bits, var_index ); + } +} + +/*! \brief Checks whether a ternary truth table depends on given variable index.\ + + When the template parameter UseDCs is false, don't cares are treated like zeros. + When the template parameter UseDCs is true, this function returns: + - true if the onset shows that the function depends on the variable. + - false if a don't cares assignments makes the function independent of the variable. + + For example, let the hexadecimal representation of the onset be 0xF0000000, and + the hexadecimal representation of the careset be 0xF0000000. This function is + independent of the variable 2, with projection function 0xF0F0F0F0 for the following + onset, careset pair ( 0xFF000000, 0xFF000000 ). + + Warning. This function DOES NOT perform the reassignment. Use has_var_inplace if that + is the desired behavior. + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +bool has_var( ternary_truth_table const& tt, uint8_t var_index ) +{ + ternary_truth_table ttc = tt; + return has_var_inplace( ttc, var_index ); +} + +/*! \brief Computes the next lexicographically larger truth table + + This methods updates `tt` to become the next lexicographically + larger truth table. If `tt` is already the largest truth table, the + updated truth table will contain all zeros. + + \param tt Truth table +*/ +template +void next_inplace( TT& tt ) +{ + if ( tt.num_vars() <= 6u ) + { + tt._bits[0]++; + tt.mask_bits(); + } + else + { + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); ++i ) + { + /* If incrementing the word does not lead to an overflow, we're done*/ + if ( ++tt._bits[i] != 0 ) + { + break; + } + } + } +} + +/*! \cond PRIVATE */ +template +inline void next_inplace( static_truth_table& tt ) +{ + tt._bits++; + tt.mask_bits(); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +inline void next_inplace( partial_truth_table& tt ) +{ + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); ++i ) + { + /* If incrementing the word does not lead to an overflow, we're done*/ + if ( ++tt._bits[i] != 0u ) + { + break; + } + } + tt.mask_bits(); +} +/*! \endcond */ + +/*! \brief Computes the next lexicographically larger truth table + + This methods updates `tt` to become the next lexicographically + larger truth table. If `tt` is already the largest truth table, the + updated truth table will contain all zeros. + + Don't cares are treated like zeros, so the truth tables 1110 and 111- will become 1111. + + This method never increase the number of don't cares of the input truth table. + + For example, truth table -101 will become -110, not -11-. + + \param tt Truth table +*/ +template +void next_inplace( ternary_truth_table& tt ) +{ + auto copy = tt; + next_inplace( tt._bits ); + tt._care = tt._care | ( tt._bits ^ copy._bits ); +} + +/*! \brief Computes the next lexicographically larger truth table + + This methods updates `tt` to become the next lexicographically + larger truth table. If `tt` is already the largest truth table, the + updated truth table will contain all zeros. + + Lexicographical increasing order for quaternary truth table: + + 0, 1, -, x + + \param tt Truth table +*/ + +template +void next_inplace( quaternary_truth_table& tt ) +{ + auto copy = tt; + int64_t first_bit_on = find_first_one_bit( tt._onset ); + int64_t first_bit_of = find_first_one_bit( tt._offset ); + if ( first_bit_on == -1 ) + first_bit_on = tt._onset.num_bits(); + if ( first_bit_of == -1 ) + first_bit_of = tt._offset.num_bits(); + if ( first_bit_of < first_bit_on ) + { + clear_bit( tt._offset, first_bit_of ); + set_bit( tt._onset, first_bit_of ); + for ( int64_t i = 0; i < first_bit_of; i++ ) + { + set_bit( tt._offset, i ); + clear_bit( tt._onset, i ); + } + } + else + { + if ( first_bit_of > first_bit_on ) + { + set_bit( tt._offset, first_bit_on ); + for ( int64_t i = 0; i < first_bit_on; i++ ) + { + set_bit( tt._offset, i ); + clear_bit( tt._onset, i ); + } + } + else + { + if ( uint64_t( first_bit_of ) == tt._offset.num_bits() && uint64_t( first_bit_on ) == tt._onset.num_bits() ) + set_bit( tt._offset, first_bit_of - 1 ); + else + { + clear_bit( tt._onset, first_bit_on ); + clear_bit( tt._offset, first_bit_on ); + } + for ( int64_t i = 0; i < first_bit_of; i++ ) + { + set_bit( tt._offset, i ); + clear_bit( tt._onset, i ); + } + } + } +} + +/*! \brief Returns the next lexicographically larger truth table + + Out-of-place variant for `next_inplace`. + + \param tt Truth table +*/ +template +inline TT next( const TT& tt ) +{ + auto copy = tt; + next_inplace( copy ); + return copy; +} + +/*! \brief Computes co-factor with respect to 0 + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +void cofactor0_inplace( TT& tt, uint8_t var_index ) +{ + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + std::transform( std::begin( tt._bits ), std::end( tt._bits ), + std::begin( tt._bits ), + [var_index]( uint64_t word ) { return ( ( word & detail::projections_neg[var_index] ) << ( uint64_t( 1 ) << var_index ) ) | + ( word & detail::projections_neg[var_index] ); } ); + } + else + { + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + tt._bits[i + j + step] = tt._bits[i + j]; + } + } + } +} + +/*! \cond PRIVATE */ +template +void cofactor0_inplace( static_truth_table& tt, uint8_t var_index ) +{ + tt._bits = ( ( tt._bits & detail::projections_neg[var_index] ) << ( 1 << var_index ) ) | + ( tt._bits & detail::projections_neg[var_index] ); +} +/*! \endcond */ + +/*! \brief Computes co-factor with respect to 0 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor0_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + cofactor0_inplace( tt._bits, var_index ); + cofactor0_inplace( tt._care, var_index ); +} + +/*! \brief Computes co-factor with respect to 0 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor0_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + cofactor0_inplace( tt._onset, var_index ); + cofactor0_inplace( tt._offset, var_index ); +} + +/*! \brief Returns co-factor with respect to 0 + + \param tt Truth table + \param var_index Variable index +*/ +template +TT cofactor0( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + cofactor0_inplace( copy, var_index ); + return copy; +} + +/*! \brief Computes co-factor with respect to 1 + + \param tt Truth table + \param var_index Variable index +*/ +template::value>> +void cofactor1_inplace( TT& tt, uint8_t var_index ) +{ + if ( tt.num_vars() <= 6 || var_index < 6 ) + { + std::transform( std::begin( tt._bits ), std::end( tt._bits ), + std::begin( tt._bits ), + [var_index]( uint64_t word ) { return ( word & detail::projections[var_index] ) | + ( ( word & detail::projections[var_index] ) >> ( uint64_t( 1 ) << var_index ) ); } ); + } + else + { + const auto step = 1 << ( var_index - 6 ); + for ( auto i = 0u; i < static_cast( tt.num_blocks() ); i += 2 * step ) + { + for ( auto j = 0; j < step; ++j ) + { + tt._bits[i + j] = tt._bits[i + j + step]; + } + } + } +} + +/*! \cond PRIVATE */ +template +void cofactor1_inplace( static_truth_table& tt, uint8_t var_index ) +{ + tt._bits = ( tt._bits & detail::projections[var_index] ) | ( ( tt._bits & detail::projections[var_index] ) >> ( 1 << var_index ) ); +} +/*! \endcond */ + +/*! \brief Computes co-factor with respect to 1 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor1_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + cofactor1_inplace( tt._bits, var_index ); + cofactor1_inplace( tt._care, var_index ); +} + +/*! \brief Computes co-factor with respect to 1 + + \param tt Ternary truth table + \param var_index Variable index +*/ +template::value>> +void cofactor1_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + cofactor1_inplace( tt._onset, var_index ); + cofactor1_inplace( tt._offset, var_index ); +} + +/*! \brief Returns co-factor with respect to 1 + + \param tt Truth table + \param var_index Variable index +*/ +template +TT cofactor1( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + cofactor1_inplace( copy, var_index ); + return copy; +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap_adjacent` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void swap_adjacent_inplace( TT& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() - 1 ); + + /* permute within each word */ + if ( var_index < 5 ) + { + const auto shift = uint64_t( 1 ) << var_index; + std::transform( std::begin( tt._bits ), std::end( tt._bits ), std::begin( tt._bits ), + [shift, var_index]( uint64_t word ) { + return ( word & detail::permutation_masks[var_index][0] ) | + ( ( word & detail::permutation_masks[var_index][1] ) << shift ) | + ( ( word & detail::permutation_masks[var_index][2] ) >> shift ); + } ); + } + /* permute (half) parts of words */ + else if ( var_index == 5 ) + { + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + const auto tmp = *it; + auto it2 = it + 1; + *it = ( tmp & 0xffffffff ) | ( *it2 << 0x20 ); + *it2 = ( *it2 & UINT64_C( 0xffffffff00000000 ) ) | ( tmp >> 0x20 ); + it += 2; + } + } + /* permute comlete words */ + else + { + const auto step = 1 << ( var_index - 6 ); + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) + { + std::swap( *( it + i + step ), *( it + i + 2 * step ) ); + } + it += 4 * step; + } + } +} + +/*! \cond PRIVATE */ +template +void swap_adjacent_inplace( static_truth_table& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + const auto shift = uint64_t( 1 ) << var_index; + + tt._bits = ( tt._bits & detail::permutation_masks[var_index][0] ) | + ( ( tt._bits & detail::permutation_masks[var_index][1] ) << shift ) | + ( ( tt._bits & detail::permutation_masks[var_index][2] ) >> shift ); +} +/*! \endcond */ + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap_adjacent` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void swap_adjacent_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + swap_adjacent_inplace( tt._bits, var_index ); + swap_adjacent_inplace( tt._care, var_index ); +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap_adjacent` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void swap_adjacent_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + swap_adjacent_inplace( tt._onset, var_index ); + swap_adjacent_inplace( tt._offset, var_index ); +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index` with `var_index + 1`. The + function will return a new truth table with the result. + + \param tt Truth table + \param var_index A variable +*/ +template +inline TT swap_adjacent( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + swap_adjacent_inplace( copy, var_index ); + return copy; +} + +/*! \brief Swaps two variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap` instead. + + \param tt Truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template::value>> +void swap_inplace( TT& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + if ( var_index1 == var_index2 ) + { + return; + } + + if ( var_index1 > var_index2 ) + { + std::swap( var_index1, var_index2 ); + } + + if ( tt.num_vars() <= 6 ) + { + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + tt._bits[0] = ( tt._bits[0] & pmask[0] ) | ( ( tt._bits[0] & pmask[1] ) << shift ) | ( ( tt._bits[0] & pmask[2] ) >> shift ); + } + else if ( var_index2 <= 5 ) + { + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + std::transform( std::begin( tt._bits ), std::end( tt._bits ), std::begin( tt._bits ), + [shift, &pmask]( uint64_t word ) { + return ( word & pmask[0] ) | ( ( word & pmask[1] ) << shift ) | ( ( word & pmask[2] ) >> shift ); + } ); + } + else if ( var_index1 <= 5 ) /* in this case, var_index2 > 5 */ + { + const auto step = 1 << ( var_index2 - 6 ); + const auto shift = 1 << var_index1; + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) + { + const auto low_to_high = ( *( it + i ) & detail::projections[var_index1] ) >> shift; + const auto high_to_low = ( *( it + i + step ) << shift ) & detail::projections[var_index1]; + *( it + i ) = ( *( it + i ) & ~detail::projections[var_index1] ) | high_to_low; + *( it + i + step ) = ( *( it + i + step ) & detail::projections[var_index1] ) | low_to_high; + } + it += 2 * step; + } + } + else + { + const auto step1 = 1 << ( var_index1 - 6 ); + const auto step2 = 1 << ( var_index2 - 6 ); + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = 0; i < step2; i += 2 * step1 ) + { + for ( auto j = 0; j < step1; ++j ) + { + std::swap( *( it + i + j + step1 ), *( it + i + j + step2 ) ); + } + } + it += 2 * step2; + } + } +} + +/*! \cond PRIVATE */ +template +inline void swap_inplace( static_truth_table& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + if ( var_index1 == var_index2 ) + { + return; + } + + if ( var_index1 > var_index2 ) + { + std::swap( var_index1, var_index2 ); + } + + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + tt._bits = ( tt._bits & pmask[0] ) | ( ( tt._bits & pmask[1] ) << shift ) | ( ( tt._bits & pmask[2] ) >> shift ); +} +/* \endcond */ + +/*! \brief Swaps two variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap` instead. + + \param tt Ternary truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template::value>> +void swap_inplace( ternary_truth_table& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + swap_inplace( tt._bits, var_index1, var_index2 ); + swap_inplace( tt._care, var_index1, var_index2 ); +} + +/*! \brief Swaps two variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will change `tt` in-place. If `tt` should not be changed, + one can use `swap` instead. + + \param tt Ternary truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template::value>> +void swap_inplace( quaternary_truth_table& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + swap_inplace( tt._onset, var_index1, var_index2 ); + swap_inplace( tt._offset, var_index1, var_index2 ); +} + +/*! \brief Swaps two adjacent variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will return a new truth table with the result. + + \param tt Truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template +inline TT swap( const TT& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + auto copy = tt; + swap_inplace( copy, var_index1, var_index2 ); + return copy; +} + +/*! \brief Flips a variable in a truth table + + The function flips variable `var_index` in `tt`. The function will + change `tt` in-place. If `tt` should not be changed, one can use + `flip` instead. + + \param tt Truth table + \param var_index A variable +*/ +template::value>> +void flip_inplace( TT& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + if ( tt.num_blocks() == 1 ) + { + const auto shift = 1 << var_index; + tt._bits[0] = ( ( tt._bits[0] << shift ) & detail::projections[var_index] ) | ( ( tt._bits[0] & detail::projections[var_index] ) >> shift ); + } + else if ( var_index < 6 ) + { + const auto shift = 1 << var_index; + std::transform( std::begin( tt._bits ), std::end( tt._bits ), std::begin( tt._bits ), + [var_index, shift]( uint64_t word ) { + return ( ( word << shift ) & detail::projections[var_index] ) | ( ( word & detail::projections[var_index] ) >> shift ); + } ); + } + else + { + const auto step = 1 << ( var_index - 6 ); + auto it = std::begin( tt._bits ); + while ( it != std::end( tt._bits ) ) + { + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) + { + std::swap( *( it + i ), *( it + i + step ) ); + } + it += 2 * step; + } + } +} + +/*! \cond PRIVATE */ +template +inline void flip_inplace( static_truth_table& tt, uint8_t var_index ) +{ + assert( var_index < tt.num_vars() ); + + const auto shift = 1 << var_index; + tt._bits = ( ( tt._bits << shift ) & detail::projections[var_index] ) | ( ( tt._bits & detail::projections[var_index] ) >> shift ); +} +/* \endcond */ + +/*! \brief Flips a variable in a ternary truth table + + The function flips variable `var_index` in `tt`. The function will + change `tt` in-place. If `tt` should not be changed, one can use + `flip` instead. + + \param tt Ternary truth table + \param var_index A variable +*/ +template::value>> +void flip_inplace( ternary_truth_table& tt, uint8_t var_index ) +{ + flip_inplace( tt._bits, var_index ); + flip_inplace( tt._care, var_index ); +} + +/*! \brief Flips a variable in a ternary truth table + + The function flips variable `var_index` in `tt`. The function will + change `tt` in-place. If `tt` should not be changed, one can use + `flip` instead. + + \param tt Ternary truth table + \param var_index A variable +*/ +template::value>> +void flip_inplace( quaternary_truth_table& tt, uint8_t var_index ) +{ + flip_inplace( tt._onset, var_index ); + flip_inplace( tt._offset, var_index ); +} + +/*! \brief Flips a variable in a truth table + + The function flips variable `var_index` in `tt`. The function will + not change `tt` and return the result as a copy. + + \param tt Truth table + \param var_index A variable +*/ +template +inline TT flip( const TT& tt, uint8_t var_index ) +{ + auto copy = tt; + flip_inplace( copy, var_index ); + return copy; +} + +/*! \brief Reorders truth table to have minimum base + + This function will reorder variables, such that there are no + "holes". For example, the function \f$ x_0 \land x_2 \f$ will be + changed to \f$ x_0 \land x_1 \f$ by swapping \f$ x_1 \f$ with \f$ + x_2 \f$. That is all variables that are not in the functional + support will be moved to the back. Note that the size of the truth + table is not changed, because for `static_truth_table` one cannot + compute it at compile-time. + + The function changes the truth table and returns a vector with all + variable indexes that were in the functional support of the original + function. + + \param tt Truth table + */ +template::value>> +std::vector min_base_inplace( TT& tt ) +{ + std::vector support; + + auto k = 0u; + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( !has_var( tt, i ) ) + { + continue; + } + if ( k < i ) + { + swap_inplace( tt, k, i ); + } + support.push_back( i ); + ++k; + } + + return support; +} + +/*! \brief Reorders truth table to have minimum base + + This function will reorder variables, such that there are no + "holes". For example, the function \f$ x_0 \land x_2 \f$ will be + changed to \f$ x_0 \land x_1 \f$ by swapping \f$ x_1 \f$ with \f$ + x_2 \f$. That is all variables that are not in the functional + support will be moved to the back. Note that the size of the truth + table is not changed, because for `static_truth_table` one cannot + compute it at compile-time. + + The function changes the truth table and returns a vector with all + variable indexes that were in the functional support of the original + function. + + \param tt Truth table + */ +template::value>> +std::vector min_base_inplace( ternary_truth_table& tt ) +{ + std::vector support; + + auto k = 0u; + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( !has_var( tt, i ) ) + { + continue; + } + if ( k < i ) + { + swap_inplace( tt, k, i ); + } + support.push_back( i ); + ++k; + } + + return support; +} + +/*! \brief Expands truth table from minimum base to original based on support + + This is the inverse operation to `min_base_inplace`, where the + support is used to swap variables back to their original positions. + + \param tt Truth table + \param support Original indexes of support variables +*/ +template +void expand_inplace( TT& tt, const std::vector& support ) +{ + for ( int i = static_cast( support.size() ) - 1; i >= 0; --i ) + { + assert( i <= support[i] ); + swap_inplace( tt, i, support[i] ); + } +} + +/*! \brief Extends smaller truth table to larger one + + The most significant variables will not be in the functional support of the + resulting truth table, but the method is helpful to align a truth table when + being used with another one. + + \param tt Larger truth table to create + \param from Smaller truth table to copy from +*/ +template::value>> +void extend_to_inplace( TT& tt, const TTFrom& from ) +{ + assert( tt.num_vars() >= from.num_vars() ); + + if ( from.num_vars() < 6 ) + { + auto mask = *from.begin(); + + for ( auto i = from.num_vars(); i < std::min( 6, tt.num_vars() ); ++i ) + { + mask |= ( mask << ( 1 << i ) ); + } + + std::fill( tt.begin(), tt.end(), mask ); + } + else + { + auto it = tt.begin(); + while ( it != tt.end() ) + { + it = std::copy( from.cbegin(), from.cend(), it ); + } + } +} + +/*! \brief Extends smaller ternary truth table to larger one + + The most significant variables will not be in the functional support of the + resulting truth table, but the method is helpful to align a truth table when + being used with another one. + + \param tt Larger ternary truth table to create + \param from Smaller ternary truth table to copy from +*/ +template::value>> +void extend_to_inplace( ternary_truth_table& tt, const ternary_truth_table& from ) +{ + extend_to_inplace( tt._bits, from._bits ); + extend_to_inplace( tt._care, from._care ); +} + +/*! \brief Extends smaller ternary truth table to larger one + + The most significant variables will not be in the functional support of the + resulting truth table, but the method is helpful to align a truth table when + being used with another one. + + \param tt Larger ternary truth table to create + \param from Smaller ternary truth table to copy from +*/ +template::value>> +void extend_to_inplace( quaternary_truth_table& tt, const quaternary_truth_table& from ) +{ + extend_to_inplace( tt._onset, from._onset ); + extend_to_inplace( tt._offset, from._offset ); +} + +/*! \brief Extends smaller truth table to larger static one + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating static truth tables. The + template parameter `NumVars` must be equal or larger to the number of + variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline static_truth_table extend_to( const TTFrom& from ) +{ + static_truth_table tt; + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Extends smaller truth table to larger dynamic one + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating dynamic truth tables. + The parameter `num_vars` must be equal or larger to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline dynamic_truth_table extend_to( const TTFrom& from, unsigned num_vars ) +{ + auto tt = create( num_vars ); + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Extends smaller ternary truth table to larger one of the same underlying type + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating dynamic truth tables. + The parameter `num_vars` must be equal or larger to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline ternary_truth_table extend_to( const ternary_truth_table& from, unsigned num_vars ) +{ + auto tt = ternary_truth_table( num_vars ); + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Extends smaller ternary truth table to larger one of the same underlying type + + This is an out-of-place version of `extend_to_inplace` that has the truth + table as a return value. It only works for creating dynamic truth tables. + The parameter `num_vars` must be equal or larger to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline quaternary_truth_table extend_to( const quaternary_truth_table& from, unsigned num_vars ) +{ + auto tt = quaternary_truth_table( num_vars ); + extend_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger truth table to smaller one + + The function expects that the most significant bits, which are cut off, are + not in the functional support of the original function. Only then it is + ensured that the resulting function is equivalent. + + \param tt Smaller truth table to create + \param from Larger truth table to copy from +*/ +template::value>> +void shrink_to_inplace( TT& tt, const TTFrom& from ) +{ + assert( tt.num_vars() <= from.num_vars() ); + + std::copy( from.begin(), from.begin() + tt.num_blocks(), tt.begin() ); + + if ( tt.num_vars() < 6 ) + { + tt.mask_bits(); + } +} + +/*! \brief Shrinks larger ternary truth table to smaller one + + The function expects that the most significant bits, which are cut off, are + not in the functional support of the original function. Only then it is + ensured that the resulting function is equivalent. + + \param tt Smaller ternary truth table to create + \param from Larger ternary truth table to copy from +*/ +template::value>> +void shrink_to_inplace( ternary_truth_table& tt, const ternary_truth_table& from ) +{ + shrink_to_inplace( tt._bits, from._bits ); + shrink_to_inplace( tt._care, from._care ); +} + +/*! \brief Shrinks larger ternary truth table to smaller one + + The function expects that the most significant bits, which are cut off, are + not in the functional support of the original function. Only then it is + ensured that the resulting function is equivalent. + + \param tt Smaller ternary truth table to create + \param from Larger ternary truth table to copy from +*/ +template::value>> +void shrink_to_inplace( quaternary_truth_table& tt, const quaternary_truth_table& from ) +{ + shrink_to_inplace( tt._onset, from._onset ); + shrink_to_inplace( tt._offset, from._offset ); +} + +/*! \brief Shrinks larger truth table to smaller static one + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating static truth tables. The template + parameter `NumVars` must be equal or smaller to the number of variables in + `from`. + + \param from Smaller truth table to copy from +*/ +template +inline static_truth_table shrink_to( const TTFrom& from ) +{ + static_truth_table tt; + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger truth table to smaller dynamic one + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating dynamic tables. The parameter + `num_vars` must be equal or smaller to the number of variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline dynamic_truth_table shrink_to( const TTFrom& from, unsigned num_vars ) +{ + auto tt = create( num_vars ); + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger ternary truth table to smaller one of the same underlying type + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating dynamic tables. The parameter + `num_vars` must be equal or smaller to the number of variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline ternary_truth_table shrink_to( const ternary_truth_table& from, unsigned num_vars ) +{ + auto tt = ternary_truth_table( num_vars ); + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Shrinks larger ternary truth table to smaller one of the same underlying type + + This is an out-of-place version of `shrink_to` that has the truth table as a + return value. It only works for creating dynamic tables. The parameter + `num_vars` must be equal or smaller to the number of variables in `from`. + + \param from Smaller truth table to copy from +*/ +template +inline quaternary_truth_table shrink_to( const quaternary_truth_table& from, unsigned num_vars ) +{ + auto tt = quaternary_truth_table( num_vars ); + shrink_to_inplace( tt, from ); + return tt; +} + +/*! \brief Left-shift truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +void shift_left_inplace( TT& tt, uint64_t shift ) +{ + /* small truth table */ + if ( tt.num_vars() <= 6 ) + { + tt._bits[0] <<= shift; + tt.mask_bits(); + return; + } + + /* large shift */ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0 ) + { + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0 ) + { + const auto rshift = 64u - rem; + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = ( tt._bits[i] << rem ) | ( tt._bits[i - 1] >> rshift ); + } + tt._bits[div] = tt._bits[0] << rem; + } + else + { + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = tt._bits[i]; + } + tt._bits[div] = tt._bits[0]; + } + + std::fill_n( std::begin( tt._bits ), div, 0u ); + } +} + +/*! \cond PRIVATE */ +template +inline void shift_left_inplace( static_truth_table& tt, uint64_t shift ) +{ + tt._bits <<= shift; + tt.mask_bits(); +} +/*! \endcond */ + +/*! \cond PRIVATE */ +inline void shift_left_inplace( partial_truth_table& tt, uint64_t shift ) +{ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0u ) + { + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0u ) + { + const auto rshift = 64u - rem; + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = ( tt._bits[i] << rem ) | ( tt._bits[i - 1] >> rshift ); + } + tt._bits[div] = tt._bits[0] << rem; + } + else + { + for ( auto i = last - div; i > 0; --i ) + { + tt._bits[i + div] = tt._bits[i]; + } + tt._bits[div] = tt._bits[0]; + } + + std::fill_n( std::begin( tt._bits ), div, 0u ); + tt.mask_bits(); + } +} +/*! \endcond */ + +/*! \brief Left-shift ternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_left_inplace( ternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt._bits, shift ); + shift_left_inplace( tt._care, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._care, i ); + } +} + +/*! \brief Left-shift quaternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_left_inplace( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt._onset, shift ); + shift_left_inplace( tt._offset, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._offset, i ); + } +} + +/*! \brief Left-shift truth table + + Out-of-place variant of `shift_left_inplace`. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline TT shift_left( const TT& tt, uint64_t shift ) +{ + auto copy = tt; + shift_left_inplace( copy, shift ); + return copy; +} + +/*! \brief Right-shift truth table + + Drops overflowing least-significant bits and fills up most-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +void shift_right_inplace( TT& tt, uint64_t shift ) +{ + /* small truth table */ + if ( tt.num_vars() <= 6 ) + { + tt._bits[0] >>= shift; + tt.mask_bits(); + return; + } + + /* large shift */ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0 ) + { + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0 ) + { + const auto rshift = 64u - rem; + for ( auto i = div; i < last; ++i ) + { + tt._bits[i - div] = ( tt._bits[i] >> rem ) | ( tt._bits[i + 1] << rshift ); + } + tt._bits[last - div] = tt._bits[last] >> rem; + } + else + { + for ( auto i = div; i <= last; ++i ) + { + tt._bits[i - div] = tt._bits[i]; + } + } + + std::fill_n( std::begin( tt._bits ) + ( tt.num_blocks() - div ), div, 0u ); + } +} + +/*! \cond PRIVATE */ +template +inline void shift_right_inplace( static_truth_table& tt, uint64_t shift ) +{ + tt._bits >>= shift; +} +/*! \endcond */ + +/*! \cond PRIVATE */ +inline void shift_right_inplace( partial_truth_table& tt, uint64_t shift ) +{ + if ( shift >= tt.num_bits() ) + { + clear( tt ); + return; + } + + if ( shift > 0u ) + { + tt.mask_bits(); + + const auto last = tt.num_blocks() - 1u; + const auto div = shift / 64u; + const auto rem = shift % 64u; + + if ( rem != 0u ) + { + const auto rshift = 64u - rem; + for ( auto i = div; i < last; ++i ) + { + tt._bits[i - div] = ( tt._bits[i] >> rem ) | ( tt._bits[i + 1] << rshift ); + } + tt._bits[last - div] = tt._bits[last] >> rem; + } + else + { + for ( auto i = div; i <= last; ++i ) + { + tt._bits[i - div] = tt._bits[i]; + } + } + + std::fill_n( std::begin( tt._bits ) + ( tt.num_blocks() - div ), div, 0u ); + } +} +/*! \endcond */ + +/*! \brief Right-shift ternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_right_inplace( ternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt._bits, shift ); + shift_right_inplace( tt._care, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._care, tt._care.num_bits() - 1 - i ); + } +} + +/*! \brief Right-shift quaternary truth table + + Drops overflowing most-significant bits and fills up least-significant bits + with zeroes. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline void shift_right_inplace( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt._onset, shift ); + shift_right_inplace( tt._offset, shift ); + for ( auto i = 0u; i < shift; i++ ) + { + set_bit( tt._offset, tt._offset.num_bits() - 1 - i ); + } +} + +/*! \brief Right-shift truth table + + Out-of-place variant of `shift_right_inplace`. + + \param tt Truth table + \param shift Number of bits to shift +*/ +template +inline TT shift_right( const TT& tt, uint64_t shift ) +{ + auto copy = tt; + shift_right_inplace( copy, shift ); + return copy; +} + +/*! \brief Composes a truth table. + + Given a function `f`, and a set of truth tables as arguments, computes the + composed truth table. For example, if `f(x1, x2) = 1001` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 1100. This function can + be regarded as a general operator with arity `vars.size()` where the behavior + of the operator is given by f. + + \param f The outer function + \param vars The ordered set of input variables + \return The composed truth table with vars.size() variables +*/ +template::value>> +inline auto compose_truth_table( const TTf& f, const std::vector& vars ) +{ + assert( vars.size() == static_cast( f.num_vars() ) ); + auto composed = vars[0].construct(); + + for ( uint64_t i = 0u; i < composed.num_bits(); ++i ) + { + uint64_t index = 0u; + for ( uint64_t j = 0u; j < vars.size(); ++j ) + { + index += get_bit( vars[j], i ) << j; + } + if ( get_bit( f, index ) ) + { + set_bit( composed, i ); + } + else + { + clear_bit( composed, i ); + } + } + + return composed; +} + +/*! \brief Composes a truth table. + + Given a function `f`, and a set of truth tables as arguments, computes the + composed truth table. For example, if `f(x1, x2) = 1001` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 1000. + In case of don't cares this method treats them as "don't knows". + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 100-. + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= -010}`, the function returns -00-. + However, if `f(x1, x2) = 1100` and + `vars = {x1 = 1001, x2= -010}`, the function returns 1001. + + This function does not support truth quaternary table with more than 6 variables. + + \param f The outer function + \param vars The ordered set of input variables + \return The composed truth table with vars.size() variables +*/ +template::value>> +inline auto compose_truth_table( const ternary_truth_table& f, const std::vector>& vars ) +{ + assert( vars.size() == static_cast( f.num_vars() ) ); + auto a = create( vars[0].num_vars() ); + auto b = create( vars[0].num_vars() ); + auto composed = ternary_truth_table( a, b ); + + for ( uint64_t i = 0u; i < composed.num_bits(); ++i ) + { + auto f_copy = f; + uint64_t cnt_care = 0u; + for ( uint64_t j = 0u; j < vars.size(); ++j ) + { + auto tt_mask = f._bits.construct(); + auto const projection = [&j, &f]( auto a ) { + return ( a | detail::projections[f.num_vars() - 1 - j] ); + }; + auto const projection_neg = [&j, &f]( auto a ) { + return ( a | detail::projections_neg[f.num_vars() - j - 1] ); + }; + if ( !is_dont_care( vars[j], i ) ) + { + auto bit = get_bit( vars[j], i ); + if ( bit ) + { + auto ttt_mask = ternary_truth_table( unary_operation( tt_mask, projection ) ); + f_copy &= ttt_mask; + } + else + { + auto ttt_mask = ternary_truth_table( unary_operation( tt_mask, projection_neg ) ); + f_copy &= ttt_mask; + } + } + else + { + ++cnt_care; + } + } + if ( is_const0( f_copy ) ) + { + set_bit( composed, i, false ); + } + else + { + if ( count_ones( f_copy._bits ) == ( 1 << cnt_care ) ) // count_ones( f_copy._onset & ~f_copy._offset ) count real ones in f_copy + { + set_bit( composed, i ); + } + else + { + set_dont_care( composed, i ); + } + } + } + + return composed; +} + +/*! \brief Composes a truth table. + + Given a function `f`, and a set of truth tables as arguments, computes the + composed truth table. For example, if `f(x1, x2) = 1001` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 1000. + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= 1010}`, the function returns 100-. + For example, if `f(x1, x2) = 1-00` and + `vars = {x1 = 1001, x2= -010}`, the function returns 100-. + If `f(x1, x2) = 1100` and + `vars = {x1 = 1x01, x2= -01x}`, the function returns 1x01. + + This function does not support truth quaternary table with more than 6 variables. + + \param f The outer function + \param vars The ordered set of input variables + \return The composed truth table with vars.size() variables +*/ +template::value>> +inline auto compose_truth_table( const quaternary_truth_table& f, const std::vector>& vars ) +{ + assert( vars.size() == static_cast( f.num_vars() ) ); + auto a = create( vars[0].num_vars() ); + auto b = create( vars[0].num_vars() ); + auto composed = quaternary_truth_table( a, b ); + + for ( uint64_t i = 0u; i < composed.num_bits(); ++i ) + { + auto f_copy = f; + uint64_t cnt_care = 0u; + bool dont_know_found = false; + for ( uint64_t j = 0u; j < vars.size(); ++j ) + { + auto tt_mask = f._onset.construct(); + auto const projection = [&j, &f]( auto a ) { + return ( a | detail::projections[f.num_vars() - 1 - j] ); + }; + auto const projection_neg = [&j, &f]( auto a ) { + return ( a | detail::projections_neg[f.num_vars() - j - 1] ); + }; + if ( !is_dont_care( vars[j], i ) && !is_dont_know( vars[j], i ) ) + { + auto bit = get_bit( vars[j], i ); + if ( bit ) + { + auto ttt_mask = quaternary_truth_table( unary_operation( tt_mask, projection ) ); + f_copy &= ttt_mask; + } + else + { + auto ttt_mask = quaternary_truth_table( unary_operation( tt_mask, projection_neg ) ); + f_copy &= ttt_mask; + } + } + else + { + ++cnt_care; + if ( is_dont_know( vars[j], i ) ) + dont_know_found = true; + } + } + if ( is_const0( f_copy ) ) + { + if ( count_ones( f_copy._onset ) == ( 1 << cnt_care ) ) + set_dont_care( composed, i ); + else + set_bit( composed, i, false ); + } + else + { + if ( count_ones( f_copy._onset ) == ( 1 << cnt_care ) ) + { + set_bit( composed, i ); + } + else + { + if ( ( !dont_know_found && is_const0( ~f_copy._onset & ~f_copy._offset ) ) ) + set_dont_care( composed, i ); + else + set_dont_know( composed, i ); + } + } + } + + return composed; +} + +/*! \brief Shifts a small truth table with respect to a mask + + This function only works for truth tables with up to 6 inputs. The function + rearranges the variables according to a mask. For example, assume the 3-input + truth table \f$ x_0 \land x_1 \f$, which is not defined on \f$ x_2 \f$. + Applying this funtion with a mask `0b101` yields the function + \f$ x_0 \land x_2 \f$, and the mask `0b110` yields the function + \f$ x_1 \land x_2 \f$. The bits in the mask provide the new positions. It + is important that the positions in the mask do not exceed the truth table + size, since all operations are performed in-place and cannot change the + number of variables of the truth table. + + \param tt Truth table + \param mask Shift mask +*/ +template::value>> +inline void shift_with_mask_inplace( TT& f, uint8_t mask ) +{ + assert( f.num_vars() <= 6 ); + + *f.begin() = detail::compute_shift( *f.begin(), mask | ( 1 << f.num_vars() ) ); +} + +/*! \brief Shifts a small truth table with respect to a mask + + This function only works for truth tables with up to 6 inputs. The function + rearranges the variables according to a mask. For example, assume the 3-input + truth table \f$ x_0 \land x_1 \f$, which is not defined on \f$ x_2 \f$. + Applying this funtion with a mask `0b101` yields the function + \f$ x_0 \land x_2 \f$, and the mask `0b110` yields the function + \f$ x_1 \land x_2 \f$. The bits in the mask provide the new positions. It + is important that the positions in the mask do not exceed the truth table + size, since all operations are performed in-place and cannot change the + number of variables of the truth table. + The final result will not contain don't cares, they are all turned into 0s. + + \param tt Truth table + \param mask Shift mask +*/ +template::value>> +inline void shift_with_mask_inplace( ternary_truth_table& f, uint8_t mask ) +{ + assert( f.num_vars() <= 6 ); + + shift_with_mask_inplace( f._bits, mask ); + f._care = ~f._care.construct(); +} + +template::value>> +inline void shift_with_mask_inplace( quaternary_truth_table& f, uint8_t mask ) +{ + std::vector mask_from = {}; + std::vector mask_to = {}; + for ( auto i = 0u; i < f.num_vars(); i++ ) + { + if ( has_var( f, i ) ) + mask_from.push_back( i ); + } + for ( uint64_t count = 0u; mask > 0; mask = (int)mask / 2, count++ ) + { + if ( mask % 2 ) + mask_to.push_back( count ); + } + assert( mask_to.size() == mask_from.size() ); + std::vector index_remove = {}; + for ( auto i = 0u; i < mask_from.size(); i++ ) + { + auto it = std::find( mask_to.begin(), mask_to.end(), mask_from[i] ); + if ( it != mask_to.end() ) + { + mask_to.erase( it ); + mask_from.erase( mask_from.begin() + i ); + } + } + for ( auto i = 0u; i < mask_from.size(); i++ ) + { + swap_inplace( f, mask_from[i], mask_to[i] ); + } +} + +/*! \brief Shifts a small truth table with respect to a mask + + Out-of-place variant of `shift_with_mask_inplace`. + + \param tt Truth table + \param mask Shift mask +*/ +template +inline TT shift_with_mask( const TT& f, uint8_t mask ) +{ + auto copy = f; + shift_with_mask_inplace( copy, mask ); + return copy; +} + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/operators.hpp b/third-party/mockturtle/lib/kitty/kitty/operators.hpp new file mode 100644 index 00000000000..bc7e76c8fc3 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/operators.hpp @@ -0,0 +1,507 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file operators.hpp + \brief Implements operator shortcuts to operations + + \author Mathias Soeken +*/ + +#pragma once + +#include "dynamic_truth_table.hpp" +#include "operations.hpp" +#include "static_truth_table.hpp" +#include "partial_truth_table.hpp" +#include "ternary_truth_table.hpp" +#include "quaternary_truth_table.hpp" + +namespace kitty +{ + +/*! \brief Operator for unary_not */ +inline dynamic_truth_table operator~( const dynamic_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +template +inline static_truth_table operator~( const static_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +inline partial_truth_table operator~( const partial_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +template +inline ternary_truth_table operator~( const ternary_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for unary_not */ +template +inline quaternary_truth_table operator~( const quaternary_truth_table& tt ) +{ + return unary_not( tt ); +} + +/*! \brief Operator for binary_and */ +inline dynamic_truth_table operator&( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +template +inline static_truth_table operator&( const static_truth_table& first, const static_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +inline partial_truth_table operator&( const partial_truth_table& first, const partial_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +template +inline ternary_truth_table operator&( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and */ +template +inline quaternary_truth_table operator&( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +inline void operator&=( dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +template +inline void operator&=( static_truth_table& first, const static_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +inline void operator&=( partial_truth_table& first, const partial_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +template +inline void operator&=( ternary_truth_table& first, const ternary_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_and and assign */ +template +inline void operator&=( quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + first = binary_and( first, second ); +} + +/*! \brief Operator for binary_or */ +inline dynamic_truth_table operator|( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +template +inline static_truth_table operator|( const static_truth_table& first, const static_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +inline partial_truth_table operator|( const partial_truth_table& first, const partial_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +template +inline ternary_truth_table operator|( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or */ +template +inline quaternary_truth_table operator|( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +inline void operator|=( dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +template +inline void operator|=( static_truth_table& first, const static_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +inline void operator|=( partial_truth_table& first, const partial_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +template +inline void operator|=( ternary_truth_table& first, const ternary_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_or and assign */ +template +inline void operator|=( quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + first = binary_or( first, second ); +} + +/*! \brief Operator for binary_xor */ +inline dynamic_truth_table operator^( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +template +inline static_truth_table operator^( const static_truth_table& first, const static_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +inline partial_truth_table operator^( const partial_truth_table& first, const partial_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +template +inline ternary_truth_table operator^( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor */ +template +inline quaternary_truth_table operator^( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +inline void operator^=( dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +template +inline void operator^=( static_truth_table& first, const static_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +inline void operator^=( partial_truth_table& first, const partial_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +template +inline void operator^=( ternary_truth_table& first, const ternary_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for binary_xor and assign */ +template +inline void operator^=( quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + first = binary_xor( first, second ); +} + +/*! \brief Operator for equal */ +inline bool operator==( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +template +inline bool operator==( const static_truth_table& first, const static_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +inline bool operator==( const partial_truth_table& first, const partial_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +template +inline bool operator==( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for equal */ +template +inline bool operator==( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +inline bool operator!=( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +template +inline bool operator!=( const static_truth_table& first, const static_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equal */ +inline bool operator!=( const partial_truth_table& first, const partial_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +template +inline bool operator!=( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for not equals (!equal) */ +template +inline bool operator!=( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return !equal( first, second ); +} + +/*! \brief Operator for less_than */ +inline bool operator<( const dynamic_truth_table& first, const dynamic_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +template +inline bool operator<( const static_truth_table& first, const static_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +inline bool operator<( const partial_truth_table& first, const partial_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +template +inline bool operator<( const ternary_truth_table& first, const ternary_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for less_than */ +template +inline bool operator<( const quaternary_truth_table& first, const quaternary_truth_table& second ) +{ + return less_than( first, second ); +} + +/*! \brief Operator for left_shift */ +inline dynamic_truth_table operator<<( const dynamic_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +template +inline static_truth_table operator<<( const static_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +inline partial_truth_table operator<<( const partial_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +template +inline ternary_truth_table operator<<( const ternary_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift */ +template +inline quaternary_truth_table operator<<( const quaternary_truth_table& tt, uint64_t shift ) +{ + return shift_left( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +inline void operator<<=( dynamic_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +template +inline void operator<<=( static_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +inline void operator<<=( partial_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +template +inline void operator<<=( ternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for left_shift_inplace */ +template +inline void operator<<=( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_left_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift */ +inline dynamic_truth_table operator>>( const dynamic_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +template +inline static_truth_table operator>>( const static_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +inline partial_truth_table operator>>( const partial_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +template +inline ternary_truth_table operator>>( const ternary_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift */ +template +inline quaternary_truth_table operator>>( const quaternary_truth_table& tt, uint64_t shift ) +{ + return shift_right( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +inline void operator>>=( dynamic_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +template +inline void operator>>=( static_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +inline void operator>>=( partial_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +template +inline void operator>>=( ternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +/*! \brief Operator for right_shift_inplace */ +template +inline void operator>>=( quaternary_truth_table& tt, uint64_t shift ) +{ + shift_right_inplace( tt, shift ); +} + +} // namespace kitty diff --git a/third-party/mockturtle/lib/kitty/kitty/partial_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/partial_truth_table.hpp new file mode 100644 index 00000000000..fdf2812eab7 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/partial_truth_table.hpp @@ -0,0 +1,300 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file partial_truth_table.hpp + \brief Implements partial_truth_table + + \author Siang-Yun Lee +*/ + +#pragma once + +#include "detail/constants.hpp" +#include "traits.hpp" + +#include +#include +#include +#include +#include +#include + +namespace kitty +{ + +/*! Truth table with resizable, arbitrary number of bits + */ +struct partial_truth_table +{ + /*! \brief Standard constructor. + \param num_bits Number of bits in use initially + */ + explicit partial_truth_table( uint32_t num_bits ) + : _bits( num_bits ? ( ( ( num_bits - 1 ) >> 6 ) + 1 ) : 0 ), + _num_bits( num_bits ) + { + } + + /*! \brief Empty constructor. + + Creates an empty truth table. It has no bit in use. This constructor is + only used for convenience, if algorithms require the existence of default + constructable classes. + */ + partial_truth_table() : _num_bits( 0 ) {} + + /*! \brief Constructs a new partial truth table instance with the same number of bits and blocks. */ + inline partial_truth_table construct() const + { + return partial_truth_table( _num_bits ); + } + + /*! \brief Returns number of (allocated) blocks. + */ + inline auto num_blocks() const noexcept { return _bits.size(); } + + /*! \brief Returns number of (used) bits. + */ + inline auto num_bits() const noexcept { return _num_bits; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend() const noexcept { return _bits.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. The truth + table type is arbitrary. The vector of bits is resized accordingly. + + \param other Other truth table + */ + template::value>> + partial_truth_table& operator=( const TT& other ) + { + _bits.resize( other.num_blocks() ); + std::copy( other.begin(), other.end(), begin() ); + _num_bits = 1 << other.num_vars(); + + return *this; + } + + /*! \brief Masks the number of valid truth table bits. + + If not all the bits in the last block are used up, + we block out the remaining bits (fill with zero). + Bits are used from LSB. + */ + inline void mask_bits() noexcept + { + if ( _num_bits & 0x3f ) + { + _bits.back() &= 0xFFFFFFFFFFFFFFFF >> ( 64 - ( _num_bits & 0x3f ) ); + } + } + + /*! \brief Resize the truth table to have the given number of bits. + + If the desired length is larger than the current length, zeros will + be filled up in the end of the truth table. If the desired length + is shorter than the current length, extra bits in the end of the + truth table will be discarded. + + \param num_bits Desired length (number of bits) + */ + inline void resize( int num_bits ) noexcept + { + _num_bits = num_bits; + + unsigned needed_blocks = num_bits ? ( ( ( num_bits - 1 ) >> 6 ) + 1 ) : 0; + _bits.resize( needed_blocks, 0u ); + + mask_bits(); + } + + /*! \brief Add a bit to the end of the truth table. + \param bit Bit to be added + */ + inline void add_bit( bool bit ) noexcept + { + resize( _num_bits + 1 ); + if ( bit ) + { + _bits.back() |= (uint64_t)1 << ( ( _num_bits & 0x3f ) - 1 ); + } + } + + /*! \brief Add several bits to the end of the truth table. + \param bits Bits to be added + */ + inline void add_bits( std::vector& bits ) noexcept + { + for ( unsigned i = 0; i < bits.size(); ++i ) + { + add_bit( bits.at( i ) ); + } + } + + /*! \brief Add several bits to the end of the truth table. + \param bits Bits to be added + \param num_bits Number of bits in `bits` to be added (counted from LSB) + */ + inline void add_bits( uint64_t bits, int num_bits = 64 ) noexcept + { + assert( num_bits <= 64 ); + + if ( ( _num_bits & 0x3f ) + num_bits <= 64 ) /* no need for a new block */ + { + if ( _bits.size() == 0u ) + { + _bits.emplace_back( 0u ); + } + _bits.back() |= bits << ( _num_bits & 0x3f ); + } + else + { + auto first_half_len = 64 - ( _num_bits & 0x3f ); + _bits.back() |= bits << ( _num_bits & 0x3f ); + _bits.emplace_back( 0u ); + _bits.back() |= ( bits & ( 0xFFFFFFFFFFFFFFFF >> ( 64 - num_bits ) ) ) >> first_half_len; + } + _num_bits += num_bits; + } + + /*! \brief Overwrite the value at position `from` to position `to`. + \param from Position of the bit to be copied + \param to Position of the bit to be overwritten + */ + inline void copy_bit( uint32_t from, uint32_t to ) + { + if ( ( _bits[from >> 6] >> ( from & 0x3f ) ) & 0x1 ) + { + /* set_bit( to ) */ + _bits[to >> 6] |= uint64_t( 1 ) << ( to & 0x3f ); + } + else + { + /* clear_bit( to ) */ + _bits[to >> 6] &= ~( uint64_t( 1 ) << ( to & 0x3f ) ); + } + } + + /*! \brief Erase the bit at the given position by swapping with the + last bit and shrinking the truth table. + + This method is faster than `erase_bit` but do not keep the order + of the bits. + + \param position Position of the bit to be erased + */ + inline void erase_bit_swap( uint32_t position ) + { + assert( position < _num_bits ); + /* move the last bit (`_num_bits - 1`) to `position` */ + copy_bit( _num_bits - 1, position ); + resize( _num_bits - 1 ); + } + + /*! \brief Erase the bit at the given position and shift all the following bits. + + This method keeps the order of the bits but is slower and + do not keep the position of the bits. + + \param position Position of the bit to be erased + */ + inline void erase_bit_shift( uint32_t position ) + { + assert( position < _num_bits ); + uint64_t mask = 0xFFFFFFFFFFFFFFFF << ( position & 0x3f ); + _bits[position >> 6] = ( _bits[position >> 6] & ~mask ) | ( _bits[position >> 6] >> 1 & mask ); + uint32_t hole = position | 0x3f; + while ( hole < _num_bits - 1 ) + { + copy_bit( hole + 1, hole ); + hole += 64; + _bits[hole >> 6] >>= 1; + } + resize( _num_bits - 1 ); + } + + /*! \cond PRIVATE */ +public: /* fields */ + std::vector _bits; + uint32_t _num_bits; + /*! \endcond */ +}; + +template<> +struct is_truth_table : std::true_type +{ +}; + +template<> +struct is_complete_truth_table : std::false_type +{ +}; + +template<> +struct is_completely_specified_truth_table : std::true_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/print.hpp b/third-party/mockturtle/lib/kitty/kitty/print.hpp new file mode 100644 index 00000000000..e7e0980391d --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/print.hpp @@ -0,0 +1,570 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file print.hpp + \brief Implements functions to print truth tables + + \author Mathias Soeken + \author Rassul Bairamkulov +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "algorithm.hpp" +#include "karnaugh_map.hpp" +#include "operations.hpp" +#include "constructors.hpp" +#include "isop.hpp" + +namespace kitty +{ + +namespace detail +{ + +inline std::string to_binary( uint16_t value, uint32_t num_vars ) +{ + std::string res( num_vars, '0' ); + auto it = res.end() - 1; + while ( value ) + { + if ( value & 1 ) + { + *it = '1'; + } + value >>= 1; + if ( it == res.begin() ) + { + break; + } + --it; + } + return res; +} + +inline void print_xmas_tree( + std::ostream& os, uint32_t num_vars, + const std::vector, + std::vector>>& style_predicates = {} ) +{ + /* create rows */ + std::vector> current( 1, { 0 } ), next; + + for ( auto i = 0u; i < num_vars; ++i ) + { + for ( const auto& row : current ) + { + if ( row.size() != 1u ) + { + next.emplace_back(); + + std::transform( row.begin() + 1, row.end(), + std::back_inserter( next.back() ), + []( auto cell ) + { return cell << 1; } ); + } + next.emplace_back( 1, row.front() << 1 ); + std::transform( row.begin(), row.end(), std::back_inserter( next.back() ), + []( auto cell ) + { return ( cell << 1 ) ^ 1; } ); + } + + std::swap( current, next ); + next.clear(); + } + + for ( const auto& row : current ) + { + /* white space padding to center columns */ + os << std::string( ( ( num_vars + 1 ) - row.size() ) / 2 * ( num_vars + 1 ), ' ' ); + for ( const auto& col : row ) + { + os << " "; + for ( const auto& pred : style_predicates ) + { + if ( pred.first( col ) ) + { + for ( auto style : pred.second ) + { + os << "\033[" << style << "m"; + } + } + } + os << to_binary( col, num_vars ) << "\033[0m"; + } + os << "\n"; + } +} + +} // namespace detail + +/*! \brief Prints truth table in binary representation + + The most-significant bit will be the first character of the string. + + \param tt Truth table + \param os Output stream +*/ +template +void print_binary( const TT& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + for_each_block_reversed( tt, [&os, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + if (word & 1) { + *it = '1'; + } + ++it; + word >>= 1; + } + os << chunk; } ); +} + +/*! \cond PRIVATE */ +inline void print_binary( const partial_truth_table& tt, + std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + bool first = true; + for_each_block_reversed( tt, [&tt, &os, chunk_size, &first]( auto word ) + { + std::string chunk( chunk_size, '0' ); + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + if (word & 1) { + *it = '1'; + } + ++it; + word >>= 1; + } + + if (first && (chunk_size == 64) && (tt.num_bits() % 64)) { + first = false; + os << chunk.substr(64 - (tt.num_bits() % 64)); + } else { + os << chunk; + } } ); +} +/*! \endcond */ + +template +void print_binary( const ternary_truth_table& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + std::string tt_string = ""; + for_each_block_reversed( tt._bits, [&tt_string, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + auto it = chunk.rbegin(); + while ( word && it != chunk.rend() ) + { + if ( word & 1 ) + { + *it = '1'; + } + ++it; + word >>= 1; + } + tt_string += chunk; } ); + for ( auto i = 0u; i < tt.num_bits(); i++ ) + { + if ( is_dont_care( tt, tt.num_bits() - 1 - i ) ) + { + tt_string[i] = '-'; + } + } + os << tt_string; +} + +template +void print_binary( const quaternary_truth_table& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = std::min( tt.num_bits(), 64 ); + std::string tt_string = ""; + for_each_block_reversed( tt._onset, [&tt_string, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + auto it = chunk.rbegin(); + while ( word && it != chunk.rend() ) + { + if ( word & 1 ) + { + *it = '1'; + } + ++it; + word >>= 1; + } + tt_string += chunk; } ); + for ( auto i = 0u; i < tt.num_bits(); i++ ) + { + if ( is_dont_care( tt, tt.num_bits() - 1 - i ) ) + tt_string[i] = '-'; + if ( is_dont_know( tt, tt.num_bits() - 1 - i ) ) + tt_string[i] = 'x'; + } + os << tt_string; +} + +/*! \brief Prints K-map of given truth table. + + Columns represent the values of the least significant variables, rows of the + most significant ones. + + \param tt Truth table + \param os Output stream (default = cout) +*/ +template +void print_kmap( const TT& tt, std::ostream& os = std::cout ) +{ + karnaugh_map kmap( tt ); + kmap.print( os ); +} + +/*! \brief Prints truth table in hexadecimal representation + + The most-significant bit will be the first character of the string. + + \param tt Truth table + \param os Output stream +*/ +template::value>> +void print_hex( const TT& tt, std::ostream& os = std::cout ) +{ + auto const chunk_size = + std::min( tt.num_vars() <= 1 ? 1 : ( tt.num_bits() >> 2 ), 16 ); + + for_each_block_reversed( tt, [&os, chunk_size]( auto word ) + { + std::string chunk( chunk_size, '0' ); + + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + auto hex = word & 0xf; + if (hex < 10) { + *it = '0' + static_cast(hex); + } else { + *it = 'a' + static_cast(hex - 10); + } + ++it; + word >>= 4; + } + os << chunk; } ); +} + +/*! \cond PRIVATE */ +inline void print_hex( const partial_truth_table& tt, + std::ostream& os = std::cout ) +{ + bool first = true; + for_each_block_reversed( tt, [&tt, &os, &first]( auto word ) + { + std::string chunk( 16, '0' ); + + auto it = chunk.rbegin(); + while (word && it != chunk.rend()) { + auto hex = word & 0xf; + if (hex < 10) { + *it = '0' + static_cast(hex); + } else { + *it = 'a' + static_cast(hex - 10); + } + ++it; + word >>= 4; + } + + if (first && (tt.num_bits() % 64)) { + first = false; + os << chunk.substr((tt.num_bits() % 4) + ? (15 - ((tt.num_bits() >> 2) % 16)) + : (16 - ((tt.num_bits() >> 2) % 16))); + } else { + os << chunk; + } } ); +} +/*! \endcond */ + +/*! \brief Prints truth table in raw binary presentation (for file I/O) + + This function is useful to store large truth tables in binary files + or `std::stringstream`. Each word is stored into 8 characters. + + \param tt Truth table + \param os Output stream +*/ +template>::value>> +void print_raw( const TT& tt, std::ostream& os ) +{ + for_each_block( tt, [&os]( auto word ) + { os.write( reinterpret_cast( &word ), sizeof( word ) ); } ); +} + +/*! \brief Returns truth table as a string in binary representation + + Calls `print_binary` internally on a string stream. + + \param tt Truth table +*/ +template +inline std::string to_binary( const TT& tt ) +{ + std::stringstream st; + print_binary( tt, st ); + return st.str(); +} + +/*! \brief Returns truth table as a string in hexadecimal representation + + Calls `print_hex` internally on a string stream. + + \param tt Truth table +*/ +template::value>> +inline std::string to_hex( const TT& tt ) +{ + std::stringstream st; + print_hex( tt, st ); + return st.str(); +} + +/*! \brief Prints minterms of a Boolean function in christmas tree pattern + + This function prints all minterms of a Boolean function and arranges them + according to the christmas tree pattern as described in Section 7.2.1.6 in + The Art of Computer Programming by Donald E. Knuth. Minterms from the + off-set are printed in red, minterms from the on-set are printed in green. + + \param tt Truth table + \param os Output stream +*/ +template::value>, typename = std::enable_if_t::value>> + +void print_xmas_tree_for_function( const TT& tt, std::ostream& os = std::cout ) +{ + detail::print_xmas_tree( os, tt.num_vars(), + { { [&]( auto v ) + { return get_bit( tt, v ); }, + { 32 } }, + { [&]( auto v ) + { return !get_bit( tt, v ); }, + { 31 } } } ); +} + +/*! \brief Prints all Boolean functions of n variables in christmas tree pattern + + This function prints all Boolean functions of n variables and arranges them + according to the christmas tree pattern as described in Section 7.2.1.6 in + The Art of Computer Programming by Donald E. Knuth. Functions can be printed + in different styles according to some properties. + + \param tt Number of variables + \param style_predicates Each pair has a predicate `bool(TT const&)` to check + whether a certain property holds for the truth table + the element in the tree represents. If this predicate + evaluates to true, then the second element in the pair + are indexes of style (ANSI term) to change the + string in the output. + \param os Output stream +*/ +template +void print_xmas_tree_for_functions( + uint32_t num_vars, + const std::vector, + std::vector>>& style_predicates = {}, + std::ostream& os = std::cout ) +{ + + std::vector, std::vector>> + _preds; + std::transform( style_predicates.begin(), style_predicates.end(), + std::back_inserter( _preds ), [&]( const auto& p ) + { return std::make_pair( + [&]( uint16_t v ) + { + auto tt = create( num_vars ); + std::copy( &v, &v + 1, tt.begin() ); + return p.first( tt ); + }, + p.second ); } ); + + detail::print_xmas_tree( os, 1 << num_vars, _preds ); +} + +/*! \brief Creates an expression for an ANF form + * + * \param anf Truth table in ANF encoding + */ +template::value>> +std::string anf_to_expression( const TT& anf ) +{ + const auto terms = count_ones( anf ); + + if ( terms == 0u ) + { + return "0"; + } + + std::string expr; + + for_each_one_bit( anf, [&]( auto bit ) + { + if ( bit == 0 ) + { + + expr += "1"; + return; + } + auto weight = __builtin_popcount(static_cast(bit)); + if (weight != 1) { + expr += "("; + } + for (auto i = 0u; i < anf.num_vars(); ++i) { + if ((bit >> i) & 1) { + expr += std::string(1, 'a' + i); + } + } + if (weight != 1) { + expr += ")"; + } } ); + + return terms == 1 ? expr : "[" + expr + "]"; +} + +/*! \brief Creates an expression for an ANF form + + All don't cares are considered to be zero. + + It is a "restricted" ANF for the ternary turth table. + * + * \param anf Ternary truth table in ANF encoding + */ +template::value>> +std::string anf_to_expression( const ternary_truth_table& anf ) +{ + return anf_to_expression( anf._bits ); +} + +/*! \brief Creates an expression in the sum of products format. + + Does not perform any optimizations. Useful when constructing GENLIB-compatible expressions + + \param tt Truth table + \param os Output stream + */ +template::value>> +void print_sop_expression( TT tt, std::ostream& os = std::cout ) +{ + /* compare to constants */ + if ( is_const0( tt ) ) + { + os << "CONST0"; + return; + } + else if ( is_const0( ~tt ) ) + { + os << "CONST1"; + return; + } + + /* extract product terms */ + auto cubes = kitty::isop( tt ); + + bool first_cube = true; // Controls insertion of '|'. It's false for the first cube to avoid leading '|'. + + /* write product terms */ + for ( auto cube : cubes ) + { + auto bits = cube._bits; + auto mask = cube._mask; + + bool brackets = __builtin_popcount( mask ) > 1; + + if ( !first_cube ) + { + os << '|'; + } + + if ( brackets ) + { + os << '('; + } + + bool first_literal = true; + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + if ( mask & 1 ) + { + if ( !first_literal ) + { + os << '&'; + } + if ( !( bits & 1 ) ) + { + os << '!'; + } + os << static_cast( 'a' + i ); + first_literal = false; + } + bits >>= 1; + mask >>= 1; + } + + if ( brackets ) + { + os << ')'; + } + first_cube = false; + } +} + +/*! \brief Creates an expression in the sum of products format. + + Does not perform any optimizations. Useful when constructing GENLIB-compatible expressions + + \param tt Truth table + */ +template::value>> +std::string to_sop_expression( TT tt ) +{ + std::stringstream os; + print_sop_expression( tt, os ); // Use the function to write into the stringstream + return os.str(); // Convert the stringstream to string and return it +} + +} /* namespace kitty */ \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/quaternary_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/quaternary_truth_table.hpp new file mode 100644 index 00000000000..14996badd0f --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/quaternary_truth_table.hpp @@ -0,0 +1,265 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file quaternary_truth_table.hpp + \brief Implements quaternary_truth_table + + \author Siang-Yun Lee + \author Gianluca Radi +*/ + +#pragma once + +#include +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" + +namespace kitty +{ + +template +struct quaternary_truth_table +{ + /*! \brief Standard constructor. + + Initialize the truth table using the constructor of the inner + truth table type. + + \param n Number of variables or number of bits (when `TT = partial_truth_table`) + */ + explicit quaternary_truth_table( uint32_t n ) + : _onset( n ), _offset( n ) + { + } + + /*! \brief Empty constructor. + + Creates an empty truth table by calling the empty constructor + of the inner truth table type. + */ + quaternary_truth_table() : _onset(), _offset() {} + + /*! \brief Construct from onset and offset. + + Meanings of the values of (offset, onset) at each bit position: + 00 is a don't-know (x) or not involved in the cube, + 01 is a positive literal (1), 10 is a negative literal (0), and + 11 is a don't-care (-), meaning that both 0 and 1 are accepted. + + \param onset Onset truth table. + \param offset Offset truth table. + */ + quaternary_truth_table( TT const& onset, TT const& offset ) + : _onset( onset ), _offset( offset ) + { + } + + /*! \brief Construct from a binary truth table. + + Initialize the truth table as equivalent to a binary truth table. + (All bits are cared and known, being either `0` or `1`, without any + `-` or `x`.) + + \param binary Binary truth table. + */ + quaternary_truth_table( TT const& binary ) + : _onset( binary ), _offset( ~binary ) + { + } + + /*! Constructs a new quaternary_truth_table instance of the same size. */ + inline quaternary_truth_table construct() const + { + if constexpr ( std::is_same_v ) + return quaternary_truth_table( _onset.num_bits() ); + else + return quaternary_truth_table( _onset.num_vars() ); + } + + /*! Returns number of variables. + */ + template::value>> + auto num_vars() const noexcept { return _onset.num_vars(); } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return _onset.num_blocks(); } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return _onset.num_bits(); } + + /*! \brief Begin iterator to onset. + */ + inline auto begin_onset() noexcept { return _onset.begin(); } + + /*! \brief End iterator to onset. + */ + inline auto end_onset() noexcept { return _onset.end(); } + + /*! \brief Begin iterator to onset. + */ + inline auto begin_onset() const noexcept { return _onset.begin(); } + + /*! \brief End iterator to onset. + */ + inline auto end_onset() const noexcept { return _onset.end(); } + + /*! \brief Reverse begin iterator to onset. + */ + inline auto rbegin_onset() noexcept { return _onset.rbegin(); } + + /*! \brief Reverse end iterator to onset. + */ + inline auto rend_onset() noexcept { return _onset.rend(); } + + /*! \brief Constant begin iterator to onset. + */ + inline auto cbegin_onset() const noexcept { return _onset.cbegin(); } + + /*! \brief Constant end iterator to onset. + */ + inline auto cend_onset() const noexcept { return _onset.cend(); } + + /*! \brief Constant reverse begin iterator to onset. + */ + inline auto crbegin_onset() const noexcept { return _onset.crbegin(); } + + /*! \brief Constant teverse end iterator to onset. + */ + inline auto crend_onset() const noexcept { return _onset.crend(); } + + /*! \brief Begin iterator to offset. + */ + inline auto begin_offset() noexcept { return _offset.begin(); } + + /*! \brief End iterator to offset. + */ + inline auto end_offset() noexcept { return _offset.end(); } + + /*! \brief Begin iterator to offset. + */ + inline auto begin_offset() const noexcept { return _offset.begin(); } + + /*! \brief End iterator to offset. + */ + inline auto end_offset() const noexcept { return _offset.end(); } + + /*! \brief Reverse begin iterator to offset. + */ + inline auto rbegin_offset() noexcept { return _offset.rbegin(); } + + /*! \brief Reverse end iterator to offset. + */ + inline auto rend_offset() noexcept { return _offset.rend(); } + + /*! \brief Constant begin iterator to offset. + */ + inline auto cbegin_offset() const noexcept { return _offset.cbegin(); } + + /*! \brief Constant end iterator to offset. + */ + inline auto cend_offset() const noexcept { return _offset.cend(); } + + /*! \brief Constant reverse begin iterator to offset. + */ + inline auto crbegin_offset() const noexcept { return _offset.crbegin(); } + + /*! \brief Constant teverse end iterator to offset. + */ + inline auto crend_offset() const noexcept { return _offset.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. + The other truth table must also be quaternary, and the inner type of + the other truth table must be assignable to the inner type of this + truth table. + + \param other Other truth table + */ + template + quaternary_truth_table& operator=( const quaternary_truth_table& other ) + { + _onset = other._onset; + _offset = other._offset; + + return *this; + } + + /*! \brief Assigned by a binary truth table. + + Replaces with a truth table equivalent to a binary truth table. + (All bits are cared and known, being either `0` or `1`, without any + `-` or `x`.) + + \param other Binary truth table. + */ + template + quaternary_truth_table& operator=( TT const& other ) + { + _onset = other; + _offset = ~other; + + return *this; + } + + /*! \brief Masks valid truth table bits. + + This operation makes sure to zero out all unused bits. + */ + inline void mask_bits() noexcept + { + _onset.mask_bits(); + _offset.mask_bits(); + } + + /*! \cond PRIVATE */ +public: /* fields */ + TT _onset; + TT _offset; + /*! \endcond */ +}; + +template +struct is_truth_table> : is_truth_table +{ +}; + +template +struct is_complete_truth_table> : is_complete_truth_table +{ +}; + +template +struct is_completely_specified_truth_table> : std::false_type +{ +}; + +} // namespace kitty diff --git a/third-party/mockturtle/lib/kitty/kitty/static_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/static_truth_table.hpp new file mode 100644 index 00000000000..437ba8af0b8 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/static_truth_table.hpp @@ -0,0 +1,290 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file static_truth_table.hpp + \brief Implements static_truth_table + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" + +namespace kitty +{ + +/*! Truth table in which number of variables is known at compile time. + + We dispatch on the Boolean template parameter to distinguish between + a small truth table (up to 6 variables) and a large truth table + (more than 6 variables). A small truth table fits into a single + block and therefore dedicated optimizations are possible. + */ +template +struct static_truth_table; + +/*! Truth table (for up to 6 variables) in which number of variables is known at compile time. + */ +template +struct static_truth_table +{ + /*! \cond PRIVATE */ + enum + { + NumBits = uint64_t( 1 ) << NumVars + }; + /*! \endcond */ + + /*! Constructs a new static truth table instance with the same number of variables. */ + inline static_truth_table construct() const + { + return static_truth_table(); + } + + /*! Returns number of variables. + */ + inline auto num_vars() const noexcept { return NumVars; } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return 1u; } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return NumBits; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return &_bits; } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return ( &_bits ) + 1; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return &_bits; } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return &_bits; } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return ( &_bits ) + 1; } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return &_bits; } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return &_bits; } + + /*! \brief Constant everse end iterator to bits. + */ + inline auto crend() const noexcept { return ( &_bits ) + 1; } + + /*! \brief Assign other truth table if number of variables match. + + This replaces the current truth table with another truth table, if `other` + has the same number of variables. Otherwise, the truth table is not + changed. + + \param other Other truth table + */ + template::value && is_complete_truth_table::value>> + static_truth_table& operator=( const TT& other ) + { + if ( other.num_vars() == num_vars() ) + { + std::copy( other.begin(), other.end(), begin() ); + } + + return *this; + } + + /*! Masks the number of valid truth table bits. + + If the truth table has less than 6 variables, it may not use all + the bits. This operation makes sure to zero out all non-valid + bits. + */ + inline void mask_bits() noexcept { _bits &= detail::masks[NumVars]; } + + /*! \cond PRIVATE */ +public: /* fields */ + uint64_t _bits = 0; + /*! \endcond */ +}; + +/*! Truth table (more than 6 variables) in which number of variables is known at compile time. + */ +template +struct static_truth_table +{ + /*! \cond PRIVATE */ + enum + { + NumBlocks = ( NumVars <= 6 ) ? 1u : ( 1u << ( NumVars - 6 ) ) + }; + + enum + { + NumBits = uint64_t( 1 ) << NumVars + }; + /*! \endcond */ + + /*! Standard constructor. + + The number of variables provided to the truth table must be known + at runtime. The number of blocks will be computed as a compile + time constant. + */ + static_truth_table() + { + _bits.fill( 0 ); + } + + /*! Constructs a new static truth table instance with the same number of variables. */ + inline static_truth_table construct() const + { + return static_truth_table(); + } + + /*! Returns number of variables. + */ + inline auto num_vars() const noexcept { return NumVars; } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return NumBlocks; } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return NumBits; } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend() const noexcept { return _bits.crend(); } + + /*! \brief Assign other truth table if number of variables match. + + This replaces the current truth table with another truth table, if `other` + has the same number of variables. Otherwise, the truth table is not + changed. + + \param other Other truth table + */ + template::value>> + static_truth_table& operator=( const TT& other ) + { + if ( other.num_bits() == num_bits() ) + { + std::copy( other.begin(), other.end(), begin() ); + } + + return *this; + } + + /*! Masks the number of valid truth table bits. + + We know that we will have at least 7 variables in this data + structure. + */ + inline void mask_bits() noexcept {} + + /*! \cond PRIVATE */ +public: /* fields */ + std::array _bits; + /*! \endcond */ +}; + +template +struct is_truth_table> : std::true_type +{ +}; + +template +struct is_complete_truth_table> : std::true_type +{ +}; + +template +struct is_completely_specified_truth_table> : std::true_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/kitty/kitty/ternary_truth_table.hpp b/third-party/mockturtle/lib/kitty/kitty/ternary_truth_table.hpp new file mode 100644 index 00000000000..0fe5a40e6b0 --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/ternary_truth_table.hpp @@ -0,0 +1,262 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file ternary_truth_table.hpp + \brief Implements ternary_truth_table + + \author Siang-Yun Lee + \author Gianluca Radi +*/ + +#pragma once + +#include +#include +#include +#include + +#include "detail/constants.hpp" +#include "traits.hpp" +#include "partial_truth_table.hpp" + +namespace kitty +{ + +template +struct ternary_truth_table +{ + /*! \brief Standard constructor. + + Initialize the truth table using the constructor of the inner + truth table type. + + \param n Number of variables or number of bits (when `TT = partial_truth_table`) + */ + explicit ternary_truth_table( uint32_t n ) + : _care( n ), _bits( n ) + { + } + + /*! \brief Empty constructor. + + Creates an empty truth table by calling the empty constructor + of the inner truth table type. + */ + ternary_truth_table() : _care(), _bits() {} + + /*! \brief Construct from bits and care. + + When `care` bit is 0, `bits` bit must be 0, meaning a don't care bit. + + \param bits Bits truth table. + \param care Care truth table. + */ + ternary_truth_table( TT const& bits, TT const& care ) + : _care( care ), _bits( bits ) + { + } + + /*! \brief Construct from a binary truth table. + + Initialize the truth table as equivalent to a binary truth table. + (All bits are cared.) + + \param binary Binary truth table. + */ + ternary_truth_table( TT const& binary ) + : _care( ~( binary.construct() ) ), _bits( binary ) + { + } + + /*! Constructs a new ternary_truth_table instance of the same size. */ + inline ternary_truth_table construct() const + { + if constexpr ( std::is_same_v ) + return ternary_truth_table( _bits.num_bits() ); + else + return ternary_truth_table( _bits.num_vars() ); + } + + /*! Returns number of variables. + */ + template::value>> + auto num_vars() const noexcept { return _bits.num_vars(); } + + /*! Returns number of blocks. + */ + inline auto num_blocks() const noexcept { return _bits.num_blocks(); } + + /*! Returns number of bits. + */ + inline auto num_bits() const noexcept { return _bits.num_bits(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin_bits() noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end_bits() noexcept { return _bits.end(); } + + /*! \brief Begin iterator to bits. + */ + inline auto begin_bits() const noexcept { return _bits.begin(); } + + /*! \brief End iterator to bits. + */ + inline auto end_bits() const noexcept { return _bits.end(); } + + /*! \brief Reverse begin iterator to bits. + */ + inline auto rbegin_bits() noexcept { return _bits.rbegin(); } + + /*! \brief Reverse end iterator to bits. + */ + inline auto rend_bits() noexcept { return _bits.rend(); } + + /*! \brief Constant begin iterator to bits. + */ + inline auto cbegin_bits() const noexcept { return _bits.cbegin(); } + + /*! \brief Constant end iterator to bits. + */ + inline auto cend_bits() const noexcept { return _bits.cend(); } + + /*! \brief Constant reverse begin iterator to bits. + */ + inline auto crbegin_bits() const noexcept { return _bits.crbegin(); } + + /*! \brief Constant teverse end iterator to bits. + */ + inline auto crend_bits() const noexcept { return _bits.crend(); } + + /*! \brief Begin iterator to care. + */ + inline auto begin_care() noexcept { return _care.begin(); } + + /*! \brief End iterator to care. + */ + inline auto end_care() noexcept { return _care.end(); } + + /*! \brief Begin iterator to care. + */ + inline auto begin_care() const noexcept { return _care.begin(); } + + /*! \brief End iterator to care. + */ + inline auto end_care() const noexcept { return _care.end(); } + + /*! \brief Reverse begin iterator to care. + */ + inline auto rbegin_care() noexcept { return _care.rbegin(); } + + /*! \brief Reverse end iterator to care. + */ + inline auto rend_care() noexcept { return _care.rend(); } + + /*! \brief Constant begin iterator to care. + */ + inline auto cbegin_care() const noexcept { return _care.cbegin(); } + + /*! \brief Constant end iterator to care. + */ + inline auto cend_care() const noexcept { return _care.cend(); } + + /*! \brief Constant reverse begin iterator to care. + */ + inline auto crbegin_care() const noexcept { return _care.crbegin(); } + + /*! \brief Constant teverse end iterator to care. + */ + inline auto crend_care() const noexcept { return _care.crend(); } + + /*! \brief Assign other truth table. + + This replaces the current truth table with another truth table. + The other truth table must also be ternary, and the inner type of + the other truth table must be assignable to the inner type of this + truth table. + + \param other Other truth table + */ + template + ternary_truth_table& operator=( const ternary_truth_table& other ) + { + _bits = other._bits; + _care = other._care; + + return *this; + } + + /*! \brief Assigned by a binary truth table. + + Replaces with a truth table equivalent to a binary truth table. + (All bits are cared.) + + \param other Binary truth table. + */ + template + ternary_truth_table& operator=( TT const& other ) + { + _bits = other; + _care = ~( other.construct() ); + + return *this; + } + + /*! \brief Masks valid truth table bits. + + This operation makes sure to zero out all unused bits. + */ + inline void mask_bits() noexcept + { + _care.mask_bits(); + _bits.mask_bits(); + } + + /*! \cond PRIVATE */ +public: /* fields */ + TT _care; + TT _bits; + /*! \endcond */ +}; + +template +struct is_truth_table> : is_truth_table +{ +}; + +template +struct is_complete_truth_table> : is_complete_truth_table +{ +}; + +template +struct is_completely_specified_truth_table> : std::false_type +{ +}; + +} // namespace kitty diff --git a/third-party/mockturtle/lib/kitty/kitty/traits.hpp b/third-party/mockturtle/lib/kitty/kitty/traits.hpp new file mode 100644 index 00000000000..5281acd21dd --- /dev/null +++ b/third-party/mockturtle/lib/kitty/kitty/traits.hpp @@ -0,0 +1,55 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2025 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file traits.hpp + \brief Type traits for truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include + +namespace kitty +{ + +template +struct is_truth_table : std::false_type +{ +}; + +template +struct is_complete_truth_table : std::false_type +{ +}; + +template +struct is_completely_specified_truth_table : std::false_type +{ +}; + +} // namespace kitty \ No newline at end of file diff --git a/third-party/mockturtle/lib/lorina/lorina/common.hpp b/third-party/mockturtle/lib/lorina/lorina/common.hpp new file mode 100644 index 00000000000..3dd1272b4c5 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/common.hpp @@ -0,0 +1,44 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file common.hpp + \brief Common data structures. + + \author Heinz Riener +*/ + +#pragma once + +namespace lorina +{ + +enum class return_code +{ + success = 0, + parse_error, +}; + +} // namespace lorina \ No newline at end of file diff --git a/third-party/mockturtle/lib/lorina/lorina/detail/call_in_topological_order.hpp b/third-party/mockturtle/lib/lorina/lorina/detail/call_in_topological_order.hpp new file mode 100644 index 00000000000..cdf7645e968 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/detail/call_in_topological_order.hpp @@ -0,0 +1,408 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! \cond PRIVATE */ + +#pragma once + +#include + +namespace lorina +{ + +namespace detail +{ + +/* std::apply in C++14 taken from https://stackoverflow.com/a/36656413 */ +template +auto apply( Function f, Tuple t, std::index_sequence ) +{ + return f( std::get(t)... ); +} + +template +auto apply( Function f, Tuple t ) +{ + static constexpr auto size = std::tuple_size::value; + return apply( f, t, std::make_index_sequence{} ); +} + +/* \brief Parameter pack + * + * A pack of parameters of different types + * + * Example: + * using PackedParams = ParamPack; + * + * PackedParams params; + * params.set( 10, "a", 10.5f ); + */ +template +class ParamPack +{ +public: + using Tuple = std::tuple; + +public: + explicit ParamPack() {} + + explicit ParamPack( Args... params ) + : params_( std::make_tuple( params... ) ) + {} + + explicit ParamPack( const std::tuple& tup ) + : params_( tup ) + {} + + void set( Args... params ) + { + params_ = std::make_tuple( params... ); + } + + auto get() const + { + return params_; + } + +private: + std::tuple params_; +}; // ParamPack + +/* \brief Multple parameter packs + * + * A vector of N parameter packs + * + * Example: + * using PackedParamsA = ParamPack; + * using PackedParamsB = ParamPack; + * + * PackedParamsA params_a( 10, "a", 4 ); + * PackedParamsB params_b( 2.0, 3 ); + * ParamPackN + * params( params_a, params_b ); + */ +template +class ParamPackN +{ +public: + explicit ParamPackN() + {} + + explicit ParamPackN( ParamPacks... packs ) + : packs_( std::make_tuple( packs... ) ) + {} + + template + void set( typename std::tuple_element>::type const& pack ) + { + static_assert( I < sizeof...( ParamPacks ) ); + std::get( packs_ ) = pack; + } + + template + auto get() const + { + static_assert( I < sizeof...( ParamPacks ) ); + return std::get( packs_ ); + } + +private: + std::tuple packs_; +}; // ParamPackN + +/* \brief Parameter pack map + * + * A map from a KeyType to a parameter pack + * + * Example: + * ParamPackMap param_map; + * param_map["a"] = ParamPack( 10, "a", 10 ); + * param_map["b"] = ParamPack( 10, "b", 10 ); + */ +template +class ParamPackMap +{ +public: + using ValueType = typename detail::ParamPack::Tuple; + +public: + explicit ParamPackMap() {} + + auto& operator[]( const KeyType& key ) + { + return map_[key]; + } + + const auto& operator[]( const KeyType& key ) const + { + return map_[key]; + } + + void insert( const KeyType& key, const std::tuple& tup ) + { + map_[key] = ParamPack( tup ); + } + + void insert( const KeyType& key, Args... args ) + { + map_[key] = ParamPack( std::make_tuple( args... ) ); + } + +private: + std::unordered_map> map_; +}; + +/* \brief Multiple parameter pack maps + * + * A vector of N parameter pack maps + * + * Example: + * using StringToStringMap = ParamPackMap; + * using StringToIntMap = ParamPackMap; + * + * ParamPackMapN maps; + * maps.get<0>()["a"] = ParamPack( std::string{"a"} ); + * maps.get<1>()["b"] = ParamPack( 10 ); + */ +template +class ParamPackMapN +{ +public: + explicit ParamPackMapN() {} + + template + auto& get() + { + static_assert( I < sizeof...( ParamMaps ) ); + return std::get( maps_ ); + } + + template + auto& get() const + { + static_assert( I < sizeof...( ParamMaps ) ); + return std::get( maps_ ); + } + +private: + std::tuple maps_; +}; + +/* \brief A callable function + * + * Example: + * Func f; + * f( std::tuple( 10, "a" ) ); + * f( std::tuple( 12, "b" ) ); + */ +template +class Func +{ +public: + using Tuple = std::tuple; + +public: + Func( std::function fn ) + : fn_( fn ) + {} + + void operator()( Tuple const& tup ) + { + detail::apply( fn_, tup ); + } + +private: + std::function fn_; +}; // FuncPack + +/* \brief Multiple packed functions + * + * A vector of N packed functions + * + * Example: + * using Fn_A = Func; + * using Fn_B = Func; + * + * FuncPackN functions( + * Fn_A( []( int a, std::string b ){ std::cout << a << ' ' << b << std::endl; } ), + * Fn_B( []( double a, int b ){ std::cout << a << ' ' << b << std::endl; } ) + * ); + * + * functions.apply<0>( std::tuple( 10, "foo" ) ); + * functions.apply<0>( std::tuple( 12, "bar" ) ); + * functions.apply<1>( std::tuple( 3.41, 0 ) ); + * functions.apply<1>( std::tuple( 2.58, 1 ) ); + */ +template +class FuncPackN +{ +public: + explicit FuncPackN( std::tuple fns ) + : fns_( fns ) + {} + + explicit FuncPackN( Fns... fns ) + : fns_( std::make_tuple( fns... ) ) + {} + + template + void apply( typename std::tuple_element>::type::Tuple const& tup ) + { + std::get( fns_ )( tup ); + } + +private: + std::tuple fns_; +}; // FuncPackN + +template +struct call_in_topological_order; + +template +struct call_in_topological_order, ParamPackMapN> +{ +public: + using dependency_type = std::pair; + +public: + explicit call_in_topological_order( FuncPackN fns ) + : fns_( fns ) + {} + + void declare_known( const std::string& name ) + { + known_.emplace( name ); + } + + template + void call_deferred( const std::vector& inputs, + const std::vector& outputs, + const typename std::tuple_element>::type::ValueType& tup ) + { + /* do we have all inputs */ + std::unordered_set unknown; + for ( const auto& input : inputs ) + { + if ( known_.find( input ) != std::end( known_ ) ) + continue; + + const auto it = waits_for_.find( input ); + if ( it == std::end( waits_for_ ) || !it->second.empty() ) + { + unknown.insert( input ); + } + } + + /* store the parameters */ + for ( const auto& output : outputs ) + { + param_maps_.template get()[output] = ParamPack( tup ); + } + + if ( !unknown.empty() ) + { + /* defer computation */ + for ( const auto& input : unknown ) + { + for ( const auto& output : outputs ) + { + triggers_[input].insert( output ); + waits_for_[output].insert( input ); + } + } + return; + } + + /* trigger dependency computation */ + for ( const auto& output : outputs ) + { + compute_dependencies( output ); + } + } + + template + void compute_dependencies( const std::string& output ) + { + /* init empty, makes sure nothing is waiting for this output */ + waits_for_[output]; + + std::stack computed; + computed.push( output ); + + while ( !computed.empty() ) + { + auto const next = computed.top(); + computed.pop(); + + // C++17: std::apply( f, _stored_params[next] ); + // detail::apply( f_, stored_params_[next] ); + + fns_.template apply( param_maps_.template get()[next].get() ); + + /* activate all the triggers */ + for ( const auto& other : triggers_[next] ) + { + waits_for_[other].erase( next ); + if ( waits_for_[other].empty() ) + { + computed.push( other ); + } + } + triggers_[next].clear(); + } + } + + std::vector unresolved_dependencies() + { + std::vector deps; + for ( const auto& item : waits_for_ ) + { + auto const& key = item.first; + auto const& wait_list = item.second; + + if ( wait_list.empty() ) + continue; + + /* collect all keys that are still waiting for an item */ + for ( const auto& entry : wait_list ) + { + deps.emplace_back( key, entry ); + } + } + return deps; + } + +private: + FuncPackN fns_; + ParamPackMapN param_maps_; + + std::unordered_set known_; + std::unordered_map> waits_for_; + std::unordered_map> triggers_; +}; // call_in_topological_order + +} // namespace detail + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/detail/utils.hpp b/third-party/mockturtle/lib/lorina/lorina/detail/utils.hpp new file mode 100644 index 00000000000..9e3141c04c3 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/detail/utils.hpp @@ -0,0 +1,240 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! \cond PRIVATE */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "call_in_topological_order.hpp" + +namespace lorina +{ + +namespace detail +{ + +/* https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf */ +inline std::istream& getline( std::istream& is, std::string& t ) +{ + t.clear(); + + /* The characters in the stream are read one-by-one using a std::streambuf. + * That is faster than reading them one-by-one using the std::istream. + * Code that uses streambuf this way must be guarded by a sentry object. + * The sentry object performs various tasks, + * such as thread synchronization and updating the stream state. + */ + + std::istream::sentry se( is, true ); + std::streambuf* sb = is.rdbuf(); + + for ( ;; ) + { + int const c = sb->sbumpc(); + switch ( c ) + { + /* deal with file endings */ + case '\n': + return is; + case '\r': + if ( sb->sgetc() == '\n' ) + { + sb->sbumpc(); + } + return is; + + /* handle the case when the last line has no line ending */ + case std::streambuf::traits_type::eof(): + if ( t.empty() ) + { + is.setstate( std::ios::eofbit ); + } + return is; + + default: + t += (char)c; + } + } +} + +template +inline std::string join( const T& t, const std::string& sep ) +{ + return std::accumulate( + std::next( t.begin() ), t.end(), std::string( t[0] ), + [&]( const std::string& s, const typename T::value_type& v ) { return s + sep + std::string( v ); } ); +} + +/* string utils are from https://stackoverflow.com/a/217605 */ + +inline void ltrim( std::string& s ) +{ + s.erase( s.begin(), std::find_if( s.begin(), s.end(), []( int ch ) { + return !std::isspace( ch ); + } ) ); +} + +inline void rtrim( std::string& s ) +{ + s.erase( std::find_if( s.rbegin(), s.rend(), []( int ch ) { + return !std::isspace( ch ); + } ) + .base(), + s.end() ); +} + +inline void trim( std::string& s ) +{ + ltrim( s ); + rtrim( s ); +} + +inline std::string trim_copy( std::string s ) +{ + trim( s ); + return s; +} + +inline void foreach_line_in_file_escape( std::istream& in, const std::function& f ) +{ + std::string line, line2; + + while ( !getline( in, line ).eof() ) + { + trim( line ); + + while ( line.back() == '\\' ) + { + line.pop_back(); + trim( line ); + + /* check if failbit has been set */ + if ( !getline( in, line2 ) ) + { + assert( false ); + std::abort(); + } + line += line2; + } + + if ( !f( line ) ) + { + break; + } + } +} + +// https://stackoverflow.com/a/14266139 +inline std::vector split( const std::string& str, const std::string& sep ) +{ + std::vector result; + + size_t last = 0; + size_t next = 0; + std::string substring; + while ( ( next = str.find( sep, last ) ) != std::string::npos ) + { + substring = str.substr( last, next - last ); + if ( substring.length() > 0 ) + { + std::string sub = str.substr( last, next - last ); + sub.erase( std::remove( sub.begin(), sub.end(), ' ' ), sub.end() ); + result.push_back( sub ); + } + last = next + 1; + } + + substring = str.substr( last ); + substring.erase( std::remove( substring.begin(), substring.end(), ' ' ), substring.end() ); + result.push_back( substring ); + + return result; +} + +#ifndef _WIN32 +inline std::string word_exp_filename( const std::string& filename ) +{ + std::string result; + + wordexp_t p; + wordexp( filename.c_str(), &p, 0 ); + + for ( auto i = 0u; i < p.we_wordc; ++i ) + { + if ( !result.empty() ) + { + result += " "; + } + result += std::string( p.we_wordv[i] ); + } + + wordfree( &p ); + + return result; +} +#else +inline const std::string& word_exp_filename( const std::string& filename ) +{ + return filename; +} +#endif + +#ifndef _WIN32 +inline std::string basename( const std::string& filepath ) +{ + return std::string( ::basename( const_cast( filepath.c_str() ) ) ); +} +#endif + +inline bool starts_with( std::string const& s, std::string const& match ) +{ + return ( s.substr( 0, match.size() ) == match ); +} + +} // namespace detail +} // namespace lorina + +/*! \endcond */ diff --git a/third-party/mockturtle/lib/lorina/lorina/diagnostics.hpp b/third-party/mockturtle/lib/lorina/lorina/diagnostics.hpp new file mode 100644 index 00000000000..c86a7b768bc --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/diagnostics.hpp @@ -0,0 +1,218 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file diagnostics.hpp + \brief Implements diagnostics. + + \author Heinz Riener +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace lorina +{ + +class diagnostic_builder; +class diagnostic_consumer; +class diagnostic_engine; + +enum class diagnostic_level +{ + ignore = 1, + note = 2, + remark = 3, + warning = 4, + error = 5, + fatal = 6, +}; + +#include "diagnostics.inc" + +/*! \brief A diagnostic engine. */ +class diagnostic_engine +{ +public: + using desc_type = std::pair; + +public: + /*! \brief Constructs a diagnostic engine. + * + * \param client Diagnostic client + */ + explicit diagnostic_engine( diagnostic_consumer *client ); + + /*! \brief Creates a diagnostic builder. + * + * \param id ID of diagnostic + */ + virtual inline diagnostic_builder report( diag_id id ); + + /*! \brief Emits a diagnostic message. + * + * \param id ID of diagnostic + * \param args Arguments + */ + virtual inline void emit( diag_id id, std::vector const& args = {} ) const; + + /*! \brief Return the number of emitted diagnostics. */ + uint64_t get_num_diagnostics() const; + +private: + /*! \brief Emit diagnostics with static ID. + * + * \param id ID of diagnostic + * \param args Arguments + */ + void emit_static_diagnostic( diag_id id, std::vector const& args ) const; + +protected: + diagnostic_consumer *client_ = nullptr; /*!< Diagnostic client. */ + mutable uint64_t num_diagnostics{0}; +}; /* diagnostic_engine */ + +/*! \brief A builder for diagnostics. + * + * An object that encapsulates a diagnostic. The diagnostic may take + * additional parameters and is finally issued at the end of its + * life time. + */ +class diagnostic_builder +{ +public: + /*! \brief Constructs a diagnostic builder. + * + * \param engine Diagnostic engine + * \param id ID of diagnostic + */ + inline explicit diagnostic_builder( diagnostic_engine& engine, diag_id id ); + + /*! \brief Destructs the diagnostic builder and issues the diagnostic. */ + inline ~diagnostic_builder(); + + /*! \brief Add argument. + * + * \param s String argument + */ + inline diagnostic_builder& add_argument( std::string const& s ); + +private: + diagnostic_engine& engine_; /*!< diagnostic engine */ + diag_id id_; /*!< id of diagnostic */ + std::vector args_; /*!< arguments for diagnostic */ +}; /* diagnostic_builder */ + +/*! \brief A consumer for diagnostics. */ +class diagnostic_consumer +{ +public: + diagnostic_consumer() = default; + virtual ~diagnostic_consumer() = default; + + /*! \brief Handle diagnostic. + * + * \param level Severity level + * \param message Diagnostic message + */ + virtual void handle_diagnostic( diagnostic_level level, std::string const& message ) const + { + (void)level; + (void)message; + } +}; + +inline diagnostic_builder::diagnostic_builder( diagnostic_engine& engine, diag_id id ) + : engine_( engine ), id_( id ) +{ +} + +inline diagnostic_builder::~diagnostic_builder() +{ + engine_.emit( id_, args_ ); +} + +inline diagnostic_builder& diagnostic_builder::add_argument( std::string const& s ) +{ + args_.emplace_back( s ); + return *this; +} + +inline diagnostic_engine::diagnostic_engine( diagnostic_consumer *client ) + : client_( client ) +{ +} + +inline uint64_t diagnostic_engine::get_num_diagnostics() const +{ + return num_diagnostics; +} + +inline diagnostic_builder diagnostic_engine::report( diag_id id ) +{ + return diagnostic_builder( *this, id ); +} + +inline void diagnostic_engine::emit_static_diagnostic( diag_id id, std::vector const& args ) const +{ + assert( id < diag_id::NUM_STATIC_ERROR_IDS ); + diagnostic_level const level = diag_info[uint32_t( id )].first; + std::string const message = diag_info[uint32_t( id )].second; + std::string_view const message_view = message; + switch ( args.size() ) + { + case 1: + client_->handle_diagnostic( level, fmt::vformat( message_view, fmt::make_format_args(args[0]) ) ); + break; + case 2: + client_->handle_diagnostic( level, fmt::vformat( message_view, fmt::make_format_args(args[0], args[1]) ) ); + break; + case 3: + client_->handle_diagnostic( level, fmt::vformat( message_view, fmt::make_format_args(args[0], args[1], args[2]) ) ); + break; + default: + case 0: + assert( args.size() == 0 ); + client_->handle_diagnostic( level, message ); + } +} + +inline void diagnostic_engine::emit( diag_id id, std::vector const& args ) const +{ + assert( client_ != nullptr ); + ++num_diagnostics; + if ( id < diag_id::NUM_STATIC_ERROR_IDS ) + { + emit_static_diagnostic( id, args ); + } +} + +} // namespace lorina diff --git a/third-party/mockturtle/lib/lorina/lorina/diagnostics.inc b/third-party/mockturtle/lib/lorina/lorina/diagnostics.inc new file mode 100644 index 00000000000..0c96e2d5967 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/diagnostics.inc @@ -0,0 +1,91 @@ +/*! pre-defined static diagnostic IDs */ +enum class diag_id +{ + /* general */ + ERR_FILE_OPEN, + ERR_PARSE_LINE, + WRN_UNRESOLVED_DEPENDENCY, + ERR_UNSUPPORTED_KEYWORD, + + /* AIGER */ + ERR_AIGER_HEADER, + ERR_AIGER_LATCH_DECLARATION, + ERR_AIGER_AND_DECLARATION, + + /* BLIF */ + ERR_BLIF_LATCH_FORMAT, + + /* VERILOG */ + ERR_VERILOG_MODULE_HEADER, + ERR_VERILOG_INPUT_DECLARATION, + ERR_VERILOG_OUTPUT_DECLARATION, + ERR_VERILOG_WIRE_DECLARATION, + ERR_VERILOG_ASSIGNMENT, + ERR_VERILOG_MODULE_INSTANTIATION_STATEMENT, + ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_MODULE, + ERR_VERILOG_MODULE_INSTANTIATION_UNDECLARED_PIN, + ERR_VERILOG_ASSIGNMENT_RHS, + + /* DIMACS */ + ERR_DIMACS_MISSING_SPEC, + + /* GENLIB */ + ERR_GENLIB_UNEXPECTED_STRUCTURE, + ERR_GENLIB_GATE, + ERR_GENLIB_EXPRESSION, + ERR_GENLIB_PIN, + ERR_GENLIB_PIN_PHASE, + ERR_GENLIB_FAILED, + + /* SUPER */ + ERR_SUPER_INFO, + ERR_SUPER_UNEXPECTED_STRUCTURE, + ERR_SUPER_GATE, + + /* sentinel element to mark the end */ + NUM_STATIC_ERROR_IDS, +}; + + +/*! pre-defined static diagnostics */ +static std::pair const diag_info[] = { + { diagnostic_level::fatal, "could not open file `{}`" }, + { diagnostic_level::fatal, "could not parse line `{}`" }, + { diagnostic_level::warning, "unresolved dependencies: `{}` requires `{}`" }, + { diagnostic_level::fatal, "unsupported keyword `{}`" }, + + /* AIGER */ + { diagnostic_level::fatal, "could not parse AIGER header `{}`" }, + { diagnostic_level::fatal, "could not parse declaration of LATCH `{}`" }, + { diagnostic_level::fatal, "could not parse declaration of AND gate `{}`" }, + + /* BLIF */ + { diagnostic_level::fatal, "latch format not supported `{}`" }, + + /* VERILOG */ + { diagnostic_level::fatal, "cannot parse module header" }, + { diagnostic_level::fatal, "cannot parse input declaration" }, + { diagnostic_level::fatal, "cannot parse output declaration" }, + { diagnostic_level::fatal, "cannot parse wire declaration" }, + { diagnostic_level::fatal, "cannot parse assign statement" }, + { diagnostic_level::fatal, "cannot parse module instantiation statement" }, + { diagnostic_level::fatal, "cannot instantiate module: module `{}` has not been declared (ensure that the module has been parsed before)" }, + { diagnostic_level::fatal, "cannot instantiate module: pin `{}` has not been declared in module `{}`" }, + { diagnostic_level::fatal, "cannot parse expression on right-hand side of assign `{}`" }, + + /* DIMACS */ + { diagnostic_level::fatal, "missing problem specification line" }, + + /* GENLIB */ + { diagnostic_level::fatal, "line `{}` has unexpected structure (expected `GATE ;`)`" }, + { diagnostic_level::fatal, "line `{}` does not start with keyword `GATE`" }, + { diagnostic_level::fatal, "expression `{}` is not immediately terminated with `;`" }, + { diagnostic_level::fatal, "unexpected `{}` token (expected `PIN`)" }, + { diagnostic_level::fatal, "unknown PIN phase type `{}` (expected `INV`, `NONINV`, or `UNKNOWN`)" }, + { diagnostic_level::fatal, "parsing failed at token `{}`" }, + + /* SUPER */ + { diagnostic_level::fatal, "line `{}` has unexpected structure (expected only one data)" }, + { diagnostic_level::fatal, "line `{}` has a wrong structure (too few fields or exceeding the limit)" }, + { diagnostic_level::fatal, "gate `{}` has a wrong number of fanin (zero or exceeding the limit)" }, +}; diff --git a/third-party/mockturtle/lib/lorina/lorina/genlib.hpp b/third-party/mockturtle/lib/lorina/lorina/genlib.hpp new file mode 100644 index 00000000000..32583baf98a --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/genlib.hpp @@ -0,0 +1,383 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file genlib.hpp + \brief Implements GENLIB parser + + \author Heinz Riener + \author Shubham Rai + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "common.hpp" +#include "detail/utils.hpp" +#include "diagnostics.hpp" +#include +#include +#include +#include +#include + +namespace lorina +{ + +enum class phase_type : uint8_t +{ + INV = 0, + NONINV = 1, + UNKNOWN = 2, +}; + +// PIN +struct pin_spec +{ + std::string name; + phase_type phase; + double input_load; + double max_load; + double rise_block_delay; + double rise_fanout_delay; + double fall_block_delay; + double fall_fanout_delay; +}; /* pin_spec */ + +/*! \brief A reader visitor for a GENLIB format. + * + * Callbacks for the GENLIB format. + */ +class genlib_reader +{ +public: + virtual void on_gate( std::string const& name, std::string const& expression, double area, std::vector const& pins, std::string const& output_pin ) const + { + (void)name; + (void)expression; + (void)area; + (void)pins; + (void)output_pin; + } +}; /* genlib_reader */ + +/*! \brief Parse for the GENLIB format. + * + */ +class genlib_parser +{ +public: + explicit genlib_parser( std::istream& in, genlib_reader const& reader, diagnostic_engine* diag ) + : in( in ) + , reader( reader ) + , diag( diag ) + {} + +public: + bool run() + { + std::string line; + std::string entry; + bool new_line; + + while ( true ) + { + new_line = std::getline( in, line ) ? true : false; + + if ( new_line == false ) + { + break; + } + + /* remove whitespaces */ + detail::trim( line ); + + /* skip comments and empty lines */ + if ( line[0] != '#' && !line.empty() ) + { + entry = line + " "; + break; + } + } + + while ( new_line ) + { + new_line = std::getline( in, line ) ? true : false; + + if ( new_line == false ) + { + if ( !parse_gate_definition( entry ) ) + { + return false; + } + break; + } + + /* remove whitespaces */ + detail::trim( line ); + + /* skip comments and empty lines */ + if ( line[0] == '#' || line.empty() ) + { + continue; + } + + if ( line.find( "GATE" ) != std::string::npos ) + { + if ( !parse_gate_definition( entry ) ) + { + return false; + } + entry = line + " "; + } + else + { + entry.append( line + " " ); + } + } + + return true; + } + +private: + bool parse_gate_definition( std::string const& line ) + { + std::stringstream ss( line ); + std::string const deliminators{" \t\r\n"}; + + std::string token; + bool in_equation = false; + + std::vector tokens; + while ( std::getline( ss, token ) ) + { + std::size_t prev = 0, pos, eq_pos; + while ( ( pos = line.find_first_of( deliminators, prev ) ) != std::string::npos ) + { + /* equation */ + if ( tokens.size() == 3 && !in_equation ) + { + /* equation is not finished */ + if ( token.substr( prev, pos - prev ).find_first_of( ";" ) == std::string::npos ) + { + in_equation = true; + eq_pos = prev; + prev = pos + 1; + continue; + } + } + if ( in_equation ) + { + if ( token.substr( prev, pos - prev ).find_first_of( ";" ) != std::string::npos ) + { + in_equation = false; + prev = eq_pos; + } + else + { + prev = pos + 1; + continue; + } + } + if ( pos > prev ) + { + tokens.emplace_back( token.substr( prev, pos - prev ) ); + } + prev = pos + 1; + } + + if ( prev < line.length() ) + { + tokens.emplace_back( token.substr( prev, std::string::npos ) ); + } + } + + if ( tokens.size() < 4u ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_UNEXPECTED_STRUCTURE ).add_argument( line ); + } + return false; + } + + if ( tokens[0] != "GATE" ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_GATE ).add_argument( line ); + } + return false; + } + auto const beg = tokens[3].find_first_of( "=" ); + auto const end = tokens[3].find_first_of( ";" ); + if ( beg == std::string::npos || end == std::string::npos ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_EXPRESSION ).add_argument( tokens[3] ); + } + return false; + } + + std::string const& name = tokens[1]; + std::string const& expression = tokens[3].substr( beg + 1, end - beg - 1 ); + std::string const& output_name = detail::trim_copy( tokens[3].substr( 0, beg ) ); + double const area = std::stod( tokens[2] ); + + std::vector pins; + + bool generic_pin{false}; + uint64_t i{4}; + for ( ; i+8 < tokens.size(); i += 9 ) + { + /* check PIN specification */ + if ( tokens[i] != "PIN" ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_PIN ).add_argument( tokens[i] ); + } + return false; + } + + std::string const& name = tokens[i+1]; + if ( tokens[i+1] == "*" ) + { + generic_pin = true; + } + phase_type phase{phase_type::UNKNOWN}; + if ( tokens[i+2] == "INV" ) + { + phase = phase_type::INV; + } + else if ( tokens[i+2] == "NONINV" ) + { + phase = phase_type::NONINV; + } + else + { + if ( tokens[i+2] != "UNKNOWN" ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_PIN_PHASE ).add_argument( tokens[i+1] ); + } + } + } + + double const input_load = std::stod( tokens[i+3] ); + double const max_load = std::stod( tokens[i+4] ); + double const rise_block_delay = std::stod( tokens[i+5] ); + double const rise_fanout_delay = std::stod( tokens[i+6] ); + double const fall_block_delay = std::stod( tokens[i+7] ); + double const fall_fanout_delay = std::stod( tokens[i+8] ); + + pins.emplace_back( pin_spec{name,phase,input_load,max_load,rise_block_delay,rise_fanout_delay,fall_block_delay,fall_fanout_delay} ); + } + + if ( pins.size() > 1 && generic_pin ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_PIN ).add_argument( line ); + } + return false; + } + + if ( i != tokens.size() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_GENLIB_FAILED ).add_argument( tokens[i] ); + } + return false; + } + + reader.on_gate( name, expression, area, pins, output_name ); + return true; + } + +protected: + std::istream& in; + genlib_reader const& reader; + diagnostic_engine* diag; +}; /* genlib_parser */ + + +/*! \brief Reader function for the GENLIB format. + * + * Reads GENLIB format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader GENLIB reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_genlib( std::istream& in, const genlib_reader& reader, diagnostic_engine* diag = nullptr ) +{ + genlib_parser parser( in, reader, diag ); + auto result = parser.run(); + if ( !result ) + { + return return_code::parse_error; + } + else + { + return return_code::success; + } +} + +/*! \brief Reader function for the GENLIB format. + * + * Reads GENLIB format from a file and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader GENLIB reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_genlib( const std::string& filename, const genlib_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_genlib( in, reader, diag ); + in.close(); + return ret; + } +} + +} /* lorina */ diff --git a/third-party/mockturtle/lib/lorina/lorina/super.hpp b/third-party/mockturtle/lib/lorina/lorina/super.hpp new file mode 100644 index 00000000000..7cd0f44eda8 --- /dev/null +++ b/third-party/mockturtle/lib/lorina/lorina/super.hpp @@ -0,0 +1,287 @@ +/* lorina: C++ parsing library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file super.hpp + \brief Implements SUPER parser for files generated by ABC + + \author Alessandro Tempia Calvino + \author Shubham Rai +*/ + +#pragma once + +#include "common.hpp" +#include "detail/utils.hpp" +#include "diagnostics.hpp" +#include +#include +#include +#include +#include + +namespace lorina +{ + +/*! \brief A reader visitor for a super format. + * + * Callbacks for the super format. + */ +class super_reader +{ +public: + virtual void on_super_info( std::string const& genlib_name, uint32_t max_num_vars, uint32_t max_supergates, uint32_t num_lines ) const + { + (void) genlib_name; + (void) max_num_vars; + (void) max_supergates; + (void) num_lines; + } + + virtual void on_supergate( std::string const& name, bool const& is_super, std::vector const& fanins_id ) const + { + (void)name; + (void)is_super; + (void)fanins_id; + } +}; /* super_reader */ + +/*! \brief Parse for the SUPER format. + * + */ +class super_parser +{ +public: + explicit super_parser( std::istream& in, super_reader const& reader, diagnostic_engine* diag ) + : in( in ) + , reader( reader ) + , diag( diag ) + { + } + +public: + bool run() + { + std::string line; + uint32_t info_lines = 0u; + uint32_t max_num_vars = 0u; + + std::vector info_vec; + + while ( std::getline( in, line ) ) + { + /* remove whitespaces */ + detail::trim( line ); + + /* skip comments and empty lines */ + if ( line[0] == '#' || line[0] == '\0' || line.empty() ) + { + continue; + } + + if ( info_lines < 4 ) + { + if ( !parse_file_info( line, info_vec ) ) + { + return false; + } + if ( info_lines == 1 ) + { + max_num_vars = std::stod( info_vec[1] ); + } + ++info_lines; + } + else + { + if ( !parse_gate_definition( line, max_num_vars ) ) + { + return false; + } + } + } + + return true; + } + +private: + bool parse_file_info( std::string const& line, std::vector& info_vec ) + { + std::stringstream ss( line ); + std::string token; + + std::vector tokens; + + while ( std::getline( ss, token ) ) + { + tokens.emplace_back( token ); + info_vec.emplace_back( token ); + } + + if ( line.find_first_of( " " ) != std::string::npos ) + { + if ( diag ) + { + diag->report( diag_id::ERR_SUPER_INFO ).add_argument( line ); + } + return false; + } + + if ( info_vec.size() == 4 ) + { + reader.on_super_info( info_vec[0], std::stoi( info_vec[1] ), std::stoi( info_vec[2] ), std::stoi( info_vec[3] ) ); + } + + return true; + } + + bool parse_gate_definition( std::string const& line, uint32_t const& max_num_vars ) + { + std::stringstream ss( line ); + std::string const deliminators{ " \t\r\n" }; + + std::string token; + std::vector tokens; + + std::string name; + std::vector fanins_id; + + while ( std::getline( ss, token ) ) + { + std::size_t prev = 0, pos; + while ( ( pos = line.find_first_of( deliminators, prev ) ) != std::string::npos ) + { + if ( pos > prev ) + { + tokens.emplace_back( token.substr( prev, pos - prev ) ); + } + prev = pos + 1; + } + + if ( prev < line.length() ) + { + tokens.emplace_back( token.substr( prev, std::string::npos ) ); + } + } + + if ( tokens.size() < 2 || tokens.size() > max_num_vars + 2 ) + { + if ( diag ) + { + diag->report( diag_id::ERR_SUPER_UNEXPECTED_STRUCTURE ).add_argument( line ); + } + return false; + } + + bool is_super = false; + uint64_t i{2}; + if ( tokens[0] == "*" ) + { + is_super = true; + name = tokens[1]; + } + else + { + name = tokens[0]; + i = 1u; + } + + for ( auto j = i; j < tokens.size(); ++j ) + { + fanins_id.emplace_back( std::stod( tokens[j] ) ); + } + + if ( fanins_id.size() == 0 ) + { + if ( diag ) + { + diag->report( diag_id::ERR_SUPER_GATE ).add_argument( line ); + } + return false; + } + + reader.on_supergate( name, is_super, fanins_id ); + return true; + } + +protected: + std::istream& in; + super_reader const& reader; + diagnostic_engine* diag; +}; /* super_parser */ + +/*! \brief Reader function for the SUPER format. + * + * Reads SUPER format from a stream and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param in Input stream + * \param reader SUPER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_super( std::istream& in, const super_reader& reader, diagnostic_engine* diag = nullptr ) +{ + super_parser parser( in, reader, diag ); + auto result = parser.run(); + if ( !result ) + { + return return_code::parse_error; + } + else + { + return return_code::success; + } +} + +/*! \brief Reader function for the SUPER format. + * + * Reads SUPER format from a .super file generated by ABC and invokes a callback + * method for each parsed primitive and each detected parse error. + * + * \param filename Name of the file + * \param reader SUPER reader with callback methods invoked for parsed primitives + * \param diag An optional diagnostic engine with callback methods for parse errors + * \return Success if parsing has been successful, or parse error if parsing has failed + */ +[[nodiscard]] inline return_code read_super( const std::string& filename, const super_reader& reader, diagnostic_engine* diag = nullptr ) +{ + std::ifstream in( detail::word_exp_filename( filename ), std::ifstream::in ); + if ( !in.is_open() ) + { + if ( diag ) + { + diag->report( diag_id::ERR_FILE_OPEN ).add_argument( filename ); + } + return return_code::parse_error; + } + else + { + auto const ret = read_super( in, reader, diag ); + in.close(); + return ret; + } +} + +} // namespace lorina