add xmg & xag optimization

noah v3.0
panhongyang 2023-11-16 19:23:53 +08:00
parent 7d336f3a8a
commit 033306cfe8
15 changed files with 4811 additions and 21 deletions

View File

@ -15,6 +15,8 @@
#include <algorithm>
#include <mockturtle/algorithms/aig_balancing.hpp>
#include <mockturtle/algorithms/balancing.hpp>
#include <mockturtle/algorithms/balancing/sop_balancing.hpp>
#include <mockturtle/networks/aig.hpp>
#include <mockturtle/views/depth_view.hpp>
#include <vector>
@ -22,6 +24,7 @@
#include "../core/misc.hpp"
using namespace std;
using namespace mockturtle;
namespace alice {
@ -30,6 +33,7 @@ class balance_command : public command {
explicit balance_command(const environment::ptr& env)
: command(env,
"transforms the current network into a well-balanced AIG") {
add_flag("--xmg, -x", "Balance for XMG");
add_flag("--strash, -s", "Balance AND finding structural hashing");
add_flag("--verbose, -v", "print the information");
}
@ -38,28 +42,40 @@ class balance_command : public command {
void execute() {
clock_t begin, end;
double totalTime = 0.0;
if (is_set("xmg")) {
xmg_network xmg = store<xmg_network>().current();
xmg = balancing(
xmg,
{sop_rebalancing<xmg_network>{}}); // TODO: we need maj-xor balancing
xmg = cleanup_dangling(xmg);
auto xmg_copy = cleanup_dangling(xmg);
phyLS::print_stats(xmg_copy);
if (store<aig_network>().size() == 0u)
std::cerr << "Error: Empty AIG network\n";
else {
auto aig = store<aig_network>().current();
if (is_set("strash")) {
begin = clock();
aig_balancing_params ps;
ps.minimize_levels = false;
aig_balance(aig, ps);
end = clock();
totalTime = (double)(end - begin) / CLOCKS_PER_SEC;
} else {
begin = clock();
aig_balance(aig);
end = clock();
totalTime = (double)(end - begin) / CLOCKS_PER_SEC;
store<xmg_network>().extend();
store<xmg_network>().current() = xmg_copy;
} else {
if (store<aig_network>().size() == 0u)
std::cerr << "Error: Empty AIG network\n";
else {
auto aig = store<aig_network>().current();
if (is_set("strash")) {
begin = clock();
aig_balancing_params ps;
ps.minimize_levels = false;
aig_balance(aig, ps);
end = clock();
totalTime = (double)(end - begin) / CLOCKS_PER_SEC;
} else {
begin = clock();
aig_balance(aig);
end = clock();
totalTime = (double)(end - begin) / CLOCKS_PER_SEC;
}
phyLS::print_stats(aig);
store<aig_network>().extend();
store<aig_network>().current() = aig;
}
phyLS::print_stats(aig);
store<aig_network>().extend();
store<aig_network>().current() = aig;
}
cout.setf(ios::fixed);

139
src/commands/xag/rm.hpp Normal file
View File

@ -0,0 +1,139 @@
/* phyLS: powerful heightened yielded Logic Synthesis
* Copyright (C) 2022-2023 */
/**
* @file rm_multi.hpp
*
* @brief RM logic optimization
*
* @author Homyoung
* @since 2023/11/16
*/
#ifndef RM_MP_HPP
#define RM_MP_HPP
#include <time.h>
#include <mockturtle/mockturtle.hpp>
#include "../../core/rm.hpp"
namespace alice {
class xagopt_command : public command {
public:
explicit xagopt_command(const environment::ptr& env)
: command(env, "Performs two-level RM logic optimization") {
add_option("strategy, -s", strategy, "cut = 0, mffc = 1");
add_flag("--minimum_and_gates, -m",
"minimum multiplicative complexity in XAG");
add_flag("--xag, -g", "RM logic optimization for xag network");
add_flag("--xmg, -x", "RM logic optimization for xmg network");
add_flag("--cec,-c", "apply equivalence checking in rewriting");
}
rules validity_rules() const {
if (is_set("xmg")) {
return {has_store_element<xmg_network>(env),
{[this]() { return (strategy <= 1 && strategy >= 0); },
"strategy must in [0,1] "}};
}
return {has_store_element<xag_network>(env),
{[this]() { return (strategy <= 1 && strategy >= 0); },
"strategy must in [0,1] "}};
}
protected:
void execute() {
if (is_set("xag")) {
// clock_t start, end;
// start = clock();
mockturtle::xag_network xag = store<xag_network>().current();
/* parameters */
ps_ntk.multiplicative_complexity = is_set("minimum_and_gates");
if (strategy == 0)
ps_ntk.strategy = rm_rewriting_params::cut;
else if (strategy == 1)
ps_ntk.strategy = rm_rewriting_params::mffc;
else
assert(false);
xag_network xag1, xag2;
xag1 = xag;
depth_view depth_xag1(xag);
rm_mixed_polarity(depth_xag1, ps_ntk);
xag = cleanup_dangling(xag);
xag2 = xag;
if (is_set("cec")) {
/* equivalence checking */
const auto miter_xag = *miter<xag_network>(xag1, xag2);
equivalence_checking_stats eq_st;
const auto result = equivalence_checking(miter_xag, {}, &eq_st);
assert(*result);
}
// end = clock();
// std::cout << "run time: " << (double)(end - start) / CLOCKS_PER_SEC
// << std::endl;
std::cout << "[rm_mixed_polarity] ";
phyLS::print_stats(xag);
store<xag_network>().extend();
store<xag_network>().current() = xag;
} else if (is_set("xmg")) {
// clock_t start, end;
// start = clock();
mockturtle::xmg_network xmg = store<xmg_network>().current();
/* parameters */
if (strategy == 0)
ps_ntk.strategy = rm_rewriting_params::cut;
else if (strategy == 1)
ps_ntk.strategy = rm_rewriting_params::mffc;
else
assert(false);
xmg_network xmg1, xmg2;
xmg1 = xmg;
depth_view depth_xmg1(xmg);
rm_mixed_polarity(depth_xmg1, ps_ntk);
xmg = cleanup_dangling(xmg);
xmg2 = xmg;
if (is_set("cec")) {
/* equivalence checking */
const auto miter_xmg = *miter<xmg_network>(xmg1, xmg2);
equivalence_checking_stats eq_st;
const auto result = equivalence_checking(miter_xmg, {}, &eq_st);
assert(*result);
}
// end = clock();
// std::cout << "run time: " << (double)(end - start) / CLOCKS_PER_SEC
// << std::endl;
std::cout << "[rm_mixed_polarity] ";
phyLS::print_stats(xmg);
store<xmg_network>().extend();
store<xmg_network>().current() = xmg;
}
}
private:
int strategy = 0;
rm_rewriting_params ps_ntk;
};
ALICE_ADD_COMMAND(xagopt, "Synthesis")
} // namespace alice
#endif

151
src/commands/xag/rm2.hpp Normal file
View File

@ -0,0 +1,151 @@
/* phyLS: powerful heightened yielded Logic Synthesis
* Copyright (C) 2022-2023 */
/**
* @file rm_multi.hpp
*
* @brief Multilevel RM logic optimization
*
* @author Homyoung
* @since 2023/11/16
*/
#ifndef RM_MP2_HPP
#define RM_MP2_HPP
#include <time.h>
#include <mockturtle/mockturtle.hpp>
#include "../../core/rm_multi.hpp"
namespace alice {
class xagopt2_command : public command {
public:
explicit xagopt2_command(const environment::ptr& env)
: command(env, "Performs multi-level RM logic optimization") {
add_option("strategy, -s", strategy, "cut = 0, mffc = 1");
add_flag("--minimum_and_gates, -m",
"minimum multiplicative complexity in XAG");
add_flag("--xag, -g", "RM logic optimization for xag network");
add_flag("--xmg, -x", "RM logic optimization for xmg network");
add_flag("--cec,-c", "apply equivalence checking in rewriting");
}
rules validity_rules() const {
if (is_set("xmg")) {
return {has_store_element<xmg_network>(env),
{[this]() { return (strategy <= 1 && strategy >= 0); },
"strategy must in [0,1] "}};
}
return {has_store_element<xag_network>(env),
{[this]() { return (strategy <= 1 && strategy >= 0); },
"strategy must in [0,1] "}};
}
protected:
void execute() {
if (is_set("xag")) {
// clock_t start, end;
// start = clock();
mockturtle::xag_network xag = store<xag_network>().current();
/* parameters */
ps_ntk.multiplicative_complexity = is_set("minimum_and_gates");
if (strategy == 0)
ps_ntk.strategy = rm_rewriting_params2::cut;
else if (strategy == 1)
ps_ntk.strategy = rm_rewriting_params2::mffc;
else
assert(false);
xag_network xag1, xag2;
xag1 = xag;
// depth_view<mockturtle::xag_network,unit_cost<mockturtle::xag_network>>
// depth_xag1(xag);
cut_rewriting_params ps;
ps.cut_enumeration_ps.cut_size = 6;
ps.cut_enumeration_ps.cut_limit = 12;
ps.min_cand_cut_size = 2;
ps.allow_zero_gain = true;
ps.progress = true;
rm_mixed_polarity2(xag, ps_ntk, ps);
xag = cleanup_dangling(xag);
xag2 = xag;
if (is_set("cec")) {
/* equivalence checking */
const auto miter_xag = *miter<xag_network>(xag1, xag2);
equivalence_checking_stats eq_st;
const auto result = equivalence_checking(miter_xag, {}, &eq_st);
assert(*result);
}
// end = clock();
// std::cout << "run time: " << (double)(end - start) / CLOCKS_PER_SEC
// << std::endl;
std::cout << "[rm_mixed_polarity] ";
phyLS::print_stats(xag);
store<xag_network>().extend();
store<xag_network>().current() = xag;
} else if (is_set("xmg")) {
// clock_t start, end;
// start = clock();
mockturtle::xmg_network xmg = store<xmg_network>().current();
/* parameters */
if (strategy == 0)
ps_ntk.strategy = rm_rewriting_params2::cut;
else if (strategy == 1)
ps_ntk.strategy = rm_rewriting_params2::mffc;
else
assert(false);
xmg_network xmg1, xmg2;
xmg1 = xmg;
cut_rewriting_params ps;
ps.cut_enumeration_ps.cut_size = 6;
ps.cut_enumeration_ps.cut_limit = 12;
ps.min_cand_cut_size = 2;
ps.allow_zero_gain = true;
ps.progress = true;
rm_mixed_polarity2(xmg, ps_ntk, ps);
xmg = cleanup_dangling(xmg);
xmg2 = xmg;
if (is_set("cec")) {
/* equivalence checking */
const auto miter_xmg = *miter<xmg_network>(xmg1, xmg2);
equivalence_checking_stats eq_st;
const auto result = equivalence_checking(miter_xmg, {}, &eq_st);
assert(*result);
}
// end = clock();
// std::cout << "run time: " << (double)(end - start) / CLOCKS_PER_SEC
// << std::endl;
std::cout << "[rm_mixed_polarity] ";
phyLS::print_stats(xmg);
store<xmg_network>().extend();
store<xmg_network>().current() = xmg;
}
}
private:
int strategy = 0;
rm_rewriting_params2 ps_ntk;
};
ALICE_ADD_COMMAND(xagopt2, "Synthesis")
} // namespace alice
#endif

View File

@ -0,0 +1,64 @@
/* phyLS: powerful heightened yielded Logic Synthesis
* Copyright (C) 2023 */
/**
* @file xagrs.hpp
*
* @brief XAG resubsitution
*
* @author Homyoung
* @since 2023/11/16
*/
#ifndef XAGRS_HPP
#define XAGRS_HPP
#include <mockturtle/mockturtle.hpp>
#include <mockturtle/algorithms/resubstitution.hpp>
#include <mockturtle/networks/xag.hpp>
#include "../../core/misc.hpp"
namespace alice
{
class xagrs_command : public command
{
public:
explicit xagrs_command( const environment::ptr& env ) : command( env, "Performs XAG resubsitution" )
{
add_flag( "-v,--verbose", ps.verbose, "show statistics" );
}
rules validity_rules() const
{
return { has_store_element<xag_network>( env ) };
}
protected:
void execute()
{
/* derive some XAG */
xag_network xag = store<xag_network>().current();
default_resubstitution( xag, ps, &st );
xag = cleanup_dangling( xag );
std::cout << "[xagrs] ";
phyLS::print_stats( xag );
store<xag_network>().extend();
store<xag_network>().current() = xag;
}
private:
resubstitution_params ps;
resubstitution_stats st;
};
ALICE_ADD_COMMAND( xagrs, "Synthesis" )
}
#endif

View File

@ -0,0 +1,95 @@
/* phyLS: powerful heightened yielded Logic Synthesis
* Copyright (C) 2023 */
/**
* @file xagrw.hpp
*
* @brief XAG rewriting
*
* @author Homyoung
* @since 2023/11/16
*/
#ifndef XAGRW_HPP
#define XAGRW_HPP
#include <mockturtle/algorithms/equivalence_checking.hpp>
#include <mockturtle/algorithms/xag_optimization.hpp>
#include <mockturtle/mockturtle.hpp>
#include <mockturtle/networks/xag.hpp>
#include <mockturtle/properties/migcost.hpp>
#include "../../core/misc.hpp"
#include "../../core/xag_rewriting.hpp"
// #include <kitty/print.hpp>
namespace alice {
class xagrw_command : public command {
public:
explicit xagrw_command(const environment::ptr& env)
: command(env, "Performs XAG rewriting") {
add_option("strategy, -s", strategy,
"aggressive = 0, selective = 1, dfs = 2");
add_flag("--cec, -c,", "apply equivalence checking in rewriting");
add_flag("-v,--verbose", "show statistics");
}
rules validity_rules() const {
return {has_store_element<xag_network>(env),
{[this]() { return (strategy <= 2 && strategy >= 0); },
"strategy must in [0,2] "}};
}
protected:
void execute() {
xag_network xag = store<xag_network>().current();
unsigned num_inv_ori = num_inverters(xag);
if (strategy == 0) {
ps_xag.strategy = xag_depth_rewriting_params::aggressive;
} else if (strategy == 1) {
ps_xag.strategy = xag_depth_rewriting_params::selective;
} else if (strategy == 2) {
ps_xag.strategy = xag_depth_rewriting_params::dfs;
} else {
assert(false);
}
xag_network xag1, xag2;
xag1 = xag;
depth_view depth_xag1(xag);
xag_depth_rewriting(depth_xag1, ps_xag);
xag = cleanup_dangling(xag);
xag2 = xag;
if (is_set("cec")) {
/* equivalence checking */
const auto miter_xag = *miter<xag_network>(xag1, xag2);
equivalence_checking_stats eq_st;
const auto result = equivalence_checking(miter_xag, {}, &eq_st);
assert(*result);
}
unsigned num_inv_opt = num_inverters(xag);
std::cout << "[xagrw] ";
phyLS::print_stats(xag);
// std::cout << "num_inv_ori:" << num_inv_ori <<std::endl;
// std::cout << "num_inv_opt:" << num_inv_opt << std::endl;
store<xag_network>().extend();
store<xag_network>().current() = xag;
}
private:
xag_depth_rewriting_params ps_xag;
int strategy = 0;
};
ALICE_ADD_COMMAND(xagrw, "Synthesis")
} // namespace alice
#endif

View File

@ -16,7 +16,7 @@
#include <mockturtle/mockturtle.hpp>
#include <mockturtle/utils/stopwatch.hpp>
#include "../core/xmginv.hpp"
#include "../../core/xmginv.hpp"
namespace alice {

View File

@ -0,0 +1,75 @@
/* phyLS: powerful heightened yielded Logic Synthesis
* Copyright (C) 2023 */
/**
* @file xmgrs.hpp
*
* @brief XMG resubsitution
*
* @author Homyoung
* @since 2023/11/16
*/
#ifndef XMGRS_HPP
#define XMGRS_HPP
#include <mockturtle/algorithms/xmg_resub.hpp>
#include <mockturtle/mockturtle.hpp>
#include <mockturtle/networks/xmg.hpp>
#include "../../core/misc.hpp"
namespace alice {
class xmgrs_command : public command {
public:
explicit xmgrs_command(const environment::ptr& env)
: command(env, "Performs XMG resubsitution") {
add_flag("-v,--verbose", ps.verbose, "show statistics");
add_flag("--cec,-c", "apply equivalence checking in resubstitution");
}
rules validity_rules() const { return {has_store_element<xmg_network>(env)}; }
protected:
void execute() {
/* derive some XMG */
xmg_network xmg = store<xmg_network>().current();
xmg_network xmg1, xmg2;
xmg1 = xmg;
xmg2 = xmg;
using view_t = depth_view<fanout_view<xmg_network>>;
fanout_view<xmg_network> fanout_view{xmg1};
view_t resub_view{fanout_view};
xmg_resubstitution(resub_view);
xmg2 = cleanup_dangling(xmg1);
std::cout << "[xmgrs] ";
auto xmg_copy = cleanup_dangling(xmg2);
phyLS::print_stats(xmg_copy);
if (is_set("cec")) {
const auto miter_xmg = *miter<xmg_network>(xmg1, xmg2);
equivalence_checking_stats eq_st;
const auto result = equivalence_checking(miter_xmg, {}, &eq_st);
assert(result);
assert(*result);
std::cout << "Network is equivalent after resub\n";
}
store<xmg_network>().extend();
store<xmg_network>().current() = xmg2;
}
private:
resubstitution_params ps;
resubstitution_stats st;
};
ALICE_ADD_COMMAND(xmgrs, "Synthesis")
} // namespace alice
#endif

145
src/commands/xmg/xmgrw.hpp Normal file
View File

@ -0,0 +1,145 @@
/* phyLS: powerful heightened yielded Logic Synthesis
* Copyright (C) 2023 */
/**
* @file xmgrw.hpp
*
* @brief algebraic XMG rewriting
*
* @author Homyoung
* @since 2023/11/16
*/
#ifndef XMGRW_COMMAND_HPP
#define XMGRW_COMMAND_HPP
#include <mockturtle/algorithms/equivalence_checking.hpp>
#include <mockturtle/algorithms/mig_algebraic_rewriting.hpp>
#include <mockturtle/networks/xmg.hpp>
#include <mockturtle/views/depth_view.hpp>
#include "../../core/misc.hpp"
#include "../../core/xmg_expand.hpp"
#include "../../core/xmg_extract.hpp"
#include "../../core/xmg_rewriting.hpp"
namespace alice {
class xmgrw_command : public command {
public:
explicit xmgrw_command(const environment::ptr& env)
: command(env, "Performs algebraic XMG rewriting") {
add_option("strategy, -s", strategy,
"qca = 0, aggressive = 1, selective = 2, dfs = 3");
add_flag("--area_aware, -a", "do not increase area");
add_flag("--xor3_flattan", "flattan xor3 to 2 xor2s");
add_flag("--only_maj", "apply mig_algebraic_depth_rewriting method");
add_flag("--cec,-c", "apply equivalence checking in rewriting");
add_flag("--expand,-e", "apply xor expand through maj");
add_flag("--hunt_constant_xor,-u", "hunt XOR constant nodes");
}
rules validity_rules() const {
return {has_store_element<xmg_network>(env),
{[this]() { return (strategy <= 3 && strategy >= 0); },
"strategy must in [0,3] "}};
}
protected:
void execute() {
xmg_network xmg = store<xmg_network>().current();
if (is_set("only_maj")) {
/* parameters */
ps_mig.allow_area_increase = !is_set("area_aware");
if (strategy == 0) {
ps_mig.strategy = mig_algebraic_depth_rewriting_params::dfs;
} else if (strategy == 1) {
ps_mig.strategy = mig_algebraic_depth_rewriting_params::aggressive;
} else if (strategy == 2) {
ps_mig.strategy = mig_algebraic_depth_rewriting_params::selective;
} else {
assert(false);
}
depth_view depth_xmg{xmg};
mig_algebraic_depth_rewriting(depth_xmg, ps_mig);
xmg = cleanup_dangling(xmg);
} else if (is_set("hunt_constant_xor")) {
auto xors = phyLS::xmg_extract(xmg);
ps_expand.strategy = xmg_expand_rewriting_params::constants;
ps_expand.xor_index = xors;
depth_view depth_xmg{xmg};
xmg_expand_rewriting(depth_xmg, ps_expand);
xmg = cleanup_dangling(xmg);
} else if (is_set("expand")) {
ps_expand.strategy = xmg_expand_rewriting_params::expand;
depth_view depth_xmg{xmg};
xmg_expand_rewriting(depth_xmg, ps_expand);
xmg = cleanup_dangling(xmg);
} else {
/* parameters */
ps_xmg.allow_area_increase = !is_set("area_aware");
ps_xmg.apply_xor3_to_xor2 = is_set("xor3_flattan");
ps_mig.allow_area_increase = !is_set("area_aware");
if (strategy == 0) {
ps_xmg.strategy = xmg_depth_rewriting_params::qca;
ps_mig.strategy = mig_algebraic_depth_rewriting_params::dfs;
} else if (strategy == 1) {
ps_xmg.strategy = xmg_depth_rewriting_params::aggressive;
ps_mig.strategy = mig_algebraic_depth_rewriting_params::aggressive;
} else if (strategy == 2) {
ps_xmg.strategy = xmg_depth_rewriting_params::selective;
ps_mig.strategy = mig_algebraic_depth_rewriting_params::selective;
} else if (strategy == 3) {
ps_xmg.strategy = xmg_depth_rewriting_params::dfs;
ps_mig.strategy = mig_algebraic_depth_rewriting_params::dfs;
} else {
assert(false);
}
xmg_network xmg1, xmg2;
xmg1 = xmg;
/* mig_algebraic_depth_rewriting is suitable for ntk that has majority
* nodes */
depth_view depth_xmg1{xmg};
mig_algebraic_depth_rewriting(depth_xmg1, ps_mig);
xmg = cleanup_dangling(xmg);
depth_view depth_xmg2{xmg};
xmg_depth_rewriting(depth_xmg2, ps_xmg);
xmg = cleanup_dangling(xmg);
xmg2 = xmg;
if (is_set("cec")) {
/* equivalence checking */
const auto miter_xmg = *miter<xmg_network>(xmg1, xmg2);
equivalence_checking_stats eq_st;
const auto result = equivalence_checking(miter_xmg, {}, &eq_st);
assert(*result);
}
}
std::cout << "[xmgrw] ";
auto xmg_copy = cleanup_dangling(xmg);
phyLS::print_stats(xmg_copy);
store<xmg_network>().extend();
store<xmg_network>().current() = xmg;
}
private:
mig_algebraic_depth_rewriting_params ps_mig;
xmg_depth_rewriting_params ps_xmg;
xmg_expand_rewriting_params ps_expand;
int strategy = 0;
};
ALICE_ADD_COMMAND(xmgrw, "Synthesis")
} // namespace alice
#endif

824
src/core/rm.hpp Normal file
View File

@ -0,0 +1,824 @@
/* phyLS: powerful heightened yielded Logic Synthesis
* Copyright (C) 2022-2023 */
/**
* @file rm_multi.hpp
*
* @brief RM logic optimization
*
* @author Homyoung
* @since 2023/11/16
*/
#pragma once
#include <algorithm>
#include <cmath>
#include <iostream>
#include <mockturtle/mockturtle.hpp>
#include <string>
#include <vector>
using namespace std;
namespace mockturtle {
/*! \brief Parameters for rm_logic_optimization.
*
* The data structure `rm_logic_optimization` holds configurable
* parameters with default arguments for `rm_logic_optimization`.
*/
struct rm_rewriting_params {
/*! \brief Rewriting strategy. */
enum strategy_t {
/*! \brief Cut is used to divide the network. */
cut,
/*! \brief mffc is used to divide the network. */
mffc,
} strategy = cut;
/*! \brief minimum multiplicative complexity in XAG. */
bool multiplicative_complexity{false};
};
namespace detail {
template <class Ntk>
class rm_mixed_polarity_impl {
public:
using node_t = node<Ntk>;
using signal_t = signal<Ntk>;
rm_mixed_polarity_impl(Ntk& ntk, rm_rewriting_params const& ps_ntk)
: ntk(ntk), ps_ntk(ps_ntk) {}
void run() {
switch (ps_ntk.strategy) {
case rm_rewriting_params::cut:
ntk_cut();
break;
case rm_rewriting_params::mffc:
ntk_mffc();
break;
}
}
private:
/**************************************************************************************************************/
/* for cost estimation we use reference counters initialized by the fanout
* size. */
void initialize_refs(Ntk& ntk1) {
ntk1.clear_values();
ntk1.foreach_node(
[&](auto const& n) { ntk1.set_value(n, ntk1.fanout_size(n)); });
}
/**************************************************************************************************************/
/* Count the number of and gates. */
int count_node_num(vector<string>& minterm, int variate_num,
string polarity) {
int count = 0;
if (variate_num > 1) {
for (int i = 0; i < minterm.size(); i++) {
int count1 = 0;
for (int j = 0; j < variate_num; j++) {
if (minterm[i][j] == '1' || polarity[j] == '2') count1++;
}
if (count1 > 0) count = count + count1 - 1;
}
}
return count;
}
/**************************************************************************************************************/
/* Convert ternary to decimal. */
int atoint(string s, int radix) {
int ans = 0;
for (int i = 0; i < s.size(); i++) {
char t = s[i];
if (t >= '0' && t <= '9')
ans = ans * radix + t - '0';
else
ans = ans * radix + t - 'a' + 10;
}
return ans;
}
/**************************************************************************************************************/
/* Convert decimal to ternary. */
string intToA(int n, int radix) {
string ans = "";
do {
int t = n % radix;
if (t >= 0 && t <= 9)
ans += t + '0';
else
ans += t - 10 + 'a';
n /= radix;
} while (n != 0);
reverse(ans.begin(), ans.end());
return ans;
}
/**************************************************************************************************************/
/* create signal by expression. */
signal_t create_ntk_from_str(std::string const& s,
std::vector<node_t> const& leaves) {
std::vector<signal_t> pis;
pis.clear();
for (const auto& l : leaves) {
pis.push_back(ntk.make_signal(l));
}
std::stack<signal_t> inputs;
int flag = 0;
int flag1 = 0;
for (auto i = 0; i < s.size(); i++) {
if (s[i] == '[') {
continue;
} else if (s[i] == '(') {
continue;
} else if (s[i] == '1') {
flag1 = 1;
continue;
} else if (s[i] >= 'a') {
if (flag == 1) {
inputs.push(ntk.create_not(pis[s[i] - 'a']));
flag = 0;
} else
inputs.push(pis[s[i] - 'a']);
} else if (s[i] == ')') {
auto x1 = inputs.top();
inputs.pop();
auto x2 = inputs.top();
inputs.pop();
inputs.push(ntk.create_and(x1, x2));
} else if (s[i] == ']') {
if (flag1 == 1) {
auto x1 = inputs.top();
inputs.pop();
inputs.push(ntk.create_not(x1));
flag1 = 0;
} else {
auto x1 = inputs.top();
inputs.pop();
auto x2 = inputs.top();
inputs.pop();
inputs.push(ntk.create_xor(x1, x2));
}
} else if (s[i] == '!') {
flag = 1;
}
}
return inputs.top();
}
/**************************************************************************************************************/
/* List all possible variable values/List truth table. */
vector<string> list_truth_table(int& variate_num) {
int a = pow(2, variate_num);
vector<int> b;
for (int i = 0; i < a; i++) {
b.push_back(i);
}
vector<string> binary;
for (int i = 0; i < a; i++) {
string binary1;
for (int j = variate_num - 1; j >= 0; j--) {
int d = ((b[i] >> j) & 1);
binary1 += d + 48;
}
binary.push_back(binary1);
}
return binary;
}
/**************************************************************************************************************/
/* List all possible polarity values. */
vector<string> list_all_polarities(int& variate_num) {
int a_1 = pow(2, variate_num);
vector<int> c;
for (int i = 0; i < a_1; i++) {
c.push_back(i);
}
vector<string> polarity1;
for (int i = 0; i < a_1; i++) {
string polarity2;
for (int j = variate_num - 1; j >= 0; j--) {
int d = ((c[i] >> j) & 1);
polarity2 += d + 48;
}
polarity1.push_back(polarity2);
}
/* Store all polarities. */
vector<string> polarity;
polarity.push_back(polarity1[0]);
int a2 = pow(3, variate_num);
string str1 = polarity1[0];
string str2 = polarity1[1];
for (int i = 0; i < a2 - 1; i++) {
int num1 = atoint(str1, 3);
int num2 = atoint(str2, 3);
int sum = num1 + num2;
string str3 = intToA(sum, 3);
str1 = "0";
if (str3.size() < variate_num) {
for (int i = 0; i < variate_num - str3.size() - 1; i++) str1 += '0';
str1 += str3;
} else
str1 = str3;
polarity.push_back(str1);
}
return polarity;
}
/**************************************************************************************************************/
/* Mixed polarity conversion algorithm based on list technique. */
vector<string> polarity_conversion(int& variate_num, vector<string>& minterm,
string& polarity) {
vector<string> RM_product = minterm;
string str;
for (int i = 0; i < variate_num; i++) {
for (int j = 0; j < RM_product.size(); j++) {
if (polarity[i] == '0') {
if (RM_product[j][i] == '0') {
str = RM_product[j];
str[i] = '1';
RM_product.push_back(str);
}
} else if (polarity[i] == '1') {
if (RM_product[j][i] == '1') {
str = RM_product[j];
str[i] = '0';
RM_product.push_back(str);
}
}
}
if (polarity[i] == '0') {
/* Delete the minimum number of even items. */
if (RM_product.size() > 0) {
sort(RM_product.begin(), RM_product.end());
for (int m = 0; m < RM_product.size() - 1; m++) {
if (RM_product[m] == RM_product[m + 1]) {
RM_product[m] = "0";
RM_product[m + 1] = "0";
}
}
}
/* Delete item with '0' in array. */
vector<string> final_term;
final_term.clear();
for (int m = 0; m < RM_product.size(); m++) {
if (RM_product[m] != "0") final_term.push_back(RM_product[m]);
}
RM_product.clear();
RM_product = final_term;
} else if (polarity[i] == '1') {
/* Delete the minimum number of even items. */
if (RM_product.size() > 0) {
sort(RM_product.begin(), RM_product.end());
for (int m = 0; m < RM_product.size() - 1; m++) {
if (RM_product[m] == RM_product[m + 1]) {
RM_product[m] = "0";
RM_product[m + 1] = "0";
}
}
}
/* Delete item with '0' in array. */
vector<string> final_term;
final_term.clear();
for (int m = 0; m < RM_product.size(); m++) {
if (RM_product[m] != "0") {
if (RM_product[m][i] == '0')
RM_product[m][i] = '1';
else if (RM_product[m][i] == '1')
RM_product[m][i] = '0';
final_term.push_back(RM_product[m]);
}
}
RM_product.clear();
RM_product = final_term;
}
}
return RM_product;
}
/**************************************************************************************************************/
/* Search for the optimal polarity and the corresponding product term. */
void search_for_optimal_polarity(vector<string>& minterm,
vector<string>& polarity, int& variate_num,
vector<string>& RM_product,
string& optimal_polarity) {
vector<string> RM_product_initial;
for (int l = 0; l < polarity.size(); l++) {
RM_product_initial =
polarity_conversion(variate_num, minterm, polarity[l]);
int count1, count2;
count1 = count_the_number_of_nodes(variate_num, RM_product_initial,
polarity[l]);
count2 =
count_the_number_of_nodes(variate_num, RM_product, optimal_polarity);
if (count1 < count2) {
optimal_polarity = polarity[l];
RM_product.clear();
RM_product = RM_product_initial;
RM_product_initial.clear();
} else {
RM_product_initial.clear();
}
}
}
/**************************************************************************************************************/
/* Count the number of nodes in the new network. */
int count_the_number_of_nodes(int& variate_num, vector<string>& RM_product,
string& optimal_polarity) {
/* Count the number of nodes. */
int node_num = 0;
if (RM_product.size() > 1) node_num = RM_product.size() - 1;
if (variate_num > 1) {
for (int i = 0; i < RM_product.size(); i++) {
int n = 0;
for (int j = variate_num - 1; j >= 0; j--) {
if (RM_product[i][j] == '1' || optimal_polarity[j] == '2') n++;
}
if (n > 0) node_num = node_num + n - 1;
}
}
return node_num;
}
/**************************************************************************************************************/
/* Create expression. */
string create_expression(vector<string>& RM_product, int& variate_num,
string& optimal_polarity, vector<string>& binary) {
string st = "abcdefghijklmnopqrstuvwxyz";
string expression = "";
if (RM_product.size() > 1)
for (int i = 0; i < RM_product.size() - 1; i++) expression += '[';
for (int i = 0; i < RM_product.size(); i++) {
int k = 0;
int l = 0;
int m = 0;
for (int j = variate_num - 1; j >= 0; j--) {
if (RM_product[i][j] == '1' || optimal_polarity[j] == '2') l++;
}
for (int j = 0; j < l - 1; j++) expression += '(';
for (int j = variate_num - 1; j >= 0; j--) {
if (optimal_polarity[j] == '0' && RM_product[i][j] == '1') {
expression += st[k];
m++;
} else if (optimal_polarity[j] == '1' && RM_product[i][j] == '1') {
expression += '!';
expression += st[k];
m++;
} else if (optimal_polarity[j] == '2' && RM_product[i][j] == '1') {
expression += st[k];
m++;
} else if (optimal_polarity[j] == '2' && RM_product[i][j] == '0') {
expression += '!';
expression += st[k];
m++;
}
if ((m > 1 && RM_product[i][j] == '1') ||
(m > 1 && optimal_polarity[j] == '2'))
expression += ')';
k++;
}
if (RM_product[i] == binary[0]) {
int flag = 0;
for (int m = variate_num - 1; m >= 0; m--) {
if (optimal_polarity[m] == '2') flag = 1;
}
if (flag == 0) expression += '1';
}
if (i > 0 && RM_product.size() > 1) expression += ']';
}
return expression;
}
/**************************************************************************************************************/
/* Replace network. */
void substitute_network_mffc(string& expression, std::vector<node_t>& leaves,
node_t n) {
auto opt = create_ntk_from_str(expression, leaves);
ntk.substitute_node(n, opt);
ntk.set_value(n, 0);
ntk.set_value(ntk.get_node(opt), ntk.fanout_size(ntk.get_node(opt)));
for (auto i = 0u; i < leaves.size(); i++) {
ntk.set_value(leaves[i], ntk.fanout_size(leaves[i]));
}
ntk.update_levels();
}
/**************************************************************************************************************/
/* Replace network. */
void substitute_network_cut(string& expression, std::vector<node_t>& leaves,
node_t n) {
auto opt = create_ntk_from_str(expression, leaves);
ntk.substitute_node(n, opt);
ntk.update_levels();
}
/**************************************************************************************************************/
/* get children of top node, ordered by node level (ascending). */
vector<signal<Ntk>> ordered_children(node<Ntk> const& n) const {
vector<signal<Ntk>> children;
ntk.foreach_fanin(n, [&children](auto const& f) { children.push_back(f); });
std::sort(
children.begin(), children.end(),
[this](auto const& c1, auto const& c2) {
if (ntk.level(ntk.get_node(c1)) == ntk.level(ntk.get_node(c2))) {
return c1.index < c2.index;
} else {
return ntk.level(ntk.get_node(c1)) < ntk.level(ntk.get_node(c2));
}
});
return children;
}
/**************************************************************************************************************/
/* Judge whether the leaf node is reached. */
int is_existence(long int root, vector<signal_t> pis) {
int flag = 0;
for (int i = 0; i < pis.size(); i++) {
if (root == ntk.get_node(pis[i])) {
flag = 1;
break;
}
}
return flag;
}
/**************************************************************************************************************/
/* Hierarchical ergodic statistics AND gates node cost function. */
void LevelOrder(long int root, vector<signal_t> pis,
int& optimization_and_nodes) {
int a = 0;
int b = 0;
if (is_existence(root, pis) == 1) return;
/* Create a queue container. */
queue<long int> deq;
deq.push(root);
while (!deq.empty()) {
long int tr = deq.front();
deq.pop();
vector<signal<Ntk>> ocs;
ocs.clear();
/* get children of top node, ordered by node level (ascending). */
ocs = ordered_children(tr);
if (ocs.size() == 2 && is_existence(ntk.get_node(ocs[0]), pis) != 1) {
deq.push(ntk.get_node(ocs[0]));
if (ntk.is_and(ntk.get_node(ocs[0])) &&
ntk.fanout_size(ntk.get_node(ocs[0])) != 1u && a == 0) {
optimization_and_nodes = optimization_and_nodes - 1;
a = 1;
} else if (ntk.is_and(ntk.get_node(ocs[0])) && a == 1)
optimization_and_nodes = optimization_and_nodes - 1;
}
if (ocs.size() == 2 && is_existence(ntk.get_node(ocs[1]), pis) != 1) {
deq.push(ntk.get_node(ocs[1]));
if (ntk.is_and(ntk.get_node(ocs[1])) &&
ntk.fanout_size(ntk.get_node(ocs[1])) != 1u && b == 0) {
optimization_and_nodes = optimization_and_nodes - 1;
b = 1;
} else if (ntk.is_and(ntk.get_node(ocs[1])) && b == 1)
optimization_and_nodes = optimization_and_nodes - 1;
}
}
}
/**************************************************************************************************************/
/* Cut is used to divide the network. */
void ntk_cut() {
/* for cost estimation we use reference counters initialized by the fanout
* size. */
initialize_refs(ntk);
/* enumerate cuts */
cut_enumeration_params ps;
ps.cut_size = 6;
ps.cut_limit = 8;
ps.minimize_truth_table = true;
/* true enables truth table computation */
auto cuts = cut_enumeration<Ntk, true>(ntk, ps);
/* iterate over all original nodes in the network */
const auto size = ntk.size();
ntk.foreach_node([&](auto n, auto index) {
/* stop once all original nodes were visited */
if (index >= size) return false;
/* do not iterate over constants or PIs */
if (ntk.is_constant(n) || ntk.is_pi(n)) return true;
/* skip cuts with small MFFC */
if (mffc_size(ntk, n) == 1) return true;
/* foreach cut */
for (auto& cut : cuts.cuts(ntk.node_to_index(n))) {
/* skip trivial cuts */
if (cut->size() < 2) continue;
std::vector<node_t> leaves;
for (auto leaf_index : *cut) {
leaves.push_back(ntk.index_to_node(leaf_index));
}
cut_view<Ntk> dcut(ntk, leaves, ntk.make_signal(n));
if (dcut.num_gates() > 14) continue;
/* skip cuts with small MFFC */
int mffc_num_nodes = mffc_size(dcut, n);
if (mffc_num_nodes == 1) continue;
string tt = to_binary(cuts.truth_table(*cut));
string str = "1";
int index1 = tt.find(str);
if (index1 >= tt.length()) continue;
int len = tt.length();
/* Stores the number of input variables. */
int variate_num = log(len) / log(2);
/* List all possible variable values/List truth table. */
vector<string> binary = list_truth_table(variate_num);
/* Stores the output of the truth table. */
vector<char> c_out;
for (int i = len - 1; i >= 0; i--) {
c_out.push_back(tt[i]);
}
vector<char> c_out1;
/* Minimum item for storing binary. */
vector<string> minterm;
for (int i = 0; i < binary.size(); i++) {
if (c_out[i] != '0') {
minterm.push_back(binary[i]);
c_out1.push_back(c_out[i]);
}
}
/* Store all polarities. */
vector<string> polarity = list_all_polarities(variate_num);
/* Count the initial cost. */
/* Storage optimal polarity. */
string optimal_polarity = polarity[0];
/* Mixed polarity conversion algorithm based on list technique. */
vector<string> RM_product =
polarity_conversion(variate_num, minterm, optimal_polarity);
/* Search for the optimal polarity and the corresponding product term.
*/
search_for_optimal_polarity(minterm, polarity, variate_num, RM_product,
optimal_polarity);
/* Count the number of nodes in the new network. */
int node_num_new = count_the_number_of_nodes(variate_num, RM_product,
optimal_polarity);
if (ps_ntk.multiplicative_complexity == true) {
int optimization_nodes = dcut.num_gates() - node_num_new -
(dcut.num_gates() - mffc_num_nodes);
if (optimization_nodes > 0) {
std::vector<signal_t> pis;
pis.clear();
for (const auto& l : leaves) {
pis.push_back(ntk.make_signal(l));
}
int and_num = 0;
/* Count the number of and nodes in the new network. */
int and_num_new =
count_node_num(RM_product, variate_num, optimal_polarity);
dcut.foreach_gate([&](auto const& n1) {
if (ntk.is_and(n1)) and_num++;
});
int optimization_and_nodes = and_num - and_num_new;
/* Hierarchical ergodic statistics AND gates node cost function. */
LevelOrder(n, pis, optimization_and_nodes);
if (optimization_and_nodes >= 0) {
/* Create expression. */
string expression = create_expression(RM_product, variate_num,
optimal_polarity, binary);
/* Replace network. */
substitute_network_cut(expression, leaves, n);
}
}
} else {
int optimization_nodes = dcut.num_gates() - node_num_new -
(dcut.num_gates() - mffc_num_nodes);
if (optimization_nodes > 0) {
/* Create expression. */
string expression = create_expression(RM_product, variate_num,
optimal_polarity, binary);
/* Replace network. */
substitute_network_cut(expression, leaves, n);
}
}
}
return true;
});
}
/**************************************************************************************************************/
/* mffc is used to divide the network. */
void ntk_mffc() {
/* for cost estimation we use reference counters initialized by the fanout
* size. */
ntk.clear_visited();
ntk.clear_values();
ntk.foreach_node(
[&](auto const& n) { ntk.set_value(n, ntk.fanout_size(n)); });
/* iterate over all original nodes in the network */
const auto size = ntk.size();
ntk.foreach_node([&](auto n, auto index) {
/* stop once all original nodes were visited */
if (index >= size) return false;
if (ntk.fanout_size(n) == 0u) {
return true;
}
/* do not iterate over constants or PIs */
if (ntk.is_constant(n) || ntk.is_pi(n)) return true;
/* skip cuts with small MFFC */
if (mffc_size(ntk, n) == 1) return true;
mffc_view mffc{ntk, n};
if (mffc.num_pos() == 0 || mffc.num_pis() > 6) {
return true;
}
std::vector<node_t> leaves(mffc.num_pis());
mffc.foreach_pi([&](auto const& m, auto j) { leaves[j] = m; });
default_simulator<kitty::dynamic_truth_table> sim(mffc.num_pis());
string tt = to_binary(simulate<kitty::dynamic_truth_table>(mffc, sim)[0]);
string str = "1";
int index1 = tt.find(str);
if (index1 >= tt.length()) return true;
int len = tt.length();
/* Stores the number of input variables. */
int variate_num = log(len) / log(2);
/* List all possible variable values/List truth table. */
vector<string> binary = list_truth_table(variate_num);
/* Stores the output of the truth table. */
vector<char> c_out;
for (int i = len - 1; i >= 0; i--) {
c_out.push_back(tt[i]);
}
vector<char> c_out1;
/* Minimum item for storing binary. */
vector<string> minterm;
for (int i = 0; i < binary.size(); i++) {
if (c_out[i] != '0') {
minterm.push_back(binary[i]);
c_out1.push_back(c_out[i]);
}
}
/* Store all polarities. */
vector<string> polarity = list_all_polarities(variate_num);
/* Count the initial cost. */
/* Storage optimal polarity. */
string optimal_polarity = polarity[0];
/* Mixed polarity conversion algorithm based on list technique. */
vector<string> RM_product =
polarity_conversion(variate_num, minterm, optimal_polarity);
/* Search for the optimal polarity and the corresponding product term. */
search_for_optimal_polarity(minterm, polarity, variate_num, RM_product,
optimal_polarity);
/* Count the number of nodes in the new network. */
int node_num_new =
count_the_number_of_nodes(variate_num, RM_product, optimal_polarity);
if (ps_ntk.multiplicative_complexity == true) {
/* Count the number of and nodes in the new network. */
int and_num_new =
count_node_num(RM_product, variate_num, optimal_polarity);
int and_num = 0;
mffc.foreach_gate([&](auto const& n1, auto i) {
if (ntk.is_and(n1)) and_num++;
});
int optimization_nodes = mffc.num_gates() - node_num_new;
int optimization_and_nodes = and_num - and_num_new;
if (optimization_nodes > 0 && optimization_and_nodes >= 0) {
/* Create expression. */
string expression = create_expression(RM_product, variate_num,
optimal_polarity, binary);
/* Replace network. */
substitute_network_mffc(expression, leaves, n);
}
} else {
int optimization_nodes = mffc.num_gates() - node_num_new;
if (optimization_nodes > 0) {
/* Create expression. */
string expression = create_expression(RM_product, variate_num,
optimal_polarity, binary);
/* Replace network. */
substitute_network_mffc(expression, leaves, n);
}
}
return true;
});
}
/**************************************************************************************************************/
private:
Ntk& ntk;
rm_rewriting_params const& ps_ntk;
};
} // namespace detail
/*! \brief RM logic optimization.
*
* This algorithm divides the network and then attempts to rewrite the
subnetwork by RM logic
* in terms of gates of the same network. The rewritten structures are added
* to the network, and if they lead to area improvement, will be used as new
* parts of the logic.
*
* **Required network functions:**
* - `get_node`
* - `level`
* - `update_levels`
* - `create_and`
* - `create_not`
* - `create_xor`
* - `substitute_node`
* - `foreach_node`
* - `foreach_po`
* - `foreach_fanin`
* - `is_and`
* - `clear_values`
* - `set_value`
* - `value`
* - `fanout_size`
*
\verbatim embed:rst
.. note::
\endverbatim
*/
template <class Ntk>
void rm_mixed_polarity(Ntk& ntk, rm_rewriting_params const& ps_ntk = {}) {
static_assert(is_network_type_v<Ntk>, "Ntk is not a network type");
static_assert(has_get_node_v<Ntk>,
"Ntk does not implement the get_node method");
static_assert(has_level_v<Ntk>, "Ntk does not implement the level method");
static_assert(has_create_maj_v<Ntk>,
"Ntk does not implement the create_maj method");
static_assert(has_create_xor_v<Ntk>,
"Ntk does not implement the create_maj method");
static_assert(has_substitute_node_v<Ntk>,
"Ntk does not implement the substitute_node method");
static_assert(has_update_levels_v<Ntk>,
"Ntk does not implement the update_levels method");
static_assert(has_foreach_node_v<Ntk>,
"Ntk does not implement the foreach_node method");
static_assert(has_foreach_po_v<Ntk>,
"Ntk does not implement the foreach_po method");
static_assert(has_foreach_fanin_v<Ntk>,
"Ntk does not implement the foreach_fanin method");
static_assert(has_is_and_v<Ntk>, "Ntk does not implement the is_and method");
static_assert(has_is_xor_v<Ntk>, "Ntk does not implement the is_xor method");
static_assert(has_clear_values_v<Ntk>,
"Ntk does not implement the clear_values method");
static_assert(has_set_value_v<Ntk>,
"Ntk does not implement the set_value method");
static_assert(has_value_v<Ntk>, "Ntk does not implement the value method");
static_assert(has_fanout_size_v<Ntk>,
"Ntk does not implement the fanout_size method");
detail::rm_mixed_polarity_impl p(ntk, ps_ntk);
p.run();
}
} /* namespace mockturtle */

1436
src/core/rm_multi.hpp Normal file

File diff suppressed because it is too large Load Diff

678
src/core/xag_rewriting.hpp Normal file
View File

@ -0,0 +1,678 @@
#pragma once
#include <iostream>
#include <optional>
#include <mockturtle/mockturtle.hpp>
namespace mockturtle
{
/*! \brief Parameters for xag_depth_rewriting.
*
* The data structure `xag_depth_rewriting_params` holds configurable
* parameters with default arguments for `xag_depth_rewriting`.
*/
struct xag_depth_rewriting_params
{
/*! \brief Rewriting strategy. */
enum strategy_t
{
/*! \brief DFS rewriting strategy.
*
* Applies depth rewriting once to all output cones whose drivers have
* maximum levels
*/
dfs,
/*! \brief Aggressive rewriting strategy.
*
* Applies depth reduction multiple times until the number of nodes, which
* cannot be rewritten, matches the number of nodes, in the current
* network; or the new network size is larger than the initial size w.r.t.
* to an `overhead`.
*/
aggressive,
/*! \brief Selective rewriting strategy.
*
* Like `aggressive`, but only applies rewriting to nodes on critical paths
* and without `overhead`.
*/
selective,
} strategy = dfs;
/*! \brief Overhead factor in aggressive rewriting strategy.
*
* When comparing to the initial size in aggressive depth rewriting, phyLS the
* number of dangling nodes are taken into account.
*/
float overhead{2.0f};
bool verbose{false};
};
namespace detail
{
template<class Ntk>
class xag_depth_rewriting_impl
{
public:
xag_depth_rewriting_impl( Ntk& ntk, xag_depth_rewriting_params const& ps )
: ntk( ntk ), ps( ps )
{
}
void run()
{
switch ( ps.strategy )
{
case xag_depth_rewriting_params::dfs:
run_dfs();
break;
case xag_depth_rewriting_params::selective:
run_selective();
break;
case xag_depth_rewriting_params::aggressive:
run_aggressive();
break;
}
}
/*****************************************Optimization strategies Begin********************************************************************/
private:
void run_dfs()
{
ntk.foreach_po( [this]( auto po ) {
const auto driver = ntk.get_node( po );
if ( ntk.level( driver ) < ntk.depth() )
return;
topo_view topo{ntk, po};
topo.foreach_node( [this]( auto n ) {
reduce_depth_ultimate( n );
return true;
} );
} );
}
/**************************************************************************************************************/
void run_selective()
{
uint32_t counter{0};
while ( true )
{
mark_critical_paths();
topo_view topo{ntk};
topo.foreach_node( [this, &counter]( auto n ) {
if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 )
return;
if ( reduce_depth_ultimate( n ) )
{
mark_critical_paths();
}
else
{
++counter;
}
} );
if ( counter > ntk.size() )
break;
}
}
/**************************************************************************************************************/
void run_aggressive()
{
uint32_t counter{0}, init_size{ntk.size()};
while ( true )
{
topo_view topo{ntk};
topo.foreach_node( [this, &counter]( auto n ) {
if ( ntk.fanout_size( n ) == 0 )
return;
if ( !reduce_depth_ultimate( n ) )
{
++counter;
}
} );
if ( ntk.size() > ps.overhead * init_size )
break;
if ( counter > ntk.size() )
break;
}
}
/*****************************************Optimization strategies End**********************************************************************/
/*****************************************Optimization Functions Begin*********************************************************************/
private:
bool reduce_depth_ultimate( node<Ntk> const& n )
{
auto b_1 = xor_inv_transfer( n );
auto b2 = xor_inv_remove( n );
auto b3 = xor_and_transfer( n );
auto b4 = reduce_depth_and_common_fanin( n );
auto b5 = reduce_depth_use_constant_fanin( n );
auto b6 = reduce_depth_use_xor_or_convert( n );
auto b7 = reduce_depth_use_xor_and_convert( n );
// auto b8 = reduce_depth_use_xor_or_convert2( n );
if(!( b_1 | b2 | b3 | b4 | b5 | b6 | b7 )){ return false; }
return true;
}
/**************************************************************************************************************/
/* [!a!b]=[ab], ![!a!b] = ![ab] */
/* Remove the INV of the two children of an node, thie node's type must be XOR. */
bool xor_inv_remove( node<Ntk> const& n )
{
if ( !ntk.is_xor( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending) */
auto ocs = ordered_children( n );
/* The two children must both have INV. */
if ( ntk.is_complemented(ocs[0]) && ntk.is_complemented(ocs[1]) )
{
ocs[0] = !ocs[0];
ocs[1] = !ocs[1];
auto opt = ntk.create_xor( ocs[0], ocs[1] );
ntk.substitute_node( n, opt );
ntk.update_levels();
}
return true;
}
/**************************************************************************************************************/
/* ![!ab] = ![a!b] = [ab], [!ab] = [a!b] = ![ab] */
/* Transfer the INV of the input to the fanout. */
/* can be used for subsquent optimization such as (a[!ab]) = (a![ab]) = (ab) */
bool xor_inv_transfer( node<Ntk> const& n )
{
if ( !ntk.is_xor( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending). */
auto ocs = ordered_children( n );
if(!ntk.is_complemented(ocs[0]) && !ntk.is_complemented(ocs[1]))
return false;
if( ( ntk.is_complemented(ocs[0]) ^ ntk.is_complemented(ocs[1] ) ) == 1)
{
if(ntk.is_complemented(ocs[0]))
{
ocs[0] = !ocs[0];
}
else
{
ocs[1] = !ocs[1];
}
}
auto opt = ntk.create_xor(ocs[0], ocs[1]) ^ true;
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
/**************************************************************************************************************/
bool reduce_depth_use_constant_fanin( node<Ntk> const& n )
{
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending). */
auto ocs = ordered_children( n );
if(ntk.fanout_size(ntk.get_node(ocs[0])) ==1 && ntk.fanout_size(ntk.get_node(ocs[1])) == 1)
{
if(ntk.is_xor(n))
{
if(ocs[0].index == 0 || ocs[1].index == 0)
{
if(ocs[0].index == 0 )
{
auto opt = ntk.is_complemented(ocs[0])? !ocs[1] : ocs[1];
ntk.substitute_node(n,opt);
ntk.update_levels();
}
if (ocs[1].index == 0 )
{
auto opt = ntk.is_complemented(ocs[1])? !ocs[0] : ocs[0];
ntk.substitute_node(n,opt);
ntk.update_levels();
}
}
if(ocs[0].index == 1 || ocs[1].index == 1 )
{
if(ocs[0].index == 1 )
{
auto opt = ntk.is_complemented(ocs[0])? ocs[1] : !ocs[1];
ntk.substitute_node(n, opt);
ntk.update_levels();
}
if(ocs[1].index == 1 )
{
auto opt = ntk.is_complemented(ocs[1])? ocs[0] : !ocs[0];
ntk.substitute_node(n, opt);
ntk.update_levels();
}
}
}
if(ntk.is_and(n))
{
if(ocs[0].index == 1 || ocs[1].index == 1)
{
if(ocs[0].index == 1 && !ntk.is_complemented(ocs[0]) )
{
auto opt = ocs[1];
ntk.substitute_node(n,opt);
ntk.update_levels();
}
else if(ocs[1].index == 1 && ntk.is_complemented(ocs[1]))
{
auto opt = ocs[0];
ntk.substitute_node(n,opt);
ntk.update_levels();
}
}
}
}
/* XOR: [a0] = a, [a1] = !a.*/
/* AND: (a1) = a, (a0) = 0. */
return true;
}
/**************************************************************************************************************/
/* Use the Boolean configuration between AND and XOR.
* There are main two expression for reference.
* (a[!ab]) = (ab), (!a[ab]) = (!ab). */
bool reduce_depth_use_xor_and_convert( node<Ntk> const& n )
{
if ( !ntk.is_and( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending). */
const auto ocs = ordered_children( n );
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 )
return false;
if( !ntk.is_xor( ntk.get_node( ocs[1] ) ) )
return false;
const auto ocs2 = ordered_children( ntk.get_node( ocs[1] ) );
/* Judgement */
if( ocs[0].index == ocs2[0].index && (ocs[0].complement ^ ocs2[0].complement) == 1)
{
if(ntk.fanout_size(ntk.get_node(ocs[1])) == 1)
{
auto opt = ntk.create_and( ocs[0], ocs2[1] );
ntk.substitute_node( n, opt );
ntk.update_levels();
}
return true;
}
return true;
}
/**************************************************************************************************************/
/* Refer to follow two expressions:
* (a(ab)) = (ab), (!a(!a!b)) = (!a!b).
*/
bool reduce_depth_and_common_fanin( node<Ntk> const& n )
{
if ( !ntk.is_and( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending). */
const auto ocs = ordered_children( n );
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 )
return false;
if( !ntk.is_and( ntk.get_node( ocs[1] ) ) )
return false;
const auto ocs2 = ordered_children( ntk.get_node( ocs[1] ) );
if(ocs[0] == ocs2[0] && ntk.fanout_size(ntk.get_node(ocs[1])) == 1 && ntk.fanout_size(ntk.get_node(ocs[1])) == 1)
{
auto opt = ocs[1];
ntk.substitute_node(n,opt);
ntk.update_levels();
}
/*if( auto cand = find_common_grand_child_two( ocs, ocs2 ); cand)
{
auto r = *cand;
auto opt = ntk.create_and( r.y, r.a );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}*/
return true;
}
/**************************************************************************************************************/
/* (a[ab]) = [a(ab)] */
bool xor_and_transfer( node<Ntk> const& n )
{
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending). */
const auto ocs = ordered_children( n );
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 )
return false;
if ( !ntk.is_and( n ) && !ntk.is_xor( ntk.get_node( ocs[1] ) ) )
return false;
const auto ocs2 = ordered_children( ntk.get_node( ocs[1] ) );
/* refer to the expressions above. */
if ( !ntk.is_complemented(ocs[0]) && !ntk.is_complemented(ocs[1]) && !ntk.is_complemented(ocs2[0]) && !ntk.is_complemented(ocs2[1]) )
return false;
if(ntk.fanout_size(ntk.get_node(ocs[1])) == 1)
{
if(ocs[0] == ocs2[0])
{
auto opt = ntk.create_xor(ocs[0], ntk.create_and(ocs2[0], ocs2[1]));
ntk.substitute_node(n, opt);
ntk.update_levels();
}
/*if ( auto cand = find_common_grand_child_two( ocs, ocs2 ); cand )
{
auto r = *cand;
auto opt = ntk.create_xor( r.a, ntk.create_and( r.a, r.y ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}*/
}
return true;
}
/**************************************************************************************************************/
/* a!(!!) (!ab) !a!(!a!b)]= !(!ab)*/
bool reduce_depth_use_xor_or_convert( node<Ntk> const& n )
{
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending). */
auto ocs = ordered_children( n );
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 )
return false;
if ( !ntk.is_xor( n ) && !ntk.is_and( ntk.get_node( ocs[1] ) ) )
return false;
auto ocs2 = ordered_children(ntk.get_node(ocs[1]));
if (ocs[0].index == ocs2[0].index && ntk.is_complemented(ocs[1]) && ntk.is_complemented(ocs2[0]) && ntk.is_complemented(ocs2[1]) && ntk.fanout_size(ntk.get_node(ocs[1])) == 1 )
{
if(ntk.is_complemented(ocs[0]))
{
ocs2[1] = !ocs2[1];
auto opt = ntk.create_nand(ocs2[0], ocs2[1]);
ntk.substitute_node(n, opt);
ntk.update_levels();
}
else
{
ocs2[1] = !ocs2[1];
auto opt = ntk.create_and(ocs2[0], ocs2[1]);
ntk.substitute_node(n, opt);
ntk.update_levels();
}
}
return true;
}
/**************************************************************************************************************/
/*{a!b} = {a[!ab]} = !(!a![!ab])) !{a!b} = (!ab)*/
bool reduce_depth_use_xor_or_convert2( node<Ntk> const& n )
{
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending). */
const auto ocs = ordered_children( n );
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 )
return false;
if ( !ntk.is_and( n ) && !ntk.is_xor( ntk.get_node( ocs[1] ) ) )
return false;
auto ocs2 = ordered_children( ntk.get_node( ocs[1] ) );
if(ntk.is_complemented(ocs[0]) && ntk.is_complemented(ocs[1]) && ocs[0] == ocs2[0] )
{
if(ntk.fanout_size(ntk.get_node(ocs[1])) == 1 && !ntk.is_complemented(ocs2[1]))
{
auto opt = ntk.create_or(ocs2[0], ocs2[1]);
ntk.substitute_node(n, opt);
ntk.update_levels();
}
return true;
}
return true;
}
/*****************************************Optimization Functions End***********************************************************************/
/*****************************************Other Necessary Parts Begin**********************************************************************/
bool is_signal_equal( const signal<Ntk>& a, const signal<Ntk>& b )
{
return ( a == b ? true : false );
}
/**************************************************************************************************************/
/* used for the subsquent judgement of the common child-node. */
using children_t = std::array< signal<Ntk>, 2 >;
std::bitset<4> get_pair_pattern( const children_t& c1, const children_t& c2 )
{
std::bitset<4> equals;
equals.set( 0u, is_signal_equal( c1[0], c2[0] ) );
equals.set( 1u, is_signal_equal( c1[0], c2[1] ) );
equals.set( 2u, is_signal_equal( c1[1], c2[0] ) );
equals.set( 3u, is_signal_equal( c1[1], c2[1] ) );
return equals;
}
/**************************************************************************************************************/
/* used to judge which two nodes are common. */
std::vector<size_t> equal_idx( const std::bitset<4> pattern )
{
std::vector<size_t> r;
for( size_t idx = 0; idx < pattern.size(); idx++ )
{
if( pattern.test( idx ) )
{
r.push_back( idx );
}
}
return r;
}
/**************************************************************************************************************/
struct grand_children_pair_t
{
signal<Ntk> x;
signal<Ntk> y;
signal<Ntk> a; //common grand child a
};
/**************************************************************************************************************/
std::optional<grand_children_pair_t> find_common_grand_child_two( const children_t& c1, const children_t& c2 )
{
grand_children_pair_t r;
auto p = get_pair_pattern( c1, c2 );
std::vector<signal<Ntk>> common;
for( size_t idx = 0; idx < p.size(); idx++ )
{
if( p.test(idx) )
{
assert( c1[idx / 2] == c2[ idx % 2 ] );
common.push_back( c1[ idx / 2] );
}
}
/* for xor children, require at least one common children */
if( common.size() != 1u )
return std::nullopt;
/* save the records */
r.a = common[0u];
for( auto i = 0u; i < 2u; i++ )
{
if( c1[i] != common[0u] && c1[i] != common[0u] )
{
r.x = c1[i];
}
if( c2[i] != common[0u] && c2[i] != common[0u] )
{
r.y = c2[i];
}
}
return r;
}
/**************************************************************************************************************/
std::array<signal<Ntk>, 2> ordered_children( node<Ntk> const& n ) const
{
std::array<signal<Ntk>, 2> children;
ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } );
std::sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) {
if( ntk.level( ntk.get_node( c1 ) ) == ntk.level( ntk.get_node( c2 ) ) )
{
return c1.index < c2.index;
}
else
{
return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) );
}
} );
return children;
}
/**************************************************************************************************************/
void mark_critical_path( node<Ntk> const& n )
{
if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) )
return;
const auto level = ntk.level( n );
ntk.set_value( n, 1 );
ntk.foreach_fanin( n, [this, level]( auto const& f ) {
if ( ntk.level( ntk.get_node( f ) ) == level - 1 )
{
mark_critical_path( ntk.get_node( f ) );
}
} );
}
/**************************************************************************************************************/
void mark_critical_paths()
{
ntk.clear_values();
ntk.foreach_po( [this]( auto const& f ) {
if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() )
{
mark_critical_path( ntk.get_node( f ) );
}
} );
}
/*****************************************Other Necessary Parts End************************************************************************/
private:
Ntk& ntk;
xag_depth_rewriting_params const& ps;
};
} // namespace detail
/*! \brief xag algebraic depth rewriting.
*
* This algorithm tries to rewrite a network with majority gates for depth
* optimization using the associativity and distributivity rule in
* majority-of-3 logic. It can be applied to networks other than xags, but
* only considers pairs of nodes which both implement the majority-of-3
* function and the XOR function.
*
* **Required network functions:**
* - `get_node`
* - `level`
* - `update_levels`
* - `create_maj`
* - `substitute_node`
* - `foreach_node`
* - `foreach_po`
* - `foreach_fanin`
* - `is_maj`
* - `clear_values`
* - `set_value`
* - `value`
* - `fanout_size`
*
\verbatim embed:rst
.. note::
\endverbatim
*/
template<class Ntk>
void xag_depth_rewriting( Ntk& ntk, xag_depth_rewriting_params const& ps = {} )
{
static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
static_assert( has_level_v<Ntk>, "Ntk does not implement the level method" );
static_assert( has_create_maj_v<Ntk>, "Ntk does not implement the create_maj method" );
static_assert( has_create_xor_v<Ntk>, "Ntk does not implement the create_maj method" );
static_assert( has_substitute_node_v<Ntk>, "Ntk does not implement the substitute_node method" );
static_assert( has_update_levels_v<Ntk>, "Ntk does not implement the update_levels method" );
static_assert( has_foreach_node_v<Ntk>, "Ntk does not implement the foreach_node method" );
static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
static_assert( has_foreach_fanin_v<Ntk>, "Ntk does not implement the foreach_fanin method" );
static_assert( has_is_and_v<Ntk>, "Ntk does not implement the is_and method" );
static_assert( has_is_xor_v<Ntk>, "Ntk does not implement the is_xor method" );
static_assert( has_clear_values_v<Ntk>, "Ntk does not implement the clear_values method" );
static_assert( has_set_value_v<Ntk>, "Ntk does not implement the set_value method" );
static_assert( has_value_v<Ntk>, "Ntk does not implement the value method" );
static_assert( has_fanout_size_v<Ntk>, "Ntk does not implement the fanout_size method" );
detail::xag_depth_rewriting_impl<Ntk> p( ntk, ps );
p.run();
}
} /* namespace mockturtle */

