diff --git a/src/commands/balance.hpp b/src/commands/balance.hpp index 74dfedf..0413845 100644 --- a/src/commands/balance.hpp +++ b/src/commands/balance.hpp @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include #include @@ -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().current(); + xmg = balancing( + xmg, + {sop_rebalancing{}}); // TODO: we need maj-xor balancing + xmg = cleanup_dangling(xmg); + auto xmg_copy = cleanup_dangling(xmg); + phyLS::print_stats(xmg_copy); - if (store().size() == 0u) - std::cerr << "Error: Empty AIG network\n"; - else { - auto aig = store().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().extend(); + store().current() = xmg_copy; + } else { + if (store().size() == 0u) + std::cerr << "Error: Empty AIG network\n"; + else { + auto aig = store().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().extend(); + store().current() = aig; } - phyLS::print_stats(aig); - - store().extend(); - store().current() = aig; } cout.setf(ios::fixed); diff --git a/src/commands/xag/rm.hpp b/src/commands/xag/rm.hpp new file mode 100644 index 0000000..9faa0b7 --- /dev/null +++ b/src/commands/xag/rm.hpp @@ -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 + +#include + +#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(env), + {[this]() { return (strategy <= 1 && strategy >= 0); }, + "strategy must in [0,1] "}}; + } + + return {has_store_element(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().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(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().extend(); + store().current() = xag; + + } else if (is_set("xmg")) { + // clock_t start, end; + // start = clock(); + mockturtle::xmg_network xmg = store().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(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().extend(); + store().current() = xmg; + } + } + + private: + int strategy = 0; + rm_rewriting_params ps_ntk; +}; + +ALICE_ADD_COMMAND(xagopt, "Synthesis") + +} // namespace alice + +#endif diff --git a/src/commands/xag/rm2.hpp b/src/commands/xag/rm2.hpp new file mode 100644 index 0000000..0abd530 --- /dev/null +++ b/src/commands/xag/rm2.hpp @@ -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 + +#include + +#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(env), + {[this]() { return (strategy <= 1 && strategy >= 0); }, + "strategy must in [0,1] "}}; + } + + return {has_store_element(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().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> + // 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(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().extend(); + store().current() = xag; + + } else if (is_set("xmg")) { + // clock_t start, end; + // start = clock(); + mockturtle::xmg_network xmg = store().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(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().extend(); + store().current() = xmg; + } + } + + private: + int strategy = 0; + rm_rewriting_params2 ps_ntk; +}; + +ALICE_ADD_COMMAND(xagopt2, "Synthesis") + +} // namespace alice + +#endif diff --git a/src/commands/xag/xagrs.hpp b/src/commands/xag/xagrs.hpp new file mode 100644 index 0000000..a0c94c2 --- /dev/null +++ b/src/commands/xag/xagrs.hpp @@ -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 +#include +#include + +#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( env ) }; + } + + protected: + void execute() + { + /* derive some XAG */ + xag_network xag = store().current(); + + default_resubstitution( xag, ps, &st ); + xag = cleanup_dangling( xag ); + + std::cout << "[xagrs] "; + phyLS::print_stats( xag ); + + store().extend(); + store().current() = xag; + } + + private: + resubstitution_params ps; + resubstitution_stats st; + }; + + ALICE_ADD_COMMAND( xagrs, "Synthesis" ) + + +} + +#endif diff --git a/src/commands/xag/xagrw.hpp b/src/commands/xag/xagrw.hpp new file mode 100644 index 0000000..828dd15 --- /dev/null +++ b/src/commands/xag/xagrw.hpp @@ -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 +#include +#include +#include +#include + +#include "../../core/misc.hpp" +#include "../../core/xag_rewriting.hpp" +// #include + +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(env), + {[this]() { return (strategy <= 2 && strategy >= 0); }, + "strategy must in [0,2] "}}; + } + + protected: + void execute() { + xag_network xag = store().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(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 <().extend(); + store().current() = xag; + } + + private: + xag_depth_rewriting_params ps_xag; + int strategy = 0; +}; + +ALICE_ADD_COMMAND(xagrw, "Synthesis") +} // namespace alice + +#endif diff --git a/src/commands/xmg/xmginv.hpp b/src/commands/xmg/xmginv.hpp index a96cde8..9824e03 100644 --- a/src/commands/xmg/xmginv.hpp +++ b/src/commands/xmg/xmginv.hpp @@ -16,7 +16,7 @@ #include #include -#include "../core/xmginv.hpp" +#include "../../core/xmginv.hpp" namespace alice { diff --git a/src/commands/xmg/xmgrs.hpp b/src/commands/xmg/xmgrs.hpp new file mode 100644 index 0000000..c806e37 --- /dev/null +++ b/src/commands/xmg/xmgrs.hpp @@ -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 +#include +#include + +#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(env)}; } + + protected: + void execute() { + /* derive some XMG */ + xmg_network xmg = store().current(); + + xmg_network xmg1, xmg2; + xmg1 = xmg; + xmg2 = xmg; + + using view_t = depth_view>; + fanout_view 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(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().extend(); + store().current() = xmg2; + } + + private: + resubstitution_params ps; + resubstitution_stats st; +}; + +ALICE_ADD_COMMAND(xmgrs, "Synthesis") + +} // namespace alice + +#endif diff --git a/src/commands/xmg/xmgrw.hpp b/src/commands/xmg/xmgrw.hpp new file mode 100644 index 0000000..c9f2cdb --- /dev/null +++ b/src/commands/xmg/xmgrw.hpp @@ -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 +#include +#include +#include + +#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(env), + {[this]() { return (strategy <= 3 && strategy >= 0); }, + "strategy must in [0,3] "}}; + } + + protected: + void execute() { + xmg_network xmg = store().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(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().extend(); + store().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 diff --git a/src/core/rm.hpp b/src/core/rm.hpp new file mode 100644 index 0000000..97612df --- /dev/null +++ b/src/core/rm.hpp @@ -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 +#include +#include +#include +#include +#include +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 rm_mixed_polarity_impl { + public: + using node_t = node; + using signal_t = signal; + + 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& 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 const& leaves) { + std::vector pis; + pis.clear(); + for (const auto& l : leaves) { + pis.push_back(ntk.make_signal(l)); + } + + std::stack 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 list_truth_table(int& variate_num) { + int a = pow(2, variate_num); + vector b; + for (int i = 0; i < a; i++) { + b.push_back(i); + } + vector 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 list_all_polarities(int& variate_num) { + int a_1 = pow(2, variate_num); + vector c; + for (int i = 0; i < a_1; i++) { + c.push_back(i); + } + vector 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 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 polarity_conversion(int& variate_num, vector& minterm, + string& polarity) { + vector 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 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 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& minterm, + vector& polarity, int& variate_num, + vector& RM_product, + string& optimal_polarity) { + vector 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& 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& RM_product, int& variate_num, + string& optimal_polarity, vector& 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& 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& 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> ordered_children(node const& n) const { + vector> 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 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 pis, + int& optimization_and_nodes) { + int a = 0; + int b = 0; + if (is_existence(root, pis) == 1) return; + /* Create a queue container. */ + queue deq; + + deq.push(root); + while (!deq.empty()) { + long int tr = deq.front(); + deq.pop(); + + vector> 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, 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 leaves; + for (auto leaf_index : *cut) { + leaves.push_back(ntk.index_to_node(leaf_index)); + } + cut_view 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 binary = list_truth_table(variate_num); + + /* Stores the output of the truth table. */ + vector c_out; + for (int i = len - 1; i >= 0; i--) { + c_out.push_back(tt[i]); + } + + vector c_out1; + /* Minimum item for storing binary. */ + vector 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 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 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 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 leaves(mffc.num_pis()); + mffc.foreach_pi([&](auto const& m, auto j) { leaves[j] = m; }); + + default_simulator sim(mffc.num_pis()); + string tt = to_binary(simulate(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 binary = list_truth_table(variate_num); + + /* Stores the output of the truth table. */ + vector c_out; + for (int i = len - 1; i >= 0; i--) { + c_out.push_back(tt[i]); + } + + vector c_out1; + /* Minimum item for storing binary. */ + vector 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 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 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 +void rm_mixed_polarity(Ntk& ntk, rm_rewriting_params const& ps_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_level_v, "Ntk does not implement the level method"); + static_assert(has_create_maj_v, + "Ntk does not implement the create_maj method"); + static_assert(has_create_xor_v, + "Ntk does not implement the create_maj method"); + static_assert(has_substitute_node_v, + "Ntk does not implement the substitute_node method"); + static_assert(has_update_levels_v, + "Ntk does not implement the update_levels method"); + static_assert(has_foreach_node_v, + "Ntk does not implement the foreach_node 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_is_and_v, "Ntk does not implement the is_and method"); + static_assert(has_is_xor_v, "Ntk does not implement the is_xor method"); + 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_value_v, "Ntk does not implement the value method"); + static_assert(has_fanout_size_v, + "Ntk does not implement the fanout_size method"); + + detail::rm_mixed_polarity_impl p(ntk, ps_ntk); + p.run(); +} + +} /* namespace mockturtle */ diff --git a/src/core/rm_multi.hpp b/src/core/rm_multi.hpp new file mode 100644 index 0000000..7848b90 --- /dev/null +++ b/src/core/rm_multi.hpp @@ -0,0 +1,1436 @@ +/* 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 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +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_params2 { + /*! \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 rm_mixed_polarity_impl2 { + public: + using node_t = node; + using signal_t = signal; + + rm_mixed_polarity_impl2(Ntk& ntk, rm_rewriting_params2 const& ps_ntk, + cut_rewriting_params const& ps) + : ntk(ntk), ps_ntk(ps_ntk), ps(ps) {} + + void run() { + switch (ps_ntk.strategy) { + case rm_rewriting_params2::cut: + ntk_cut(); + break; + case rm_rewriting_params2::mffc: + ntk_mffc(); + break; + } + } + + private: + /**************************************************************************************************************/ + uint32_t recursive_deref1(Ntk& ntk1, node const& n) { + /* terminate? */ + if (ntk1.is_constant(n) || ntk1.is_pi(n)) return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}(ntk1, n); + if (ntk1.is_and(n)) and_node_deref++; + ntk1.foreach_fanin(n, [&](auto const& s) { + if (ntk1.decr_value(ntk1.get_node(s)) == 0) { + value += recursive_deref1(ntk1, ntk1.get_node(s)); + } + }); + return value; + } + /**************************************************************************************************************/ + uint32_t recursive_ref1(Ntk& ntk1, node const& n) { + /* terminate? */ + if (ntk1.is_constant(n) || ntk1.is_pi(n)) return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}(ntk1, n); + if (ntk1.is_and(n)) and_node_ref++; + ntk1.foreach_fanin(n, [&](auto const& s) { + if (ntk1.incr_value(ntk1.get_node(s)) == 0) { + value += recursive_ref1(ntk1, ntk1.get_node(s)); + } + }); + return value; + } + /**************************************************************************************************************/ + std::pair recursive_ref_contains(Ntk& ntk1, node const& n, + node const& repl) { + /* terminate? */ + if (ntk1.is_constant(n) || ntk1.is_pi(n)) return {0, false}; + + /* recursively collect nodes */ + int32_t value = NodeCostFn{}(ntk1, n); + if (ntk1.is_and(n)) and_node_ref++; + bool contains = (n == repl); + ntk1.foreach_fanin(n, [&](auto const& s) { + contains = contains || (ntk1.get_node(s) == repl); + if (ntk1.incr_value(ntk1.get_node(s)) == 0) { + const auto [v, c] = + recursive_ref_contains(ntk1, ntk1.get_node(s), repl); + value += v; + contains = contains || c; + } + }); + return {value, contains}; + } + /**************************************************************************************************************/ + void pw_opt_by_onset( + std::map> onset) // on_set optimization + { + int x = 0; + int i, j; + vector decimalism; + set decimalism1; // Check to see if the elements are all equal + int L = onset.size(), + C = (onset[0]).size(); // L is the length and C is the width + /*cout << L << " " << C << endl;*/ + if (L == 2) return; + + // cout << "------------------------------" << endl; + // for (int i = 0; i < L; i++) + //{ + // for (int j = 0; j = 2 && + expression_new[expression_new.size() - 2] == '!') { + expression_new.insert(expression_new.size() - 2, "("); + expression_new += "*"; + } else { + expression_new.insert(expression_new.size() - 1, "("); + expression_new += "*"; + } + count_and++; + } + } + if (count_and > 0) { + for (i = 0; i < count_and; i++) { + expression_new += ")"; + } + } + return; + } + + for (i = 2; i < L; i++) { + for (j = 0; j < C; j++) { + if (onset[i][j] == 1) x += pow(2, onset[1][j]); + } + if (x != 0) decimalism1.insert(x); + if (x == 1) + decimalism.push_back(0); + else if (!(x & (x - 1)) && x > 0) + decimalism.push_back(0); + else + decimalism.push_back(1); + x = 0; + } + if (find(decimalism.begin(), decimalism.end(), 1) == decimalism.end() && + decimalism1.size() > 1) { + int cout_row_0 = 0; + int count_xor = 0; + + for (i = 2; i < L; i++) { + int flag = 0; + for (j = 0; j < C; j++) { + string str = ""; + if (onset[i][j] == 1) { + str = st[onset[1][j]]; + if (onset[0][j] == 0) + expression_new += str; + else { + expression_new += "!"; + expression_new += str; + } + } + } + if (i + 1 < L) { + for (j = 0; j < C; j++) { + if (onset[i + 1][j] != 0) break; + } + + if (j == C) { + cout_row_0++; + flag = 1; + } + } + + if (i < L - 1 && flag == 0) { + if (expression_new.size() >= 2 && + expression_new[expression_new.size() - 2] == '!') { + expression_new.insert(expression_new.size() - 2, "["); + expression_new += "+"; + } else { + expression_new.insert(expression_new.size() - 1, "["); + expression_new += "+"; + } + count_xor++; + } + } + if (cout_row_0 % 2 != 0) { + // cout << expression_new[expression_new.size() - 2] << endl; + if (expression_new[expression_new.size() - 2] == '+') + expression_new.insert(expression_new.size() - 1, "!"); + else if (expression_new[expression_new.size() - 2] == '!') { + string str = ""; + for (int jj = 0; jj < expression_new.size(); jj++) { + if (jj != expression_new.size() - 2) str += expression_new[jj]; + } + expression_new = str; + } + } + if (count_xor > 0) { + for (i = 0; i < count_xor; i++) { + expression_new += "]"; + } + } + + return; + } + + // for (int i = 0; i < decimalism.size(); i++) + // cout << decimalism[i] << endl; + + // Extract all 1 columns + int count_and = 0; + for (i = 0; i < C; i++) { + for (j = 2; j < L; j++) { + if (onset[j][i] != 1) break; + } + string str = ""; + if (j == L) { + str = st[onset[1][i]]; + if (onset[0][i] == 0) + expression_new += str; + else { + expression_new += "!"; + expression_new += str; + } + C--; + + if (C > 0) { + if (expression_new.size() >= 2 && + expression_new[expression_new.size() - 2] == '!') { + expression_new.insert(expression_new.size() - 2, "("); + expression_new += "*"; + } else { + expression_new.insert(expression_new.size() - 1, "("); + expression_new += "*"; + } + count_and++; + } + + for (int k = i; k < C; k++) { // Delete column i + for (int m = 0; m < L; m++) { + onset[m][k] = onset[m][k + 1]; + } + } + i--; + } + } + if (C == 0 && count_and > 0) { + for (i = 0; i < count_and; i++) { + expression_new += ")"; + } + return; + } else if (C == 0 && count_and == 0) + return; + + int cout_1 = 0, cout_0 = 0; + if (C == 1) { + for (i = 2; i < L; i++) { + if (onset[i][0] == 0) { + cout_0++; + } else + cout_1++; + } + + if (cout_0 % 2 != 0 && onset[0][0] == 0 && cout_1 % 2 != 0) { + expression_new += "!"; + expression_new += st[onset[1][0]]; + } else if (cout_0 % 2 != 0 && onset[0][0] == 0 && cout_1 % 2 == 0) + expression_new += "1"; + else if (cout_0 % 2 == 0 && onset[0][0] == 0 && cout_1 % 2 != 0) + expression_new += st[onset[1][0]]; + else if (cout_0 % 2 == 0 && onset[0][0] == 0 && cout_1 % 2 == 0) + expression_new += ""; + else if (cout_0 % 2 != 0 && onset[0][0] == 1 && cout_1 % 2 != 0) + expression_new += st[onset[1][0]]; + else if (cout_0 % 2 != 0 && onset[0][0] == 1 && cout_1 % 2 == 0) + expression_new += ""; + else if (cout_0 % 2 == 0 && onset[0][0] == 1 && cout_1 % 2 != 0) { + expression_new += "!"; + expression_new += st[onset[1][0]]; + } else if (cout_0 % 2 == 0 && onset[0][0] == 1 && cout_1 % 2 == 0) + expression_new += "1"; + + if (count_and == 0) + return; + else if (count_and > 0) { + for (i = 0; i < count_and; i++) { + expression_new += ")"; + } + return; + } + } + + int count_row_0 = 0; // Delete all 0 lines and invert any xor items + std::map> temporary; + for (i = 0; i < 2; i++) { + for (j = 0; j < C; j++) { + temporary[i][j] = onset[i][j]; + } + } + + // cout << "temporary: " << endl; + // for (i = 0; i < temporary.size(); i++) + //{ + // for (j = 0; j < temporary[i].size(); j++) + // { + // cout << temporary[i][j] << " "; + // } + // cout << endl; + // } + + int a = 2; + int flag_row_0 = 0; + for (i = 2; i < L; i++) { + for (j = 0; j < C; j++) { + if (onset[i][j] != 0) break; + } + + if (j == C) { + count_row_0++; + } else if (j != C) { + for (j = 0; j < C; j++) { + temporary[a][j] = onset[i][j]; + } + a++; + } + } + + if (count_row_0 % 2 != 0) { + expression_new += "["; + expression_new += "1"; + flag_row_0 = 1; + } + + onset.clear(); + onset = temporary; + L = onset.size(); + C = (onset[0]).size(); + + // cout << "temporary: " << endl; + // for (i = 0; i < temporary.size(); i++) + //{ + // for (j = 0; j < temporary[i].size(); j++) + // { + // cout << temporary[i][j] << " "; + // } + // cout << endl; + // } + + // Extraction of local public variables + std::map maxi; + maxi.clear(); + for (i = 0; i < C; i++) { + // Statistical maxi + maxi[i] = 0; + vector row(L - 2, ""); + int n = 0; + for (j = 2; j < L; j++) { + for (int k = 0; k < C; k++) { + if (k != i) row[n] += onset[j][k] + '0'; + } + n++; + } + for (int m = 0; m < row.size(); m++) { + int cnt = count(row.begin(), row.end(), row[m]); + int count_1 = count(row[m].begin(), row[m].end(), '1'); + int sum = cnt * count_1; + if (sum > maxi[i]) maxi[i] = sum; + } + + // cout << "-----------------------" << endl; + // for (int m = 0; m< row.size(); m++) + // cout << row[m] << endl; + // cout << maxi[i] << endl; + // cout << "-----------------------" << endl; + } + + // for (i = 0; i < C; i++) + // cout << maxi[i] << " "; + // cout << endl; + + int k = C / 2; + vector CK(k, 0); + for (i = 0; i < k; i++) { // Find CK + int max = maxi[0]; + for (j = 1; j < C; j++) { + if (maxi[j] > max) { + max = maxi[j]; + CK[i] = j; + } + } + maxi[CK[i]] = -1; + } + + // for (i = 0; i < CK.size(); i++) + // cout << "CK: " << CK[i] << endl; + + int temp; + for (i = 0; i < k; i++) { // Swap CK to onset table behind + for (j = 0; j < L; j++) { + temp = onset[j][CK[i]]; + onset[j][CK[i]] = onset[j][C - i - 1]; + onset[j][C - i - 1] = temp; + } + } + + // Looking for LK + vector onset_string( + L, ""); // Convert the onset table to a CK string stripping table + int nn = 0; + for (i = 0; i < L; i++) { + for (j = 0; j < C - k; j++) { + onset_string[nn] += onset[i][j] + '0'; + } + nn++; + } + + // for (i = 0; i < onset_string.size(); i++) + // cout << onset_string[i] << endl; + // cout << endl; + + int max = 0, index = 0; + int LK_count = 0; + for (i = 2; i < onset_string.size(); i++) { + int cnt = + count(onset_string.begin() + 2, onset_string.end(), onset_string[i]); + int count_1 = count(onset_string[i].begin(), onset_string[i].end(), '1'); + int sum = cnt * count_1; + if (sum > max) { + index = i; + max = sum; + LK_count = cnt; + } + } + + /*cout << "index: " << index << endl;*/ + + vector onset_string1( + L, ""); // Convert the onset table to the onset string table + for (i = 0; i < L; i++) { + for (j = 0; j < C; j++) { + onset_string1[i] += onset[i][j] + '0'; + } + } + + // for (i = 0; i < onset_string1.size(); i++) + // cout << onset_string1[i] << endl; + // cout << endl; + + string str = onset_string1[index].substr(0, C - k); + // cout << "str: " << str << endl; + vector temp1; // Storage LK + temp1.clear(); + for (i = 2; i < onset_string1.size(); i++) { + if (str == onset_string1[i].substr(0, C - k)) { + temp1.push_back(onset_string1[i]); + } + } + /*cout << "temp1:" << endl; + for (i = 0; i < temp1.size(); i++) + cout << temp1[i] << endl; + cout << endl;*/ + + vector temp2; // Stores other child tables + temp2.clear(); + for (i = 2; i < onset_string1.size(); i++) { + if (str != onset_string1[i].substr(0, C - k)) { + temp2.push_back(onset_string1[i]); + } + } + + /* cout << "temp2:" << endl; + for (i = 0; i < temp2.size(); i++) + cout << temp2[i] << endl; + cout << endl;*/ + + vector onset_string_new; // Stores the onset table after it has + // been transformed + onset_string_new.push_back(onset_string1[0]); + onset_string_new.push_back(onset_string1[1]); + for (i = 0; i < temp2.size(); i++) { + onset_string_new.push_back(temp2[i]); + } + for (i = 0; i < temp1.size(); i++) { + onset_string_new.push_back(temp1[i]); + } + + // for (i = 0; i < onset_string_new.size(); i++) + // cout << onset_string_new[i] << endl; + for (i = 0; i < L; i++) { + for (j = 0; j < C; j++) { + onset[i][j] = onset_string_new[i][j] - '0'; + } + } + // cout << endl; + // cout <<"LK_count: " << LK_count << endl; + std::map> onset_st1; + std::map> onset_st2; + onset_st1.clear(); + onset_st2.clear(); + + for (i = 0; i < L - LK_count; i++) // Store ST2 + { + for (j = 0; j < C; j++) { + onset_st2[i][j] = onset[i][j]; + } + } + // cout << "ST2: " << endl; + // for (i = 0; i < onset_st2.size(); i++) + //{ + // for (j = 0; j < onset_st2[i].size(); j++) + // { + // cout << onset_st2[i][j] << " "; + // } + // cout << endl; + // } + + // cout << endl; + + // Store ST1 + for (i = 0; i < 2; i++) { + for (j = 0; j < C; j++) { + onset_st1[i][j] = onset[i][j]; + } + } + + int n = 2; + for (i = L - LK_count; i < L; i++) { + for (j = 0; j < C; j++) { + onset_st1[n][j] = onset[i][j]; + } + n++; + } + + // cout << "st1: " << endl; + // for (i = 0; i < onset_st1.size(); i++) + //{ + // for (j = 0; j < onset_st1[i].size(); j++) + // { + // cout << onset_st1[i][j] << " "; + // } + // cout << endl; + // } + + // cout << endl; + + int flag1 = 0; + for (i = 2; i < L; i++) { + for (j = 0; j < C; j++) { + if (onset_st1[i][j] == 1) { + flag1 = 1; + break; + } + } + if (flag1 == 1) break; + } + int flag2 = 0; + for (i = 2; i < L; i++) { + for (j = 0; j < C; j++) { + if (onset_st2[i][j] == 1) { + flag2 = 1; + break; + } + } + if (flag2 == 1) break; + } + + if (flag1 == 1 && flag2 == 1 && onset_st1.size() > 2 && + onset_st2.size() > 2) + expression_new += "["; + if (flag1 == 1) pw_opt_by_onset(onset_st1); + if (flag1 == 1 && flag2 == 1 && onset_st1.size() > 2 && + onset_st2.size() > 2) + expression_new += "+"; + if (flag2 == 1) pw_opt_by_onset(onset_st2); + if (flag1 == 1 && flag2 == 1 && onset_st1.size() > 2 && + onset_st2.size() > 2) + expression_new += "]"; + + if (flag_row_0 == 1) expression_new += "]"; + + if (count_and > 0) { + for (i = 0; i < count_and; i++) { + expression_new += ")"; + } + } + + // cout << "------------------------------" << endl; + // for (int i = 0; i < L; i++) + //{ + // for (int j = 0; j < C; j++) + // { + // cout << onset[i][j] << " "; + // } + // cout << endl; + // } + // cout << "------------------------------" << endl; + } + /**************************************************************************************************************/ + void adjust_the_expression() { + vector symbol; + for (int i = 0; i < expression_new.size(); i++) { + if (expression_new[i] == '*') { + continue; + } else if (expression_new[i] == '+') { + continue; + } else if (expression_new[i] == '-') { + continue; + } else { + expression_adjusted += expression_new[i]; + } + } + } + /**************************************************************************************************************/ + /* 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 nodes in the new network. */ + int count_the_number_of_nodes(int& variate_num, vector& 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; + } + /**************************************************************************************************************/ + /* 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> const& children) { + std::vector pis; + pis.clear(); + pis.push_back(ntk.get_constant(true)); + for (const auto& l : children) { + pis.push_back(l); + } + + std::stack inputs; + int flag = 0; + + for (auto i = 0; i < s.size(); i++) { + if (s[i] == '[') { + continue; + } else if (s[i] == '(') { + continue; + } else if (s[i] == '1') { + inputs.push(pis[0]); + } else if (s[i] >= 'a') { + if (flag == 1) { + inputs.push(ntk.create_not(pis[s[i] - 'a' + 1])); + flag = 0; + } else + inputs.push(pis[s[i] - 'a' + 1]); + } 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] == ']') { + 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; + } + } + if (s.size() == 0) inputs.push(ntk.get_constant(false)); + assert(inputs.size() == 1u); + return inputs.top(); + } + /**************************************************************************************************************/ + /* List all possible variable values/List truth table. */ + vector list_truth_table(int& variate_num) { + int a = pow(2, variate_num); + vector b; + for (int i = 0; i < a; i++) { + b.push_back(i); + } + vector 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 list_all_polarities(int& variate_num) { + int a_1 = pow(2, variate_num); + vector c; + for (int i = 0; i < a_1; i++) { + c.push_back(i); + } + vector 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 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 polarity_conversion(int& variate_num, vector& minterm, + string& polarity) { + vector 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 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 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& minterm, + vector& polarity, int& variate_num, + vector& RM_product, + string& optimal_polarity) { + int minimum = INT_MAX; + vector RM_product_initial; + for (int l = 0; l < polarity.size(); l++) { + RM_product_initial = + polarity_conversion(variate_num, minterm, polarity[l]); + + int count = 0; + count = count_the_number_of_nodes(variate_num, RM_product_initial, + polarity[l]); + + if (count < minimum) { + optimal_polarity = polarity[l]; + RM_product.clear(); + RM_product = RM_product_initial; + RM_product_initial.clear(); + minimum = count; + } else { + RM_product_initial.clear(); + } + } + } + /**************************************************************************************************************/ + /* Create expression. */ + string create_expression(vector& RM_product, int& variate_num, + string& optimal_polarity, vector& 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; + } + /**************************************************************************************************************/ + /* Cut is used to divide the network. */ + void ntk_cut() { + /* enumerate cuts */ + const auto cuts = + cut_enumeration( + ntk, ps.cut_enumeration_ps); + + /* for cost estimation we use reference counters initialized by the fanout + * size */ + ntk.clear_values(); + ntk.foreach_node( + [&](auto const& n) { ntk.set_value(n, ntk.fanout_size(n)); }); + + /* store best replacement for each cut */ + node_map>, Ntk> best_replacements(ntk); + + /* iterate over all original nodes in the network */ + const auto size = ntk.size(); + auto max_total_gain = 0u; + progress_bar pbar{ntk.size(), + "rm_optimization_by_cut |{0}| node = {1:>4}@{2:>2} / " + + std::to_string(size) + " comm. gain = {3}", + ps.progress}; + ntk.foreach_node([&](auto const& 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 */ + int a = 0; + int32_t best_gain = -1; + int32_t best_cut = 0; + signal best_signal; + for (auto& cut : cuts.cuts(ntk.node_to_index(n))) { + a++; + /* skip trivial cuts */ + if (cut->size() < ps.min_cand_cut_size) continue; + + pbar(index, ntk.node_to_index(n), best_replacements[n].size(), + max_total_gain); + + std::vector> children; + for (auto l : *cut) { + children.push_back(ntk.make_signal(ntk.index_to_node(l))); + } + + and_node_deref = 0; + int32_t value = recursive_deref1(ntk, n); + { + string tt = to_binary(cuts.truth_table(*cut)); + + 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 binary = list_truth_table(variate_num); + + /* Stores the output of the truth table. */ + vector c_out; + for (int i = len - 1; i >= 0; i--) { + c_out.push_back(tt[i]); + } + + vector c_out1; + /* Minimum item for storing binary. */ + vector 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 polarity = list_all_polarities(variate_num); + + /* Storage optimal polarity. */ + string optimal_polarity; + /* Mixed polarity conversion algorithm based on list technique. */ + vector RM_product; + + /* Search for the optimal polarity and the corresponding product + * term.*/ + search_for_optimal_polarity(minterm, polarity, variate_num, + RM_product, optimal_polarity); + + int pos = optimal_polarity.find("2"); + string expression = ""; + if (pos != optimal_polarity.npos) { + expression = create_expression(RM_product, variate_num, + optimal_polarity, binary); + } else { + onset.clear(); // 初始化onset表 + for (int i = 0; i < optimal_polarity.size(); i++) + onset[0][i] = optimal_polarity[i] - '0'; + + for (int i = 0; i < variate_num; i++) + onset[1][i] = variate_num - i - 1; + + for (int i = 0; i < RM_product.size(); i++) { + for (int j = 0; j < variate_num; j++) { + onset[i + 2][j] = RM_product[i][j] - '0'; + } + } + + // cout<<"-----------------------------------"<(ntk, ntk.get_node(f_new)); + + int32_t gain = contains ? -1 : value - v; + int32_t and_node_num = and_node_deref - and_node_ref; + + if (ps_ntk.multiplicative_complexity == true) { + if (((gain >= 0) || (ps.allow_zero_gain && gain == 0)) && + gain > best_gain && and_node_num >= 0) { + best_gain = gain; + best_signal = f_new; + best_cut = a; + // best_replacements[n].push_back( f_new ); + } + } else { + if (((gain > 0) || (ps.allow_zero_gain && gain == 0)) && + gain > best_gain) { + best_gain = gain; + best_signal = f_new; + best_cut = a; + // best_replacements[n].push_back( f_new ); + } + } + + if (best_gain > 0) { + max_total_gain += best_gain; + } + } + + recursive_ref(ntk, n); + } + + if (best_gain != -1) { + int b = 0; + for (auto& cut : cuts.cuts(ntk.node_to_index(n))) { + b++; + /* skip trivial cuts */ + if (cut->size() < ps.min_cand_cut_size) continue; + + if (b == best_cut) { + (*cut)->data.gain = best_gain; + best_replacements[n].push_back(best_signal); + } + } + } + + return true; + }); + + auto [g, map] = network_cuts_graph(ntk, cuts, ps); + + const auto is = (ps.candidate_selection_strategy == + cut_rewriting_params::minimize_weight) + ? maximum_weighted_independent_set_gwmin(g) + : maximal_weighted_independent_set(g); + + for (const auto v : is) { + const auto v_node = map[v].first; + const auto v_cut = map[v].second; + + if (best_replacements[v_node].empty()) continue; + + const auto replacement = best_replacements[v_node][v_cut]; + + if (ntk.is_constant(ntk.get_node(replacement)) || + v_node == ntk.get_node(replacement)) + continue; + + if (!ntk.is_dead(ntk.get_node(replacement))) { + ntk.substitute_node(v_node, replacement); + } + } + } + /**************************************************************************************************************/ + /* mffc is used to divide the network. */ + void ntk_mffc() { + progress_bar pbar{ntk.size(), + "rm_optimization_by_mffc |{0}| node = {1:>4} cand = " + "{2:>4} est. reduction = {3:>5}", + ps.progress}; + + ntk.clear_visited(); + ntk.clear_values(); + ntk.foreach_node( + [&](auto const& n) { ntk.set_value(n, ntk.fanout_size(n)); }); + + const auto size = ntk.num_gates(); + ntk.foreach_gate([&](auto const& n, auto i) { + if (i >= size) { + return false; + } + if (ntk.fanout_size(n) == 0u) { + return true; + } + mffc_view mffc{ntk, n}; + + pbar(i, i, _candidates, _estimated_gain); + + if (mffc.num_pos() == 0 || mffc.num_pis() > 6) { + return true; + } + + std::vector> leaves(mffc.num_pis()); + mffc.foreach_pi( + [&](auto const& m, auto j) { leaves[j] = ntk.make_signal(m); }); + + default_simulator sim(mffc.num_pis()); + string tt = to_binary(simulate(mffc, sim)[0]); + + 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 binary = list_truth_table(variate_num); + + /* Stores the output of the truth table. */ + vector c_out; + for (int i = len - 1; i >= 0; i--) { + c_out.push_back(tt[i]); + } + + vector c_out1; + /* Minimum item for storing binary. */ + vector 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 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 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); + + int pos = optimal_polarity.find("2"); + string expression = ""; + if (pos != optimal_polarity.npos) { + expression = create_expression(RM_product, variate_num, + optimal_polarity, binary); + } else { + onset.clear(); // 初始化onset表 + for (int i = 0; i < optimal_polarity.size(); i++) + onset[0][i] = optimal_polarity[i] - '0'; + + for (int i = 0; i < variate_num; i++) onset[1][i] = variate_num - i - 1; + + for (int i = 0; i < RM_product.size(); i++) { + for (int j = 0; j < variate_num; j++) { + onset[i + 2][j] = RM_product[i][j] - '0'; + } + } + + expression_new = ""; + pw_opt_by_onset(onset); + expression_adjusted = ""; + adjust_the_expression(); + expression = expression_adjusted; + } + + auto f_new = create_ntk_from_str(expression, leaves); + + and_node_deref = 0; + int32_t value = recursive_deref1(ntk, n); + + and_node_ref = 0; + auto value2 = recursive_ref1(ntk, ntk.get_node(f_new)); + + int32_t gain = value - value2; + int32_t and_node_num = and_node_deref - and_node_ref; + + if (ps_ntk.multiplicative_complexity == true) { + if ((gain > 0 && and_node_num >= 0)) { + ++_candidates; + _estimated_gain += gain; + ntk.substitute_node(n, f_new); + + ntk.set_value(n, 0); + ntk.set_value(ntk.get_node(f_new), + ntk.fanout_size(ntk.get_node(f_new))); + for (auto i = 0u; i < leaves.size(); i++) { + ntk.set_value(ntk.get_node(leaves[i]), + ntk.fanout_size(ntk.get_node(leaves[i]))); + } + } else { + recursive_deref(ntk, ntk.get_node(f_new)); + recursive_ref(ntk, n); + } + } else { + if ((gain > 0)) { + ++_candidates; + _estimated_gain += gain; + ntk.substitute_node(n, f_new); + + ntk.set_value(n, 0); + ntk.set_value(ntk.get_node(f_new), + ntk.fanout_size(ntk.get_node(f_new))); + for (auto i = 0u; i < leaves.size(); i++) { + ntk.set_value(ntk.get_node(leaves[i]), + ntk.fanout_size(ntk.get_node(leaves[i]))); + } + } else { + recursive_deref(ntk, ntk.get_node(f_new)); + recursive_ref(ntk, n); + } + } + + return true; + }); + } + + /**************************************************************************************************************/ + private: + int and_node_deref = 0; + int and_node_ref = 0; + string expression_new = ""; + string expression_adjusted = ""; + string st = "abcdefghijklmnopqrstuvwxyz"; + std::map> onset; + Ntk& ntk; + rm_rewriting_params2 const& ps_ntk; + cut_rewriting_params const& ps; + + uint32_t _candidates{0}; + uint32_t _estimated_gain{0}; +}; +} // 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 > +void rm_mixed_polarity2(Ntk& ntk, rm_rewriting_params2 const& ps_ntk = {}, + cut_rewriting_params const& ps = {}) { + static_assert(is_network_type_v, "Ntk is not a network type"); + 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"); + 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"); + static_assert(has_clear_values_v, + "Ntk does not implement the clear_values method"); + static_assert(has_incr_value_v, + "Ntk does not implement the incr_value method"); + static_assert(has_decr_value_v, + "Ntk does not implement the decr_value method"); + static_assert(has_set_value_v, + "Ntk does not implement the set_value 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_substitute_node_v, + "Ntk does not implement the substitute_node method"); + static_assert(has_make_signal_v, + "Ntk does not implement the make_signal method"); + + detail::rm_mixed_polarity_impl2 p(ntk, ps_ntk, ps); + p.run(); +} + +} /* namespace mockturtle */ diff --git a/src/core/xag_rewriting.hpp b/src/core/xag_rewriting.hpp new file mode 100644 index 0000000..dc2aa50 --- /dev/null +++ b/src/core/xag_rewriting.hpp @@ -0,0 +1,678 @@ +#pragma once + +#include +#include + +#include + +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 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 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 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 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 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 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 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 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!(!a!b)]= (!ab) [!a!(!a!b)]= !(!ab)*/ + bool reduce_depth_use_xor_or_convert( node 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 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& a, const signal& b ) + { + return ( a == b ? true : false ); + } +/**************************************************************************************************************/ + /* used for the subsquent judgement of the common child-node. */ + using children_t = std::array< signal, 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 equal_idx( const std::bitset<4> pattern ) + { + std::vector 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 x; + signal y; + signal a; //common grand child a + }; +/**************************************************************************************************************/ + std::optional 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> 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, 2> ordered_children( node const& n ) const + { + std::array, 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 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 +void xag_depth_rewriting( Ntk& ntk, xag_depth_rewriting_params const& ps = {} ) +{ + 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_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node 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_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + 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_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + detail::xag_depth_rewriting_impl p( ntk, ps ); + p.run(); +} + +} /* namespace mockturtle */ diff --git a/src/core/xmg_expand.hpp b/src/core/xmg_expand.hpp new file mode 100644 index 0000000..985ab87 --- /dev/null +++ b/src/core/xmg_expand.hpp @@ -0,0 +1,170 @@ +#pragma once + +#include +#include + +#include + +namespace mockturtle +{ + struct xmg_expand_rewriting_params + { + enum strategy_t + { + expand, + constants + } strategy = expand; + + std::vector xor_index; + }; + +namespace detail +{ +template +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, 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 const& n, std::vector 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 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, 3> ordered_children( node const& n ) const + { + std::array, 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 +void xmg_expand_rewriting( Ntk& ntk, xmg_expand_rewriting_params const& ps ) +{ + detail::xmg_expand_rewriting_impl p( ntk, ps ); + p.run(); +} + +} /* namespace mockturtle */ diff --git a/src/core/xmg_extract.hpp b/src/core/xmg_extract.hpp new file mode 100644 index 0000000..3e7e154 --- /dev/null +++ b/src/core/xmg_extract.hpp @@ -0,0 +1,66 @@ +#ifndef XMG_EXTRACT_HPP +#define XMG_EXTRACT_HPP + +#include +#include + +#include "utils.hpp" + +using namespace mockturtle; + +namespace phyLS +{ + + std::vector xmg_extract( xmg_network const& xmg ) + { + std::vector 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 diff --git a/src/core/xmg_rewriting.hpp b/src/core/xmg_rewriting.hpp new file mode 100644 index 0000000..74ee5d2 --- /dev/null +++ b/src/core/xmg_rewriting.hpp @@ -0,0 +1,925 @@ +#pragma once + +#include +#include + +#include + +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 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 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 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 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 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, signal, signal, bool>; + std::optional associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal 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 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 = */ + bool reduce_depth_xor_complementary_associativity( node 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 xor_compl_associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal 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 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 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& a, const signal& b ) + { + return ( a == b ? true : false ); + } + + bool is_three_signal_equal( const signal& a, const signal& b, const signal& c ) + { + return ( a == b ? ( b == c ? true : false ): false ); + } + + using children_t = std::array< signal, 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 x; + signal y; + signal z; + signal a; //common grand child a + signal b; //common grand child b + }; + + std::vector equal_idx( const std::bitset<9> pattern ) + { + std::vector 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 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> 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 find_common_grand_child_two( const signal& v, const children_t& c1, const children_t& c2 ) + { + grand_children_pair_t r; + auto p = get_pair_pattern( c1, c2 ); + + std::vector> 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, 3> ordered_children( node const& n ) const + { + std::array, 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 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 +void xmg_depth_rewriting( Ntk& ntk, xmg_depth_rewriting_params const& ps = {} ) +{ + 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_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node 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_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_maj method" ); + 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_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + detail::xmg_depth_rewriting_impl p( ntk, ps ); + p.run(); +} + +} /* namespace mockturtle */ diff --git a/src/phyLS.cpp b/src/phyLS.cpp index 4deb51c..4245e7d 100644 --- a/src/phyLS.cpp +++ b/src/phyLS.cpp @@ -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) \ No newline at end of file