170
src/core/xmg_expand.hpp Normal file
View File

@ -0,0 +1,170 @@
#pragma once
#include <iostream>
#include <optional>
#include <mockturtle/mockturtle.hpp>
namespace mockturtle
{
struct xmg_expand_rewriting_params
{
enum strategy_t
{
expand,
constants
} strategy = expand;
std::vector<unsigned> xor_index;
};
namespace detail
{
template<class Ntk>
class xmg_expand_rewriting_impl
{
public:
xmg_expand_rewriting_impl( Ntk& ntk, xmg_expand_rewriting_params const& ps )
: ntk( ntk ), ps( ps )
{
}
void run()
{
switch( ps.strategy )
{
case xmg_expand_rewriting_params::expand:
run_xor_maj_expand();
break;
case xmg_expand_rewriting_params::constants:
run_xor_constants();
break;
}
}
private:
void run_xor_maj_expand()
{
ntk.foreach_po( [this]( auto po ) {
topo_view topo{ntk, po};
topo.foreach_node( [this]( auto n ) {
xor_maj_expand( n );
return true;
} );
} );
}
void run_xor_constants()
{
ntk.foreach_po( [this]( auto po ) {
topo_view topo{ntk, po};
topo.foreach_node( [this]( auto n ) {
xor_constants( n, ps.xor_index );
return true;
} );
} );
}
private:
void print_children( std::array<signal<Ntk>, 3u> const& children )
{
std::cout << "children 0 : " << ntk.get_node( children[0] ) << std::endl;
std::cout << "children 1 : " << ntk.get_node( children[1] ) << std::endl;
std::cout << "children 2 : " << ntk.get_node( children[2] ) << std::endl;
}
bool xor_constants( node<Ntk> const& n, std::vector<unsigned> const& xors )
{
if ( !ntk.is_xor3( n ) )
return false;
auto copy_node_2n = n * 2;
auto copy_node_2n_plus_1 = n * 2 + 1;
if( std::find( xors.begin(), xors.end(), copy_node_2n ) != xors.end() )
{
//std::cout << "Please assign constants to node " << n << std::endl;
auto opt = ntk.get_constant( false );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
else if( std::find( xors.begin(), xors.end(), copy_node_2n_plus_1 ) != xors.end() )
{
//std::cout << "Please assign constants to node " << n << std::endl;
auto opt = ntk.get_constant( true );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
else
{
return false;
}
}
bool xor_maj_expand( node<Ntk> const& n )
{
if ( !ntk.is_xor3( n ) )
return false;
if ( ntk.level( n ) <= 1 )
return false;
/* get children of top node, ordered by node level (ascending) */
const auto ocs = ordered_children( n );
/* if the first child is not constant, return */
if( ocs[0].index != 0 )
return false;
if( !ntk.is_maj( ntk.get_node( ocs[1] ) ) || !ntk.is_maj( ntk.get_node( ocs[2] ) ) )
return false;
const auto ogcs = ordered_children( ntk.get_node( ocs[1] ) );
auto n1 = ntk.create_xor( ogcs[0], ocs[2] );
auto n2 = ntk.create_xor( ogcs[1], ocs[2] );
auto n3 = ntk.create_xor( ogcs[2], ocs[2] );
auto opt = ntk.create_maj( n1, n2, n3 );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
std::array<signal<Ntk>, 3> ordered_children( node<Ntk> const& n ) const
{
std::array<signal<Ntk>, 3> children;
ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } );
std::sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) {
if( ntk.level( ntk.get_node( c1 ) ) == ntk.level( ntk.get_node( c2 ) ) )
{
return c1.index < c2.index;
}
else
{
return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) );
}
} );
return children;
}
private:
Ntk& ntk;
xmg_expand_rewriting_params const& ps;
};
} // namespace detail
template<class Ntk>
void xmg_expand_rewriting( Ntk& ntk, xmg_expand_rewriting_params const& ps )
{
detail::xmg_expand_rewriting_impl<Ntk> p( ntk, ps );
p.run();
}
} /* namespace mockturtle */

66
src/core/xmg_extract.hpp Normal file
View File

@ -0,0 +1,66 @@
#ifndef XMG_EXTRACT_HPP
#define XMG_EXTRACT_HPP
#include <mockturtle/algorithms/circuit_validator.hpp>
#include <mockturtle/views/topo_view.hpp>
#include "utils.hpp"
using namespace mockturtle;
namespace phyLS
{
std::vector<unsigned> xmg_extract( xmg_network const& xmg )
{
std::vector<unsigned> xors;
topo_view topo{ xmg };
circuit_validator v( xmg );
int count = 0;
topo.foreach_node( [&]( auto n ) {
if( xmg.is_xor3( n ) )
{
auto children = get_children( xmg, n );
//print_children( children );
if( xmg.get_node( children[0] ) == 0
&& !xmg.is_pi( xmg.get_node( children[1] ) )
&& !xmg.is_pi( xmg.get_node( children[2] ) ) )
{
auto result1 = v.validate( children[1], children[2] );
auto result2 = v.validate( children[1], !children[2] );
if( result1 == std::nullopt || result2 == std::nullopt )
{
//std::cout << "UNKNOWN status\n";
return;
}
if( result1 || result2 )
{
if( *result1 )
{
std::cout << "XOR node " << n << " can be removed as constant 0\n";
xors.push_back( n * 2 );
count++;
}
else if( *result2 )
{
std::cout << "XOR node " << n << " can be removed as constant 1\n";
xors.push_back( n * 2 + 1 );
count++;
}
}
}
}
} );
std::cout << "[xmgrw -u] Find " << count << " XOR constant nodes\n";
return xors;
}
}
#endif

925
src/core/xmg_rewriting.hpp Normal file
View File

@ -0,0 +1,925 @@
#pragma once
#include <iostream>
#include <optional>
#include <mockturtle/mockturtle.hpp>
namespace mockturtle
{
/*! \brief Parameters for xmg_depth_rewriting.
*
* The data structure `xmg_depth_rewriting_params` holds configurable
* parameters with default arguments for `xmg_depth_rewriting`.
*/
struct xmg_depth_rewriting_params
{
/*! \brief Rewriting strategy. */
enum strategy_t
{
/*! \brief DFS rewriting strategy.
*
* Applies depth rewriting once to all output cones whose drivers have
* maximum levels
*/
dfs,
/*! \brief Aggressive rewriting strategy.
*
* Applies depth reduction multiple times until the number of nodes, which
* cannot be rewritten, matches the number of nodes, in the current
* network; or the new network size is larger than the initial size w.r.t.
* to an `overhead`.
*/
aggressive,
/*! \brief Selective rewriting strategy.
*
* Like `aggressive`, but only applies rewriting to nodes on critical paths
* and without `overhead`.
*/
selective,
/*! \brief Selective rewriting strategy.
*
* Like `dfs`, rewrite XOR( a, XOR( b, c ) ) as XOR3(a, b, c )
*/
qca
} strategy = dfs;
/*! \brief Overhead factor in aggressive rewriting strategy.
*
* When comparing to the initial size in aggressive depth rewriting, also the
* number of dangling nodes are taken into account.
*/
float overhead{2.0f};
/*! \brief Allow area increase while optimizing depth. */
bool allow_area_increase{true};
/*! \brief xor3 to two xor2s that compatiable with old cirkit */
bool apply_xor3_to_xor2{false};
};
namespace detail
{
template<class Ntk>
class xmg_depth_rewriting_impl
{
public:
xmg_depth_rewriting_impl( Ntk& ntk, xmg_depth_rewriting_params const& ps )
: ntk( ntk ), ps( ps )
{
}
void run()
{
switch ( ps.strategy )
{
case xmg_depth_rewriting_params::dfs:
run_dfs();
break;
case xmg_depth_rewriting_params::selective:
run_selective();
break;
case xmg_depth_rewriting_params::aggressive:
run_aggressive();
break;
case xmg_depth_rewriting_params::qca:
run_qca();
break;
}
if( ps.apply_xor3_to_xor2 )
{
run_xor3_flatten();
}
}
private:
void run_qca()
{
/* reduce depth */
ntk.foreach_po( [this]( auto po ) {
const auto driver = ntk.get_node( po );
if ( ntk.level( driver ) < ntk.depth() )
return;
topo_view topo{ntk, po};
topo.foreach_node( [this]( auto n ) {
reduce_depth_ultimate( n );
return true;
} );
} );
/* rewrite xor2 to xor3 */
ntk.foreach_po( [this]( auto po ) {
topo_view topo{ntk, po};
topo.foreach_node( [this]( auto n ) {
reduce_depth_xor2_to_xor3( n );
return true;
} );
} );
}
void run_dfs()
{
ntk.foreach_po( [this]( auto po ) {
const auto driver = ntk.get_node( po );
if ( ntk.level( driver ) < ntk.depth() )
return;
topo_view topo{ntk, po};
topo.foreach_node( [this]( auto n ) {
reduce_depth_ultimate( n );
return true;
} );
} );
}
void run_selective()
{
uint32_t counter{0};
while ( true )
{
mark_critical_paths();
topo_view topo{ntk};
topo.foreach_node( [this, &counter]( auto n ) {
if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 )
return;
if ( reduce_depth_ultimate( n ) )
{
mark_critical_paths();
}
else
{
++counter;
}
} );
if ( counter > ntk.size() )
break;
}
}
void run_aggressive()
{
uint32_t counter{0}, init_size{ntk.size()};
while ( true )
{
topo_view topo{ntk};
topo.foreach_node( [this, &counter]( auto n ) {
if ( ntk.fanout_size( n ) == 0 )
return;
if ( !reduce_depth_ultimate( n ) )
{
++counter;
}
} );
if ( ntk.size() > ps.overhead * init_size )
break;
if ( counter > ntk.size() )
break;
}
}
void run_xor3_flatten()
{
/* rewrite xor3 to xor2 */
ntk.foreach_po( [this]( auto po ) {
topo_view topo{ntk, po};
topo.foreach_node( [this]( auto n ) {
xor3_to_xor2( n );
return true;
} );
} );
}
private:
bool reduce_depth_ultimate( node<Ntk> const& n )
{
auto b_1 = reduce_depth( n );
auto b2 = reduce_depth_xor_associativity( n );
auto b3 = reduce_depth_xor_complementary_associativity( n );
auto b4 = reduce_depth_xor_distribute( n );
if( !( b_1 | b2 | b3 | b4 ) ) { return false; }
return true;
}
bool xor3_to_xor2( node<Ntk> const& n )
{
if ( !ntk.is_xor3( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending) */
const auto ocs = ordered_children( n );
/* if the first child is constant, return */
if( ocs[0].index == 0 )
return false;
auto opt = ntk.create_xor( ocs[2], ntk.create_xor( ocs[0], ocs[1] ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
bool reduce_depth_xor2_to_xor3( node<Ntk> const& n )
{
if ( !ntk.is_xor3( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending) */
const auto ocs = ordered_children( n );
/* if the first child is not constant, return */
if( ocs[0].index != 0 )
return false;
/* if there are no XOR children, return */
if ( !ntk.is_xor3( ntk.get_node( ocs[2] ) ) && !ntk.is_xor3( ntk.get_node( ocs[1] ) ) )
return false;
if( ntk.is_xor3( ntk.get_node( ocs[2] ) ) )
{
/* size optimization, do not allow area increase */
if( ntk.fanout_size( ntk.get_node( ocs[2] ) ) == 1 )
{
/* get children of last child */
auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) );
/* propagate inverter if necessary */
if ( ntk.is_complemented( ocs[2] ) )
{
/* complement the constants */
ocs2[0] = !ocs2[0];
}
if( ocs2[0].index == 0 )
{
auto opt = ntk.create_xor3( ocs[1], ocs2[1], ocs2[2] );
ntk.substitute_node( n, opt );
ntk.update_levels();
}
return true;
}
}
if( ntk.is_xor3( ntk.get_node( ocs[1] ) ) )
{
/* size optimization, do not allow area increase */
if( ntk.fanout_size( ntk.get_node( ocs[1] ) ) == 1 )
{
/* get children of second child */
auto ocs2 = ordered_children( ntk.get_node( ocs[1] ) );
/* propagate inverter if necessary */
if ( ntk.is_complemented( ocs[1] ) )
{
/* complement the constants */
ocs2[0] = !ocs2[0];
}
if( ocs2[0].index == 0 )
{
auto opt = ntk.create_xor3( ocs[2], ocs2[1], ocs2[2] );
ntk.substitute_node( n, opt );
ntk.update_levels();
}
return true;
}
}
return false;
}
bool reduce_depth( node<Ntk> const& n )
{
if ( !ntk.is_maj( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending) */
const auto ocs = ordered_children( n );
if ( !ntk.is_maj( ntk.get_node( ocs[2] ) ) )
return false;
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 )
return false;
/* child must have single fanout, if no area overhead is allowed */
if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 )
return false;
/* get children of last child */
auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) );
/* depth of last grand-child must be higher than depth of second grand-child */
if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) )
return false;
/* propagate inverter if necessary */
if ( ntk.is_complemented( ocs[2] ) )
{
ocs2[0] = !ocs2[0];
ocs2[1] = !ocs2[1];
ocs2[2] = !ocs2[2];
}
if ( auto cand = associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand )
{
const auto& [x, y, z, u, assoc] = *cand;
auto opt = ntk.create_maj( z, assoc ? u : x, ntk.create_maj( x, y, u ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
/* distributivity */
if ( ps.allow_area_increase )
{
auto opt = ntk.create_maj( ocs2[2],
ntk.create_maj( ocs[0], ocs[1], ocs2[0] ),
ntk.create_maj( ocs[0], ocs[1], ocs2[1] ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
return false;
}
using candidate_t = std::tuple<signal<Ntk>, signal<Ntk>, signal<Ntk>, signal<Ntk>, bool>;
std::optional<candidate_t> associativity_candidate( signal<Ntk> const& v, signal<Ntk> const& w, signal<Ntk> const& x, signal<Ntk> const& y, signal<Ntk> const& z ) const
{
if ( v.index == x.index )
{
return candidate_t{w, y, z, v, v.complement == x.complement};
}
if ( v.index == y.index )
{
return candidate_t{w, x, z, v, v.complement == y.complement};
}
if ( w.index == x.index )
{
return candidate_t{v, y, z, w, w.complement == x.complement};
}
if ( w.index == y.index )
{
return candidate_t{v, x, z, w, w.complement == y.complement};
}
return std::nullopt;
}
bool reduce_depth_xor_associativity( node<Ntk> const& n )
{
if ( !ntk.is_xor3( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending) */
const auto ocs = ordered_children( n );
if ( !ntk.is_xor3( ntk.get_node( ocs[2] ) ) )
return false;
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 )
return false;
/* child must have single fanout, if no area overhead is allowed */
if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 )
return false;
/* get children of last child */
auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) );
/* depth of last grand-child must be higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs2[2] ) ) + 1 <= ntk.level( ntk.get_node( ocs[1] ) ) )
return false;
/* propagate inverter if necessary */
if ( ntk.is_complemented( ocs[2] ) )
{
ocs2[2] = !ocs2[2];
}
if( ocs[0] == ocs2[0] && ocs[0].index == 0 && ntk.fanout_size( ntk.get_node( ocs[2] ) ) == 1)
{
auto opt = ntk.create_xor3( ocs[0], ocs2[2],
ntk.create_xor3( ocs2[0], ocs2[1], ocs[1] ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
return false;
}
/* XOR complementary associativity <xy[!yz]> = <xy[xz]> */
bool reduce_depth_xor_complementary_associativity( node<Ntk> const& n )
{
if ( !ntk.is_maj( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending) */
const auto ocs = ordered_children( n );
if ( !ntk.is_xor3( ntk.get_node( ocs[2] ) ) )
return false;
/* depth of last child must be (significantly) higher than depth of second child */
/* depth of last child must be higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[2] ) ) < ntk.level( ntk.get_node( ocs[1] ) ) + 1 )
return false;
/* multiple child fanout is allowable */
if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 )
return false;
/* get children of last child */
auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) );
/* depth of last grand-child must be higher than depth of second grand-child */
if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) )
return false;
/* propagate inverter if necessary */
if ( ntk.is_complemented( ocs[2] ) )
{
if ( ntk.is_complemented( ocs2[0] ) )
{
ocs2[0] = !ocs2[0];
}
else if ( ntk.is_complemented( ocs2[1] ) )
{
ocs2[1] = !ocs2[1];
}
else if ( ntk.is_complemented( ocs2[2] ) )
{
ocs2[2] = !ocs2[2];
}
else
{
ocs2[0] = !ocs2[0];
}
}
if ( auto cand = xor_compl_associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand )
{
const auto& [x, y, z, u, assoc] = *cand;
auto opt = ntk.create_maj( x, u, ntk.create_xor3( assoc ? !x : x, y, z ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
return false;
}
std::optional<candidate_t> xor_compl_associativity_candidate( signal<Ntk> const& v, signal<Ntk> const& w, signal<Ntk> const& x, signal<Ntk> const& y, signal<Ntk> const& z ) const
{
if ( v.index == x.index )
{
return candidate_t{w, y, z, v, v.complement == x.complement};
}
if ( v.index == y.index )
{
return candidate_t{w, x, z, v, v.complement == y.complement};
}
if ( v.index == z.index )
{
return candidate_t{w, x, y, v, v.complement == z.complement};
}
if ( w.index == x.index )
{
return candidate_t{v, y, z, w, w.complement == x.complement};
}
if ( w.index == y.index )
{
return candidate_t{v, x, z, w, w.complement == y.complement};
}
if ( w.index == z.index )
{
return candidate_t{v, x, y, w, w.complement == z.complement};
}
return std::nullopt;
}
/* XOR distribute over MAJ */
bool reduce_depth_xor_distribute( node<Ntk> const& n )
{
if ( !ntk.is_maj( n ) )
return false;
if ( ntk.level( n ) == 0 )
return false;
/* get children of top node, ordered by node level (ascending) */
const auto ocs = ordered_children( n );
std::vector<unsigned> xor_index;
for( auto i = 0; i <= 2; i++ )
{
if( ntk.is_xor3( ntk.get_node( ocs[i] ) ) )
{
xor_index.push_back( i );
}
}
/* consider at least two xor child nodes */
if ( xor_index.size() < 2 )
return false;
/* depth of last child must be (significantly) higher than depth of second child */
if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 )
return false;
/* child must have single fanout, if no area overhead is allowed */
if( !ps.allow_area_increase )
{
for( const auto& index : xor_index )
{
if( ntk.fanout_size( ntk.get_node( ocs[index] ) ) != 1 )
{
return false;
}
}
}
/* propagate inverter if necessary */
for( const auto& index : xor_index )
{
if( ntk.is_complemented( ntk.get_node( ocs[index] ) ) )
{
auto ocs_next = ordered_children( ntk.get_node( ocs[index] ) );
ocs_next[2] = !ocs_next[2];
}
}
if( xor_index.size() == 3u )
{
auto ocs1 = ordered_children( ntk.get_node( ocs[0] ) );
auto ocs2 = ordered_children( ntk.get_node( ocs[1] ) );
auto ocs3 = ordered_children( ntk.get_node( ocs[2] ) );
if ( auto cand = find_common_grand_child_three( ocs1, ocs2, ocs3 ); cand )
{
auto r = *cand;
auto opt = ntk.create_xor3( r.a, r.b, ntk.create_maj( r.x, r.y, r.z ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
return false;
}
else if( xor_index.size() == 2u )
{
auto ocs2 = ordered_children( ntk.get_node( ocs[1] ) );
auto ocs3 = ordered_children( ntk.get_node( ocs[2] ) );
if ( auto cand = find_common_grand_child_two( ocs[0], ocs2, ocs3 ); cand )
{
auto r = *cand;
auto opt = ntk.create_xor3( r.a, r.b, ntk.create_maj( r.x, r.y, r.z ) );
ntk.substitute_node( n, opt );
ntk.update_levels();
return true;
}
return false;
}
else
{
return false;
}
return false;
}
bool is_signal_equal( const signal<Ntk>& a, const signal<Ntk>& b )
{
return ( a == b ? true : false );
}
bool is_three_signal_equal( const signal<Ntk>& a, const signal<Ntk>& b, const signal<Ntk>& c )
{
return ( a == b ? ( b == c ? true : false ): false );
}
using children_t = std::array< signal<Ntk>, 3 >;
std::bitset<9> get_pair_pattern( const children_t& c1, const children_t& c2 )
{
std::bitset<9> equals;
equals.set( 0u, is_signal_equal( c1[0], c2[0] ) );
equals.set( 1u, is_signal_equal( c1[0], c2[1] ) );
equals.set( 2u, is_signal_equal( c1[0], c2[2] ) );
equals.set( 3u, is_signal_equal( c1[1], c2[0] ) );
equals.set( 4u, is_signal_equal( c1[1], c2[1] ) );
equals.set( 5u, is_signal_equal( c1[1], c2[2] ) );
equals.set( 6u, is_signal_equal( c1[2], c2[0] ) );
equals.set( 7u, is_signal_equal( c1[2], c2[1] ) );
equals.set( 8u, is_signal_equal( c1[2], c2[2] ) );
return equals;
}
/* struct for representing grand chiledren */
struct grand_children_pair_t
{
signal<Ntk> x;
signal<Ntk> y;
signal<Ntk> z;
signal<Ntk> a; //common grand child a
signal<Ntk> b; //common grand child b
};
std::vector<size_t> equal_idx( const std::bitset<9> pattern )
{
std::vector<size_t> r;
for( size_t idx = 0; idx < pattern.size(); idx++ )
{
if( pattern.test( idx ) )
{
r.push_back( idx );
}
}
return r;
}
/* find common grand children in three children */
std::optional<grand_children_pair_t> find_common_grand_child_three( const children_t& c1, const children_t& c2, const children_t& c3 )
{
grand_children_pair_t r;
auto p1 = get_pair_pattern( c1, c2 );
auto p2 = get_pair_pattern( c1, c3 );
auto p3 = get_pair_pattern( c2, c3 );
auto s1 = equal_idx( p1 );
auto s2 = equal_idx( p2 );
auto s3 = equal_idx( p3 );
if( s1.size() != 2u || s2.size() != 2u || s3.size() != 2u )
return std::nullopt;
auto c11 = c1[ s1[0u] / 3 ];
auto c12 = c1[ s1[1u] / 3 ];
auto c21 = c2[ s3[0u] / 3 ];
auto c22 = c2[ s3[1u] / 3 ];
auto c31 = c3[ s2[0u] % 3 ];
auto c32 = c3[ s2[1u] % 3 ];
std::bitset<8> equals;
equals.set( 0u, is_three_signal_equal( c11, c21, c31 ) );
equals.set( 1u, is_three_signal_equal( c11, c21, c32 ) );
equals.set( 2u, is_three_signal_equal( c11, c22, c31 ) );
equals.set( 3u, is_three_signal_equal( c11, c22, c32 ) );
equals.set( 4u, is_three_signal_equal( c12, c21, c31 ) );
equals.set( 5u, is_three_signal_equal( c12, c21, c32 ) );
equals.set( 6u, is_three_signal_equal( c12, c22, c31 ) );
equals.set( 7u, is_three_signal_equal( c12, c22, c32 ) );
std::vector<signal<Ntk>> common;
for( size_t idx = 0; idx < 8; idx++ )
{
if( equals.test( idx ) )
{
if( idx < 4 )
{
common.push_back( c11 );
}
else
{
common.push_back( c12 );
}
}
}
if( common.size() != 2 )
return std::nullopt;
r.a = common[0u];
r.b = common[1u];
for( auto i = 0u; i < 3u; i++ )
{
if( c1[i] != common[0u] && c1[i] != common[1u] )
{
r.x = c1[i];
}
if( c2[i] != common[0u] && c2[i] != common[1u] )
{
r.y = c2[i];
}
if( c3[i] != common[0u] && c3[i] != common[1u] )
{
r.z = c3[i];
}
}
return r;
}
/* find common grand children in two children */
std::optional<grand_children_pair_t> find_common_grand_child_two( const signal<Ntk>& v, const children_t& c1, const children_t& c2 )
{
grand_children_pair_t r;
auto p = get_pair_pattern( c1, c2 );
std::vector<signal<Ntk>> common;
for( size_t idx = 0; idx < p.size(); idx++ )
{
if( p.test(idx) )
{
assert( c1[idx / 3] == c2[ idx % 3 ] );
common.push_back( c1[ idx / 3] );
}
}
//std::cout << "common size: " << common.size() << std::endl;
//std::cout << "common 0 index: " << common[0].index << std::endl;
//std::cout << "common 1 index: " << common[1].index << std::endl;
//std::cout << "v index: " << v.index << std::endl;
/* for xor children, require at least two common children */
if( common.size() != 2u )
return std::nullopt;
/* the signal v index must equal one of the common signal index, and one common signal is
* constant 1 or 0 */
if( v.index != common[0u].index && v.index != common[1u].index )
return std::nullopt;
if( common[0u].index != 0 && common[1u].index != 0 )
return std::nullopt;
/* save the records */
r.a = common[0u];
r.b = common[1u];
for( auto i = 0u; i < 3u; i++ )
{
if( c1[i] != common[0u] && c1[i] != common[1u] )
{
r.x = c1[i];
}
if( c2[i] != common[0u] && c2[i] != common[1u] )
{
r.y = c2[i];
}
}
if( v == !common[0u] || v == !common[1u] )
{
r.z = ntk.get_constant( true );
}
if( v == common[0u] || v == common[1u] )
{
r.z = ntk.get_constant( false );
}
return r;
}
std::array<signal<Ntk>, 3> ordered_children( node<Ntk> const& n ) const
{
std::array<signal<Ntk>, 3> children;
ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } );
std::sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) {
if( ntk.level( ntk.get_node( c1 ) ) == ntk.level( ntk.get_node( c2 ) ) )
{
return c1.index < c2.index;
}
else
{
return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) );
}
} );
return children;
}
void mark_critical_path( node<Ntk> const& n )
{
if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) )
return;
const auto level = ntk.level( n );
ntk.set_value( n, 1 );
ntk.foreach_fanin( n, [this, level]( auto const& f ) {
if ( ntk.level( ntk.get_node( f ) ) == level - 1 )
{
mark_critical_path( ntk.get_node( f ) );
}
} );
}
void mark_critical_paths()
{
ntk.clear_values();
ntk.foreach_po( [this]( auto const& f ) {
if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() )
{
mark_critical_path( ntk.get_node( f ) );
}
} );
}
private:
Ntk& ntk;
xmg_depth_rewriting_params const& ps;
};
} // namespace detail
/*! \brief XMG algebraic depth rewriting.
*
* This algorithm tries to rewrite a network with majority gates for depth
* optimization using the associativity and distributivity rule in
* majority-of-3 logic. It can be applied to networks other than XMGs, but
* only considers pairs of nodes which both implement the majority-of-3
* function and the XOR function.
*
* **Required network functions:**
* - `get_node`
* - `level`
* - `update_levels`
* - `create_maj`
* - `substitute_node`
* - `foreach_node`
* - `foreach_po`
* - `foreach_fanin`
* - `is_maj`
* - `clear_values`
* - `set_value`
* - `value`
* - `fanout_size`
*
\verbatim embed:rst
.. note::
The implementation of this algorithm was heavily inspired by an
implementation from Luca Amarù.
\endverbatim
*/
template<class Ntk>
void xmg_depth_rewriting( Ntk& ntk, xmg_depth_rewriting_params const& ps = {} )
{
static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
static_assert( has_level_v<Ntk>, "Ntk does not implement the level method" );
static_assert( has_create_maj_v<Ntk>, "Ntk does not implement the create_maj method" );
static_assert( has_create_xor_v<Ntk>, "Ntk does not implement the create_maj method" );
static_assert( has_substitute_node_v<Ntk>, "Ntk does not implement the substitute_node method" );
static_assert( has_update_levels_v<Ntk>, "Ntk does not implement the update_levels method" );
static_assert( has_foreach_node_v<Ntk>, "Ntk does not implement the foreach_node method" );
static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
static_assert( has_foreach_fanin_v<Ntk>, "Ntk does not implement the foreach_fanin method" );
static_assert( has_is_maj_v<Ntk>, "Ntk does not implement the is_maj method" );
static_assert( has_is_xor3_v<Ntk>, "Ntk does not implement the is_maj method" );
static_assert( has_clear_values_v<Ntk>, "Ntk does not implement the clear_values method" );
static_assert( has_set_value_v<Ntk>, "Ntk does not implement the set_value method" );
static_assert( has_value_v<Ntk>, "Ntk does not implement the value method" );
static_assert( has_fanout_size_v<Ntk>, "Ntk does not implement the fanout_size method" );
detail::xmg_depth_rewriting_impl<Ntk> p( ntk, ps );
p.run();
}
} /* namespace mockturtle */

View File

@ -70,5 +70,11 @@
#include "commands/abc/balance.hpp"
#include "commands/abc/refactor.hpp"
#include "commands/abc/rewrite.hpp"
#include "commands/xag/rm.hpp"
#include "commands/xag/rm2.hpp"
#include "commands/xag/xagrw.hpp"
#include "commands/xag/xagrs.hpp"
#include "commands/xmg/xmgrs.hpp"
#include "commands/xmg/xmgrw.hpp"
ALICE_MAIN(phyLS)