From 0f6ca59029006891481ce82a06f056380a59114b Mon Sep 17 00:00:00 2001 From: Franz Reichl Date: Tue, 14 Apr 2026 15:10:06 +0200 Subject: [PATCH] Extend the eSLIM package --- abclib.dsp | 54 +- src/base/abci/abc.c | 345 +++++++- src/opt/eslim/areaEngine.cpp | 163 ++++ src/opt/eslim/areaEngine.hpp | 63 ++ src/opt/eslim/cadicalSolver.hpp | 218 +++++ src/opt/eslim/delayEngine.cpp | 252 ++++++ src/opt/eslim/delayEngine.hpp | 81 ++ src/opt/eslim/eSLIM.cpp | 663 ++++++++++++--- src/opt/eslim/eSLIM.h | 43 +- src/opt/eslim/eSLIMMan.hpp | 54 +- src/opt/eslim/eSLIMMan.tpp | 461 ++-------- src/opt/eslim/eslimCirMan.cpp | 1121 +++++++++++++++++++++++++ src/opt/eslim/eslimCirMan.hpp | 234 ++++++ src/opt/eslim/module.make | 9 +- src/opt/eslim/relationGeneration.cpp | 528 +++++++++--- src/opt/eslim/relationGeneration.hpp | 228 ++--- src/opt/eslim/relationSynthesiser.cpp | 612 ++++++++++++++ src/opt/eslim/relationSynthesiser.hpp | 145 ++++ src/opt/eslim/satInterfaces.hpp | 196 ----- src/opt/eslim/selectionStrategies.hpp | 181 ++++ src/opt/eslim/selectionStrategies.tpp | 226 +++++ src/opt/eslim/selectionStrategy.hpp | 154 ---- src/opt/eslim/selectionStrategy.tpp | 233 ----- src/opt/eslim/subcircuit.cpp | 132 +++ src/opt/eslim/subcircuit.hpp | 80 ++ src/opt/eslim/synthesisEngine.hpp | 240 ------ src/opt/eslim/synthesisEngine.tpp | 632 -------------- src/opt/eslim/synthesisEngines.hpp | 115 +++ src/opt/eslim/tabooList.hpp | 178 ++++ src/opt/eslim/utils.hpp | 182 +++- src/opt/eslim/windowMan.hpp | 79 ++ src/opt/eslim/windowMan.tpp | 221 +++++ 32 files changed, 5723 insertions(+), 2400 deletions(-) create mode 100644 src/opt/eslim/areaEngine.cpp create mode 100644 src/opt/eslim/areaEngine.hpp create mode 100644 src/opt/eslim/cadicalSolver.hpp create mode 100644 src/opt/eslim/delayEngine.cpp create mode 100644 src/opt/eslim/delayEngine.hpp create mode 100644 src/opt/eslim/eslimCirMan.cpp create mode 100644 src/opt/eslim/eslimCirMan.hpp create mode 100644 src/opt/eslim/relationSynthesiser.cpp create mode 100644 src/opt/eslim/relationSynthesiser.hpp delete mode 100644 src/opt/eslim/satInterfaces.hpp create mode 100644 src/opt/eslim/selectionStrategies.hpp create mode 100644 src/opt/eslim/selectionStrategies.tpp delete mode 100644 src/opt/eslim/selectionStrategy.hpp delete mode 100644 src/opt/eslim/selectionStrategy.tpp create mode 100644 src/opt/eslim/subcircuit.cpp create mode 100644 src/opt/eslim/subcircuit.hpp delete mode 100644 src/opt/eslim/synthesisEngine.hpp delete mode 100644 src/opt/eslim/synthesisEngine.tpp create mode 100644 src/opt/eslim/synthesisEngines.hpp create mode 100644 src/opt/eslim/tabooList.hpp create mode 100644 src/opt/eslim/windowMan.hpp create mode 100644 src/opt/eslim/windowMan.tpp diff --git a/abclib.dsp b/abclib.dsp index 321b851dfc..d272b819ea 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -4194,6 +4194,26 @@ SOURCE=.\src\opt\sbd\sbdWin.c # PROP Default_Filter "" # Begin Source File +SOURCE=.\src\opt\eslim\areaEngine.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\areaEngine.hpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\cadicalSolver.hpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\delayEngine.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\delayEngine.hpp +# End Source File +# Begin Source File + SOURCE=.\src\opt\eslim\eSLIM.cpp # End Source File # Begin Source File @@ -4202,6 +4222,14 @@ SOURCE=.\src\opt\eslim\eSLIM.h # End Source File # Begin Source File +SOURCE=.\src\opt\eslim\eslimCirMan.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\eslimCirMan.hpp +# End Source File +# Begin Source File + SOURCE=.\src\opt\eslim\eSLIMMan.hpp # End Source File # Begin Source File @@ -4214,11 +4242,31 @@ SOURCE=.\src\opt\eslim\relationGeneration.hpp # End Source File # Begin Source File -SOURCE=.\src\opt\eslim\satInterfaces.hpp +SOURCE=.\src\opt\eslim\relationSynthesiser.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\relationSynthesiser.hpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\selectionStrategies.hpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\subcircuit.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\subcircuit.hpp +# End Source File +# Begin Source File + +SOURCE=.\src\opt\eslim\synthesisEngines.hpp # End Source File # Begin Source File -SOURCE=.\src\opt\eslim\selectionStrategy.hpp +SOURCE=.\src\opt\eslim\tabooList.hpp # End Source File # Begin Source File @@ -4226,7 +4274,7 @@ SOURCE=.\src\opt\eslim\utils.hpp # End Source File # Begin Source File -SOURCE=.\src\opt\eslim\synthesisEngine.hpp +SOURCE=.\src\opt\eslim\windowMan.hpp # End Source File # End Group # End Group diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 74fc6e356f..998b3e16bc 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -665,6 +665,7 @@ static int Abc_CommandAbc9Divide ( Abc_Frame_t * pAbc, int argc, cha static int Abc_CommandAbc9Test ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9eSLIM ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAbc9elSLIM ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9CatBtor ( Abc_Frame_t * pAbc, int argc, char ** argv ); @@ -1517,6 +1518,7 @@ void Abc_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "ABC9", "&test", Abc_CommandAbc9Test, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&eslim", Abc_CommandAbc9eSLIM, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "elslim", Abc_CommandAbc9elSLIM, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&catbtor", Abc_CommandAbc9CatBtor, 0 ); { @@ -59976,18 +59978,43 @@ int Abc_CommandAbc9eSLIM( Abc_Frame_t * pAbc, int argc, char ** argv ) { int c; Gia_Man_t * pTemp; seteSLIMParams(¶ms); + params.aig = 1; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "DIMPRSTVZdfhns" ) ) != EOF ) { + while ( ( c = Extra_UtilGetopt( argc, argv, "CDIPRSTVWXZcfhistx" ) ) != EOF ) { switch ( c ) { + case 'C': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-C\" should be followed by an integer.\n" ); + goto usage; + } + params.approximate_relation = 1; + params.relation_tfo_bound = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.relation_tfo_bound < 0 ) + goto usage; + if (globalUtilOptind < argc) { + char *next; + int tfi_bound = strtol (argv[globalUtilOptind], &next, 10); + if (argv[globalUtilOptind] != next && *next == '\0') { + params.generate_relation_with_tfi_limit = 1; + params.relation_tfi_bound = tfi_bound; + globalUtilOptind++; + if (params.relation_tfi_bound < 0) { + goto usage; + } + } + } + break; case 'D': if ( globalUtilOptind >= argc ) { Abc_Print( -1, "Command line switch \"-D\" should be followed by an integer.\n" ); goto usage; } - params.timeout_inprocessing = atoi(argv[globalUtilOptind]); + params.synthesis_approach = atoi(argv[globalUtilOptind]); globalUtilOptind++; - if ( params.timeout_inprocessing < 1 ) + if ( params.synthesis_approach < 0 || params.synthesis_approach > 2) goto usage; break; case 'I': @@ -60001,15 +60028,231 @@ int Abc_CommandAbc9eSLIM( Abc_Frame_t * pAbc, int argc, char ** argv ) { if ( params.iterations < 0 ) goto usage; break; - case 'M': + case 'P': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-P\" should be followed by a float.\n" ); + goto usage; + } + params.expansion_probability = atof(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.expansion_probability <= 0 || params.expansion_probability > 1) + goto usage; + break; + case 'R': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-R\" should be followed by an integer.\n" ); + goto usage; + } + params.nruns = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.nruns < 1 ) + goto usage; + break; + case 'S': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-S\" should be followed by an integer.\n" ); + goto usage; + } + params.subcircuit_max_size = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.subcircuit_max_size < 2 ) + goto usage; + break; + case 'T': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-T\" should be followed by an integer.\n" ); + goto usage; + } + params.timeout = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.timeout < 1 ) + goto usage; + break; + case 'V': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-V\" should be followed by an integer.\n" ); + goto usage; + } + params.verbosity_level = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.verbosity_level < 0 || params.verbosity_level > 3 ) + goto usage; + break; + case 'W': + if ( globalUtilOptind >= argc + 1) + { + Abc_Print( -1, "Command line switch \"-W\" should be followed by two integers.\n" ); + goto usage; + } + params.nWindows = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if (params.nWindows < 0) + goto usage; + params.window_size = atoi(argv[globalUtilOptind]); + if (params.window_size <= 0) + goto usage; + globalUtilOptind++; + break; + case 'X': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-X\" should be followed by an integer.\n" ); + goto usage; + } + params.additional_gates = atoi(argv[globalUtilOptind]); + if ( params.additional_gates < 0 ) + goto usage; + globalUtilOptind++; + break; + case 'Z': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-Z\" should be followed by an integer.\n" ); + goto usage; + } + params.fix_seed = 1; + params.seed = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + break; + case 'c' : + params.criticial_path_selection_bias ^= 1; + break; + case 'f' : + params.forward_search ^= 1; + break; + case 'h': + goto usage; + case 'i' : + params.apply_inprocessing ^= 1; + break; + case 's' : + params.fill_subcircuits ^= 1; + break; + case 't' : + params.use_taboo_list ^= 1; + break; + case 'x' : + params.aig = 0; + break; + default: + goto usage; + } + } + if (params.nWindows > 0 && params.synthesis_approach != 0) { + Abc_Print( -1, "Windows can only be used with area minimization\n" ); + return 1; + } + if ( pAbc->pGia == NULL ) { + Abc_Print( -1, "Abc_CommandAbc9Test(): There is no AIG.\n" ); + return 1; + } + + pTemp = applyeSLIM(pAbc->pGia, ¶ms); + + Abc_FrameUpdateGia( pAbc, pTemp ); + return 0; + + + usage: + Abc_Print( -2, "usage: &eslim [-CDIPRSTVWXZ ] [-cfhistx]\n" ); + Abc_Print( -2, "\t circuit optimization using exact synthesis and the SAT-based local improvement method (SLIM)\n" ); + Abc_Print( -2, "\t-C : approximate Boolean relations by only considering the frist C levels in the cone \n"); + Abc_Print( -2, "\t-D : the delay mode to use [default = %d]\n", params.synthesis_approach ); + Abc_Print( -2, "\t-I : the maximal number of iterations (0 = no limit) for the individual eSLIM runs [default = %d]\n", params.iterations ); + Abc_Print( -2, "\t-P : the probability of expanding a node [default = %.2f]\n", params.expansion_probability ); + Abc_Print( -2, "\t-R : the number of runs of eSLIM + Inprocessing [default = %d]\n", params.nruns ); + Abc_Print( -2, "\t-S : the maximal size of considered subcircuits [default = %d]\n", params.subcircuit_max_size ); + Abc_Print( -2, "\t-T : the timeout in seconds for the individual eSLIM runs [default = %d]\n", params.timeout ); + Abc_Print( -2, "\t-V : the verbosity level [default = %d]\n", params.verbosity_level); + Abc_Print( -2, "\t-W : Use m windows of size n [default = %d, %d]\n", params.nWindows, params.window_size); + Abc_Print( -2, "\t-X : the maximal number of additional gates that may be used for depth optimization [default = %d]\n", params.additional_gates); + Abc_Print( -2, "\t-Z : use a fixed seed\n", params.seed); + Abc_Print( -2, "\t-c : toggle bias for selection of nodes on the crictical path\n"); + Abc_Print( -2, "\t-f : toggle forward expansion of root nodes\n"); + Abc_Print( -2, "\t-h : print the command usage\n"); + Abc_Print( -2, "\t-i : toggle inprocessing\n"); + Abc_Print( -2, "\t-s : toggle fill subcircuits\n"); + Abc_Print( -2, "\t-t : toggle use taboo list\n"); + Abc_Print( -2, "\t-x : allow xor-gates\n"); + Abc_Print( -2, "\t\n" ); + Abc_Print( -2, "\t This command was contributed by Franz-Xaver Reichl from University of Freiburg.\n" ); + + return 1; + +} + +int Abc_CommandAbc9elSLIM( Abc_Frame_t * pAbc, int argc, char ** argv ) { + extern void seteSLIMParams(eSLIM_ParamStruct* params); + extern Abc_Ntk_t* applyelSLIM(Abc_Ntk_t * pGia, const eSLIM_ParamStruct* params); + + eSLIM_ParamStruct params; + Abc_Ntk_t* pNtk, *pNtknew; + int c; + seteSLIMParams(¶ms); + Extra_UtilGetoptReset(); + params.subcircuit_max_size = 4; + while ( ( c = Extra_UtilGetopt( argc, argv, "CDGIPRSTVWXZfhist" ) ) != EOF ) { + switch ( c ) { + case 'C': if ( globalUtilOptind >= argc ) { - Abc_Print( -1, "Command line switch \"-M\" should be followed by an integer.\n" ); + Abc_Print( -1, "Command line switch \"-C\" should be followed by an integer.\n" ); goto usage; } - params.mode = atoi(argv[globalUtilOptind]); + params.approximate_relation = 1; + params.relation_tfo_bound = atoi(argv[globalUtilOptind]); globalUtilOptind++; - if ( params.mode < 0 || params.mode > 2) + if ( params.relation_tfo_bound < 0 ) + goto usage; + if (globalUtilOptind < argc) { + char *next; + int tfi_bound = strtol (argv[globalUtilOptind], &next, 10); + if (argv[globalUtilOptind] != next && *next == '\0') { + params.generate_relation_with_tfi_limit = 1; + params.relation_tfi_bound = tfi_bound; + globalUtilOptind++; + if (params.relation_tfi_bound < 0) { + goto usage; + } + } + } + break; + case 'D': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-D\" should be followed by an integer.\n" ); + goto usage; + } + params.synthesis_approach = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.synthesis_approach < 0 || params.synthesis_approach > 2) + goto usage; + break; + case 'I': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-I\" should be followed by an integer.\n" ); + goto usage; + } + params.iterations = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.iterations < 0 ) + goto usage; + break; + case 'G': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-G\" should be followed by an integer.\n" ); + goto usage; + } + params.gate_size = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if ( params.gate_size < 1 || params.gate_size > 6 ) goto usage; break; case 'P': @@ -60040,9 +60283,9 @@ int Abc_CommandAbc9eSLIM( Abc_Frame_t * pAbc, int argc, char ** argv ) { Abc_Print( -1, "Command line switch \"-S\" should be followed by an integer.\n" ); goto usage; } - params.subcircuit_size_bound = atoi(argv[globalUtilOptind]); + params.subcircuit_max_size = atoi(argv[globalUtilOptind]); globalUtilOptind++; - if ( params.subcircuit_size_bound < 2 ) + if ( params.subcircuit_max_size < 2 ) goto usage; break; case 'T': @@ -60067,6 +60310,32 @@ int Abc_CommandAbc9eSLIM( Abc_Frame_t * pAbc, int argc, char ** argv ) { if ( params.verbosity_level < 0 || params.verbosity_level > 3 ) goto usage; break; + case 'W': + if ( globalUtilOptind >= argc + 1) + { + Abc_Print( -1, "Command line switch \"-W\" should be followed by two integers.\n" ); + goto usage; + } + params.nWindows = atoi(argv[globalUtilOptind]); + globalUtilOptind++; + if (params.nWindows < 0) + goto usage; + params.window_size = atoi(argv[globalUtilOptind]); + if (params.window_size <= 0) + goto usage; + globalUtilOptind++; + break; + case 'X': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-X\" should be followed by an integer.\n" ); + goto usage; + } + params.additional_gates = atoi(argv[globalUtilOptind]); + if ( params.additional_gates < 0 ) + goto usage; + globalUtilOptind++; + break; case 'Z': if ( globalUtilOptind >= argc ) { @@ -60077,54 +60346,76 @@ int Abc_CommandAbc9eSLIM( Abc_Frame_t * pAbc, int argc, char ** argv ) { params.seed = atoi(argv[globalUtilOptind]); globalUtilOptind++; break; - case 'd' : - params.apply_inprocessing ^= 1; + case 'c' : + params.criticial_path_selection_bias ^= 1; break; case 'f' : - params.forbidden_pairs ^= 1; + params.forward_search ^= 1; break; case 'h': goto usage; - case 'n' : - params.extended_normality_processing ^= 1; + case 'i' : + params.apply_inprocessing ^= 1; break; case 's' : params.fill_subcircuits ^= 1; break; + case 't' : + params.use_taboo_list ^= 1; + break; default: goto usage; } } - if ( pAbc->pGia == NULL ) { - Abc_Print( -1, "Abc_CommandAbc9Test(): There is no AIG.\n" ); - return 1; + + pNtk = Abc_FrameReadNtk(pAbc); + if ( pNtk == NULL ) + { + Abc_Print( -1, "Empty network.\n" ); + return 1; + } + if ( !Abc_NtkIsLogic(pNtk) ) + { + Abc_Print( -1, "This command can only be applied to a logic network.\n" ); + return 1; + } + if (!Abc_NtkHasSop(pNtk)) { + Abc_NtkToSop( pNtk, -1, ABC_INFINITY ); } - pTemp = applyeSLIM(pAbc->pGia, ¶ms); + + params.aig = 0; + pNtknew = applyelSLIM(pNtk, ¶ms); + Abc_FrameReplaceCurrentNetwork( pAbc, pNtknew ); - Abc_FrameUpdateGia( pAbc, pTemp ); return 0; usage: - Abc_Print( -2, "usage: &eslim [-DIMPRSTVZ ] [-dfhns]\n" ); - Abc_Print( -2, "\t circuit minimization using exact synthesis and the SAT-based local improvement method (SLIM)\n" ); - Abc_Print( -2, "\t-D : the timeout in seconds for the individual deepsyn runs [default = %d]\n", params.timeout_inprocessing ); + Abc_Print( -2, "usage: elslim [-CDGIPRSTVWXZ ] [-cfhist]\n" ); + Abc_Print( -2, "\t Lut optimization using exact synthesis and the SAT-based local improvement method (SLIM)\n" ); + Abc_Print( -2, "\t-C : approximate Boolean relations by only considering the frist C levels in the cone \n"); + Abc_Print( -2, "\t-D : the delay mode to use [default = %d]\n", params.synthesis_approach ); + Abc_Print( -2, "\t-G : the maximal number of fanins gates may use (at most 6) [default = %d]\n", params.gate_size ); Abc_Print( -2, "\t-I : the maximal number of iterations (0 = no limit) for the individual eSLIM runs [default = %d]\n", params.iterations ); - Abc_Print( -2, "\t-M : the synthesis mode to use [default = %d]\n", params.mode ); Abc_Print( -2, "\t-P : the probability of expanding a node [default = %.2f]\n", params.expansion_probability ); Abc_Print( -2, "\t-R : the number of runs of eSLIM + Inprocessing [default = %d]\n", params.nruns ); - Abc_Print( -2, "\t-S : the maximal size of considered subcircuits [default = %d]\n", params.subcircuit_size_bound ); + Abc_Print( -2, "\t-S : the maximal size of considered subcircuits [default = %d]\n", params.subcircuit_max_size ); Abc_Print( -2, "\t-T : the timeout in seconds for the individual eSLIM runs [default = %d]\n", params.timeout ); Abc_Print( -2, "\t-V : the verbosity level [default = %d]\n", params.verbosity_level); + Abc_Print( -2, "\t-W : Use m windows of size n [default = %d, %d]\n", params.nWindows, params.window_size); + Abc_Print( -2, "\t-X : the maximal number of additional gates that may be used for depth optimization [default = %d]\n", params.additional_gates); Abc_Print( -2, "\t-Z : use a fixed seed\n", params.seed); - Abc_Print( -2, "\t-d : toggle inprocessing with deepsyn\n"); - Abc_Print( -2, "\t-f : toggle using subcircuits with forbidden pairs\n"); + Abc_Print( -2, "\t-c : toggle bias for selection of nodes on the crictical path\n"); + Abc_Print( -2, "\t-f : toggle forward expansion of root nodes\n"); Abc_Print( -2, "\t-h : print the command usage\n"); - Abc_Print( -2, "\t-n : toggle extended normality processing\n"); + Abc_Print( -2, "\t-i : toggle inprocessing\n"); Abc_Print( -2, "\t-s : toggle fill subcircuits\n"); + Abc_Print( -2, "\t-t : toggle use taboo list\n"); Abc_Print( -2, "\t\n" ); Abc_Print( -2, "\t This command was contributed by Franz-Xaver Reichl from University of Freiburg.\n" ); + return 1; + } int Abc_CommandAbc9CatBtor( Abc_Frame_t * pAbc, int argc, char ** argv ) { diff --git a/src/opt/eslim/areaEngine.cpp b/src/opt/eslim/areaEngine.cpp new file mode 100644 index 0000000000..8c3c106381 --- /dev/null +++ b/src/opt/eslim/areaEngine.cpp @@ -0,0 +1,163 @@ +/**CFile**************************************************************** + + FileName [areaEngine.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Implementation of area based SAT encoding.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#include "areaEngine.hpp" + +ABC_NAMESPACE_IMPL_START +namespace eSLIM { + + AreaEngine::AreaEngine(const Relation& relation, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log) + : RelationSynthesiser(relation, subcir, subcir.nodes.size(), cfg, log) { + for (int i = 0; i < subcir.inputs.size(); i++) { + input_index_map[subcir.inputs[i]] = i; + } + for (int i = 0; i < subcir.outputs.size(); i++) { + output_index_map[subcir.outputs[i]] = i; + } + if (subcir.forbidden_pairs.size() > 0) { + setupCycleConstraints(); + } + } + + eSLIMCirMan AreaEngine::synthesiseMinimumSizeCircuit(const Relation& relation, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log) { + AreaEngine synth(relation, subcir, cfg, log); + std::vector last_model; + int replacement_size = 0; + for (int i = synth.max_size; i>=0; i--) { + double timeout = synth.getDynamicTimeout(i); + int status = synth.checkSize(i, timeout); + if (status == 10) { + last_model = synth.solver.getModel(); + i = synth.getSizeFromActivationVars(last_model); + replacement_size = i; + } else { + break; + } + } + if (last_model.empty()) { + // no replacement found + return eSLIMCirMan(0); + } else { + return synth.getReplacement(last_model, replacement_size); + } + } + + int AreaEngine::checkSize(unsigned int size) { + return checkSize(size, 0); + } + + int AreaEngine::checkSize(unsigned int size, double timeout) { + std::vector assumptions = getSizeAssumption(size); + int status = timeout == 0 ? solver.solve(assumptions) : solver.solve(timeout, assumptions); + logRun(status, size); + return status; + } + + void AreaEngine::setupCycleConstraints() { + assert (!subcir.forbidden_pairs.empty()); + setupConnectionVariables(); + if (subcir.forbidden_pairs.size() > 1) { + connectionVariablesForCombinedCycles(); + } + addCycleConstraints(); + } + + void AreaEngine::addCycleConstraints() { + for (auto const& [outp, inputs] : subcir.forbidden_pairs) { + for (int inp : inputs) { + int output_idx = output_index_map.at(outp); + for (int i = 0; i < max_size; i++) { + solver.addClause({-gate_activation_variables[i], -gate_output_variables[i][output_idx], -connection_variables.at(inp)[i + subcir.inputs.size()]}); + } + for (int i = 0; i < subcir.inputs.size(); i++) { + solver.addClause({-gate_output_variables[max_size + i][output_idx], -connection_variables.at(inp)[i]}); + } + } + } + } + + void AreaEngine::setupConnectionVariables() { + for (auto const& [out, inputs] : subcir.forbidden_pairs) { + for (int in : inputs) { + if (connection_variables.find(in) == connection_variables.end()) { + connection_variables[in] = getNewVariableVector(max_size + subcir.inputs.size()); + } + } + } + + for (const auto& [input_var, connection_vars] : connection_variables) { + int input_idx = input_index_map.at(input_var); + // An input is connected to itself + solver.addClause({connection_vars[input_idx]}); + for (int i = 0; i < max_size; i++) { + int activation_variable = gate_activation_variables[i]; + for (int j = 0; j < subcir.inputs.size(); j++) { + // if gate i uses input j and input j is connected to input_var then also gate i is connected to input_var + solver.addClause({-activation_variable, -selection_variables[i][j], -connection_vars[j], connection_vars[subcir.inputs.size() + i]}); + } + for (int j = 0; j < i; j++) { + // if gate i uses gate j and gate j is connected to input_var then also gate i is connected to input_var + solver.addClause({-activation_variable, -selection_variables[i][subcir.inputs.size() + j], -connection_vars[subcir.inputs.size() + j], connection_vars[subcir.inputs.size() + i]}); + } + } + } + } + + + void AreaEngine::connectionVariablesForCombinedCycles() { + assert (subcir.forbidden_pairs.size() > 1); + // Inputs are assigned to the outputs on which they depend + std::unordered_map> inputs_per_output; + // Inputs in pairs + std::unordered_set inputs_in_pairs; + for (auto const& [outp, inputs] : subcir.forbidden_pairs) { + for (int inp : inputs) { + if (inputs_per_output.find(outp) == inputs_per_output.end()) { + inputs_per_output[outp] = {inp}; + } else { + inputs_per_output[outp].insert(inp); + } + inputs_in_pairs.insert(inp); + } + } + for (const auto& [ov, inp] : inputs_per_output) { + for (int iv : inputs_in_pairs) { + if (inp.find(iv) == inp.end()) { + for (int k : inputs_per_output.at(ov)) { + for (int i = 0; i < max_size; i++) { + // Input k depends on output ov. + // If gate i represents output ov and gate i is connected to iv then also gate k is connected to iv. + solver.addClause({-gate_activation_variables[i], -gate_output_variables[i][output_index_map.at(ov)], + -connection_variables.at(iv)[subcir.inputs.size() + i], connection_variables.at(iv)[input_index_map.at(k)]}); + } + for (int i = 0; i < subcir.inputs.size(); i++) { + // Input k depends on output ov. + // If input i represents output ov and input i is connected to iv then gate k shall be connected to iv too. + solver.addClause({-gate_output_variables[max_size + i][output_index_map.at(ov)], -connection_variables.at(iv)[i], connection_variables.at(iv)[input_index_map.at(k)]}); + } + } + } + } + } + } + + +} +ABC_NAMESPACE_IMPL_END \ No newline at end of file diff --git a/src/opt/eslim/areaEngine.hpp b/src/opt/eslim/areaEngine.hpp new file mode 100644 index 0000000000..594b22254d --- /dev/null +++ b/src/opt/eslim/areaEngine.hpp @@ -0,0 +1,63 @@ +/**CFile**************************************************************** + + FileName [areaEngine.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Implementation of area based SAT encoding.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__AREAENGINE_hpp +#define ABC__OPT__ESLIM__AREAENGINE_hpp + +#include "misc/util/abc_global.h" + +#include "relationSynthesiser.hpp" + + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class AreaEngine : private RelationSynthesiser { + + public: + static eSLIMCirMan synthesiseMinimumSizeCircuit(const Relation& relation, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log); + + private: + + std::unordered_map> connection_variables; + std::unordered_map input_index_map; + std::unordered_map output_index_map; + + AreaEngine(const Relation& relation, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log); + + int checkSize(unsigned int size, double timeout); + int checkSize(unsigned int size); + + void setupCycleConstraints(); + void setupConnectionVariables(); + // If we have multiple cyclic pairs, different pairs may form form a cycle together + // Assume we have two different pairs (x,y) and (a,b) in potential_cycles. + // It can be the case that x is connected to b and a is connected to y. + // To rule out cycles of this kind we make use of the following idea: + // If (x,y) is in self.potential_cycles then there is a path from x to y that is not part of the current subcircuit. + // Now if b is connected to x, this means that y is also connected to b. + // Thus, all gates that use y as an input are connected to b. + void connectionVariablesForCombinedCycles(); + void addCycleConstraints(); + }; + +} +ABC_NAMESPACE_CXX_HEADER_END +#endif \ No newline at end of file diff --git a/src/opt/eslim/cadicalSolver.hpp b/src/opt/eslim/cadicalSolver.hpp new file mode 100644 index 0000000000..ece98efa95 --- /dev/null +++ b/src/opt/eslim/cadicalSolver.hpp @@ -0,0 +1,218 @@ +/**CFile**************************************************************** + + FileName [cadicalSolver.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Interface for Cadical SAT solver.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__SATINTERFACE_h +#define ABC__OPT__ESLIM__SATINTERFACE_h + +#include +#include +#include +#include + +#include "sat/cadical/cadical.hpp" + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class CadicalSolver { + public: + ~CadicalSolver(); + void addClause(const std::vector& clause); + void addClause(std::vector&& clause); + void addCombinedClause(const std::vector& cl1, const std::vector& cl2, const std::vector& cl3); + void assume(const std::vector& assumptions); + int solve(double timeout, const std::vector& assumptions); + int solve(const std::vector& assumptions); + int solve(double timeout); + int solve(); + std::vector getFailed(const std::vector& assumptions); + bool isFailed(int lit); + std::vector getValues(const std::vector& variables); + bool getValue(int var); + std::vector getModel(); + double getRunTime() const; + + private: + void assumeAll(const std::vector& assumptions); + int val(int variable); + double last_runtime; + CaDiCaL::Solver solver; + + public: + class TimeoutTerminator : public CaDiCaL::Terminator { + public: + TimeoutTerminator(double max_runtime); + bool terminate(); + + private: + double max_runtime; //in seconds + std::chrono::time_point start = std::chrono::steady_clock::now(); + }; + + }; + + inline CadicalSolver::TimeoutTerminator::TimeoutTerminator(double max_runtime) : max_runtime(max_runtime) { + } + + inline bool CadicalSolver::TimeoutTerminator::terminate() { + auto current_time = std::chrono::steady_clock::now(); + std::chrono::duration elapsed_seconds = current_time - start; + return elapsed_seconds.count() > max_runtime; + } + + + + inline CadicalSolver::~CadicalSolver()=default; + + inline void CadicalSolver::addClause(const std::vector& clause) { + for (auto& l: clause) { + solver.add(l); + } + solver.add(0); + } + + inline void CadicalSolver::addClause(std::vector&& clause) { + for (auto& l: clause) { + solver.add(l); + } + solver.add(0); + } + + inline void CadicalSolver::addCombinedClause(const std::vector& cl1, const std::vector& cl2, const std::vector& cl3) { + for (auto& l: cl1) { + solver.add(l); + } + for (auto& l: cl2) { + solver.add(l); + } + for (auto& l: cl3) { + solver.add(l); + } + solver.add(0); + } + + inline void CadicalSolver::assume(const std::vector& assumptions) { + for (auto& l: assumptions) { + solver.assume(l); + } + } + + + inline void CadicalSolver::assumeAll(const std::vector& assumptions) { + solver.reset_assumptions(); + assume(assumptions); + } + + inline int CadicalSolver::solve(double timeout, const std::vector& assumptions) { + assumeAll(assumptions); + std::chrono::time_point start = std::chrono::steady_clock::now(); + TimeoutTerminator terminator(timeout); + solver.connect_terminator(&terminator); + int status = solver.solve(); + last_runtime = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); + if (solver.state() == CaDiCaL::INVALID) { + std::cerr<<"Solver is in invalid state"<& assumptions) { + assumeAll(assumptions); + std::chrono::time_point start = std::chrono::steady_clock::now(); + int status = solver.solve(); + last_runtime = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); + return status; + } + + inline int CadicalSolver::solve(double timeout) { + std::chrono::time_point start = std::chrono::steady_clock::now(); + TimeoutTerminator terminator(timeout); + solver.connect_terminator(&terminator); + int status = solver.solve(); + last_runtime = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); + if (solver.state() == CaDiCaL::INVALID) { + std::cerr<<"Solver is in invalid state"< start = std::chrono::steady_clock::now(); + int status = solver.solve(); + last_runtime = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); + return status; + } + + inline double CadicalSolver::getRunTime() const { + return last_runtime; + } + + inline std::vector CadicalSolver::getFailed(const std::vector& assumptions) { + std::vector failed_literals; + for (auto& l: assumptions) { + if (solver.failed(l)) { + failed_literals.push_back(l); + } + } + return failed_literals; + } + + inline bool CadicalSolver::isFailed(int lit) { + return solver.failed(lit); + } + + inline std::vector CadicalSolver::getValues(const std::vector& variables) { + std::vector assignment; + for (auto& v: variables) { + assignment.push_back(val(v)); + } + return assignment; + } + + inline bool CadicalSolver::getValue(int var) { + return val(var) > 0; + } + + inline std::vector CadicalSolver::getModel() { + std::vector assignment; + assignment.push_back(false); + for (int v = 1; v <= solver.vars(); v++) { + assignment.push_back(val(v) > 0); + } + return assignment; + } + + inline int CadicalSolver::val(int variable) { + auto l = solver.val(variable); + auto v = abs(l); + if (v == variable) { + return l; + } else { + return variable; + } + } + +} +ABC_NAMESPACE_CXX_HEADER_END +#endif \ No newline at end of file diff --git a/src/opt/eslim/delayEngine.cpp b/src/opt/eslim/delayEngine.cpp new file mode 100644 index 0000000000..0604a242a0 --- /dev/null +++ b/src/opt/eslim/delayEngine.cpp @@ -0,0 +1,252 @@ +/**CFile**************************************************************** + + FileName [delayEngine.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Implementation of delay based SAT encoding.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#include "delayEngine.hpp" + + +ABC_NAMESPACE_IMPL_START +namespace eSLIM { + + DelayEngine::DelayEngine( const Relation& relation, const Subcircuit& subcir, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log) + : RelationSynthesiser(relation, subcir, max_size, cfg, log) { + assert (subcir.forbidden_pairs.empty()); + symmetry_selector = getNewVariable(); + symmetry_selector_assignment = symmetry_selector; + std::set unique_ats_set (subcir.arrival_times.begin(), subcir.arrival_times.end()); + unique_arrival_times = std::vector (unique_ats_set.begin(), unique_ats_set.end()); + for (int i : subcir.arrival_times) { + auto it = unique_ats_set.find(i); + int idx = std::distance(unique_ats_set.begin(), it); + inputid2uniqueid.push_back(idx); + } + setupVariables(); + constrainDelayVariables(); + setupDelayConstraints(); + max_delay = delay_selectors.begin()->first; + } + + // do not enfore activation variables -> a minimum nof gates might forbid a realization with a certain depth. + eSLIMCirMan DelayEngine::synthesizeMinimumDelayCircuit(const Relation& relation, const Subcircuit& subcir, unsigned int delay, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log) { + DelayEngine synth(relation, subcir, max_size, cfg, log); + std::vector last_model = synth.reduceDelay(max_size, delay); + if (last_model.empty()) { + // no replacement found + return eSLIMCirMan(0); + } else { + int replacement_size = synth.getSizeFromActivationVars(last_model); + return synth.getReplacement(last_model, replacement_size); + } + } + + eSLIMCirMan DelayEngine::synthesizeMinimumDelayAreaCircuit(const Relation& relation, const Subcircuit& subcir, unsigned int delay, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log) { + DelayEngine synth(relation, subcir, max_size, cfg, log); + std::vector last_model = synth.reduceDelay(max_size, delay); + if (last_model.empty()) { + // We could not even find a replacement with the same delay as the original circuit (timeout) + // Hence, it is unlikely that we find a circuit that has the same delay but fewer gates + return eSLIMCirMan(0); + } + int replacement_size = synth.getSizeFromActivationVars(last_model); + int replacement_delay = synth.getDelayFromDelayVariables(last_model); + std::vector model = synth.reduceArea(replacement_delay, replacement_size - 1); + if (model.size() > 0) { + replacement_size = synth.getSizeFromActivationVars(model); + std::swap(last_model, model); + } + return synth.getReplacement(last_model, replacement_size); + } + + eSLIMCirMan DelayEngine::synthesizeMinimumAreaDelayCircuit(const Relation& relation, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log) { + DelayEngine synth(relation, subcir, subcir.nodes.size(), cfg, log); + std::vector last_model = synth.reduceArea(synth.max_delay, subcir.nodes.size()); + if (last_model.empty()) { + // We could not even find a replacement with the same area as the original circuit (timeout) + // Hence, it is unlikely that we find a circuit that has the same area and a causes a smaller delay + return eSLIMCirMan(0); + } + int replacement_size = synth.getSizeFromActivationVars(last_model); + int replacement_delay = synth.getDelayFromDelayVariables(last_model); + assert (replacement_delay > 0); + std::vector model = synth.reduceDelay(replacement_size, replacement_delay - 1); + if (model.size() > 0) { + replacement_size = synth.getSizeFromActivationVars(model); + std::swap(last_model, model); + } + return synth.getReplacement(last_model, replacement_size); + } + + eSLIMCirMan DelayEngine::synthesizeMinimumAreaDelayConstraintCircuit(const Relation& relation, const Subcircuit& subcir, unsigned int delay, const eSLIMConfig& cfg, eSLIMLog& log) { + DelayEngine synth(relation, subcir, subcir.nodes.size(), cfg, log); + std::vector last_model = synth.reduceArea(delay, subcir.nodes.size()); + if (last_model.empty()) { + return eSLIMCirMan(0); + } else { + int replacement_size = synth.getSizeFromActivationVars(last_model); + return synth.getReplacement(last_model, replacement_size); + } + } + + std::vector DelayEngine::reduceArea(unsigned int max_delay, unsigned int initial_area) { + std::vector last_model; + for (int i = initial_area; i>=0; i--) { + double timeout = getDynamicTimeout(i); + int status = existsReplacement(i, max_delay, timeout); + if (status == 10) { + last_model = solver.getModel(); + i = getSizeFromActivationVars(last_model); // it is possible that more than one gate is reduced in an iteration + } else { + break; + } + } + return last_model; + } + + std::vector DelayEngine::reduceDelay(unsigned int max_size, unsigned int initial_delay) { + assert (delay_selectors.find(initial_delay) != delay_selectors.end()); + std::vector last_model; + for( auto it = delay_selectors.find(initial_delay); it != delay_selectors.end(); ++it ) { + int d = it->first; + double timeout = getDynamicTimeout(max_size); + int status = existsReplacement(max_size, d, timeout); + if (status == 10) { + last_model = solver.getModel(); + } else { + break; + } + } + return last_model; + } + + std::vector DelayEngine::getAssumptions(unsigned int delay) { + std::vector assumptions; + for (auto const& [d, ds] : delay_selectors) { + if (d > delay) { + assumptions.push_back(-ds); + } + } + return assumptions; + } + + std::vector DelayEngine::getAssumptions(unsigned int size, unsigned int delay) { + std::vector assumptions = getAssumptions(delay); + std::vector area_assumptions = getSizeAssumption(size); + assumptions.insert(assumptions.end(), area_assumptions.begin(), area_assumptions.end()); + return assumptions; + } + + unsigned int DelayEngine::getDelayFromDelayVariables(const std::vector& model) { + unsigned int delay = 0; + for (auto const& [d, dvars] : delays2delay_variables) { + for (int v : dvars) { + if (model[v]) { + return d; + } + } + } + return delay; + } + + int DelayEngine::existsReplacement(unsigned int delay, double timeout) { + std::vector assumptions = getAssumptions(delay); + int status = timeout == 0 ? solver.solve(assumptions) : solver.solve(timeout, assumptions); + unsigned int size = status == 10 ? getSizeFromActivationVars() : max_size; + logRun(status, size); + return status; + } + + int DelayEngine::existsReplacement(unsigned int size, unsigned int delay, double timeout) { + std::vector assumptions = getAssumptions(size, delay); + int status = timeout == 0 ? solver.solve(assumptions) : solver.solve(timeout, assumptions); + logRun(status, size); + return status; + } + + void DelayEngine::setupVariables() { + for (int i = 0; i < max_size; i++) { + std::vector> dvars; + for (int j = 0; j < unique_arrival_times.size(); j++) { + dvars.push_back(getNewVariableVector(i + 1)); + } + delay_variables.push_back(dvars); + } + for (int i = 0; i < subcir.outputs.size(); i++) { + std::vector> dvars; + for (int j = 0; j < unique_arrival_times.size(); j++) { + dvars.push_back(getNewVariableVector(max_size + 1)); + } + output_delays.push_back(dvars); + } + } + + int DelayEngine::getDelaySelector(int delay) { + if (delay_selectors.find(delay) == delay_selectors.end()) { + delay_selectors[delay] = getNewVariable(); + } + return delay_selectors[delay]; + } + + + void DelayEngine::constrainDelayVariables() { + for (int i = 0; i < max_size; i++) { + int activation_variable = gate_activation_variables[i]; + for (int j = 0; j < subcir.inputs.size(); j++) { + solver.addClause({-activation_variable, -selection_variables[i][j], delay_variables[i][inputid2uniqueid[j]][0]}); + } + for (int j = 0; j < unique_arrival_times.size(); j++) { + for (int k = 0; k < i; k++) { + int node_id = subcir.inputs.size() + k; + for (int l = 0; l <= k; l++) { + solver.addClause({-activation_variable, -selection_variables[i][node_id], -delay_variables[k][j][l], delay_variables[i][j][l+1]}); + } + solver.addClause({-activation_variable, -delay_variables[i][j][k+1], delay_variables[i][j][k]}); + } + } + } + for (int i = 0; i < subcir.outputs.size(); i++) { + for (int j = 0; j < subcir.inputs.size(); j++) { + solver.addClause({-gate_output_variables[max_size + j][i], output_delays[i][inputid2uniqueid[j]][0]}); + } + for (int j = 0; j < unique_arrival_times.size(); j++) { + for (int k = 0; k < max_size; k++) { + solver.addClause({-output_delays[i][j][k+1], output_delays[i][j][k]}); + for (int l = 0; l <= k; l++) { + solver.addClause({-gate_output_variables[k][i], -delay_variables[k][j][l], output_delays[i][j][l+1]}); + } + } + } + } + } + + void DelayEngine::setupDelayConstraints() { + for (int i = 0; i < unique_arrival_times.size(); i++) { + for (int o = 0; o < subcir.outputs.size(); o++) { + for (int d = 0; d <= max_size; d++) { + int delay = unique_arrival_times[i] + subcir.remaining_times[o] + d; + int ds = getDelaySelector(delay); + solver.addClause({ds, -output_delays[o][i][d]}); + + delays2delay_variables[delay].push_back(output_delays[o][i][d]); + } + } + } + } + +} +ABC_NAMESPACE_IMPL_END \ No newline at end of file diff --git a/src/opt/eslim/delayEngine.hpp b/src/opt/eslim/delayEngine.hpp new file mode 100644 index 0000000000..66f9dc464e --- /dev/null +++ b/src/opt/eslim/delayEngine.hpp @@ -0,0 +1,81 @@ +/**CFile**************************************************************** + + FileName [delayEngine.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Implementation of delay based SAT encoding.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__DELAYENGINE_hpp +#define ABC__OPT__ESLIM__DELAYENGINE_hpp + +#include + +#include "misc/util/abc_global.h" + +#include "relationSynthesiser.hpp" + + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class DelayEngine : public RelationSynthesiser { + + public: + // Find a replacement with at most max_size gates that yields minimum delay + static eSLIMCirMan synthesizeMinimumDelayCircuit(const Relation& relation, const Subcircuit& subcir, unsigned int delay, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log); + // Find a replacement with at most max_size gates that yields minimum delay. Among these circuits find one of minimum size + static eSLIMCirMan synthesizeMinimumDelayAreaCircuit(const Relation& relation, const Subcircuit& subcir, unsigned int delay, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log); + // First minimize area, then minimize delay + static eSLIMCirMan synthesizeMinimumAreaDelayCircuit(const Relation& relation, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log); + // Find a smaller replacement that must not increase delay + static eSLIMCirMan synthesizeMinimumAreaDelayConstraintCircuit(const Relation& relation, const Subcircuit& subcir, unsigned int delay, const eSLIMConfig& cfg, eSLIMLog& log); + + private: + + std::vector unique_arrival_times; + std::vector inputid2uniqueid; + unsigned int max_delay; + int symmetry_selector; + int symmetry_selector_assignment; + // Indicate the length of the longest paths from inputs to gates + std::vector>> delay_variables; + std::vector>> output_delays; + + std::map> delay_selectors; + std::map, std::greater> delays2delay_variables; + + DelayEngine(const Relation& relation, const Subcircuit& subcir, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log); + int existsReplacement(unsigned int size, unsigned int delay, double timeout); + int existsReplacement(unsigned int delay, double timeout); + std::vector getAssumptions(unsigned int delay); + std::vector getAssumptions(unsigned int size, unsigned int delay); + void setupVariables(); + + unsigned int getDelayFromDelayVariables(const std::vector& model); + + std::vector reduceArea(unsigned int max_delay, unsigned int initial_area); + std::vector reduceDelay(unsigned int max_size, unsigned int initial_delay); + + int getDelaySelector(int delay); + void setupDelayConstraints(); + void constrainDelayVariables(); + + }; + + +} +ABC_NAMESPACE_CXX_HEADER_END +#endif \ No newline at end of file diff --git a/src/opt/eslim/eSLIM.cpp b/src/opt/eslim/eSLIM.cpp index 3eea6eaa1b..6705515686 100644 --- a/src/opt/eslim/eSLIM.cpp +++ b/src/opt/eslim/eSLIM.cpp @@ -12,204 +12,611 @@ Affiliation [University of Freiburg] - Date [Ver. 1.0. Started - March 2025.] + Date [Ver. 1.0. Started - April 2026.] - Revision [$Id: eSLIM.cpp,v 1.00 2025/03/17 00:00:00 Exp $] + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] ***********************************************************************/ + #include "utils.hpp" #include "eSLIMMan.hpp" #include "relationGeneration.hpp" -#include "selectionStrategy.hpp" +#include "selectionStrategies.hpp" +#include "windowMan.hpp" #include "misc/util/abc_namespaces.h" +#include "opt/mfs/mfs.h" +#include "base/main/main.h" #include "eSLIM.h" +#include "eslimCirMan.hpp" +#include "synthesisEngines.hpp" +#include "selectionStrategies.hpp" ABC_NAMESPACE_HEADER_START Gia_Man_t * Gia_ManDeepSyn( Gia_Man_t * pGia, int nIters, int nNoImpr, int TimeOut, int nAnds, int Seed, int fUseTwo, int fChoices, int fVerbose ); + int Abc_NtkMfs( Abc_Ntk_t * pNtk, Mfs_Par_t * pPars ); ABC_NAMESPACE_HEADER_END ABC_NAMESPACE_IMPL_START eSLIM::eSLIMConfig getCfg(const eSLIM_ParamStruct* params) { eSLIM::eSLIMConfig config; - config.extended_normality_processing = params->extended_normality_processing; config.apply_strash = params->apply_strash; config.fill_subcircuits = params->fill_subcircuits; config.fix_seed = params->fix_seed; config.trial_limit_active = params->trial_limit_active; config.timeout = params->timeout; config.iterations = params->iterations; - config.subcircuit_size_bound = params->subcircuit_size_bound; + config.subcircuit_max_size = params->subcircuit_max_size; + config.additional_gates = params->additional_gates; config.strash_intervall = params->strash_intervall; config.nselection_trials = params->nselection_trials; config.seed = params->seed; config.verbosity_level = params->verbosity_level; config.expansion_probability = params->expansion_probability; + config.approximate_relation = params->approximate_relation; + config.relation_tfo_bound = params->relation_tfo_bound; + config.generate_relation_with_tfi_limit = params->generate_relation_with_tfi_limit; + config.relation_tfi_bound = params->relation_tfi_bound; + config.nwindows = params->nWindows; + config.window_size = params->window_size; + + config.aig = params->aig; + config.gate_size = params->gate_size; + + if (params->use_taboo_list) { + config.apply_strash = false; + } + return config; } void seteSLIMParams(eSLIM_ParamStruct* params) { - params->forbidden_pairs = 1; - params->extended_normality_processing = 0; + params->fill_subcircuits = 0; params->apply_strash = 1; params->fix_seed = 0; params->trial_limit_active = 1; params->apply_inprocessing = 1; - params->timeout = 1620; - params->timeout_inprocessing = 180; + params->synthesis_approach = 0; + params->seed = 0; + params->verbosity_level = 1; + params->use_taboo_list = 0; + params->forward_search = 0; + + params->nWindows = 0; + params->window_size = 500; + + params->criticial_path_selection_bias = 0; + + params->timeout = 1800; params->iterations = 0; - params->subcircuit_size_bound = 6; + params->subcircuit_max_size = 6; + params->additional_gates = 0; params->strash_intervall = 100; params->nselection_trials = 100; params->nruns = 1; - params->mode = 0; - params->seed = 0; - params->verbosity_level = 1; - params->expansion_probability = 0.4; -} - -Gia_Man_t* selectApproach(Gia_Man_t* pGia, eSLIM::eSLIMConfig params, eSLIM::eSLIMLog& log, int approach, bool allow_forbidden_pairs) { - switch (approach) { - case 0 : - if (allow_forbidden_pairs) { - return eSLIM::eSLIM_Man::applyeSLIM(pGia, params, log); - } else { - return eSLIM::eSLIM_Man::applyeSLIM(pGia, params, log); - }; - case 1: - if (allow_forbidden_pairs) { - return eSLIM::eSLIM_Man::applyeSLIM(pGia, params, log); - } else { - return eSLIM::eSLIM_Man::applyeSLIM(pGia, params, log); - }; - case 2: - if (allow_forbidden_pairs) { - return eSLIM::eSLIM_Man::applyeSLIM(pGia, params, log); - } else { - return eSLIM::eSLIM_Man::applyeSLIM(pGia, params, log); - }; - default: - std::cerr << "eSLIM -- Invalid mode given\n"; - assert (false && "Invalid approach selected"); - return nullptr; - } + + params->expansion_probability = 0.5; + params->approximate_relation = 0; + params->relation_tfo_bound = 0; + params->generate_relation_with_tfi_limit = 0; + params->relation_tfi_bound = 0; + + params->aig = 1; + params->gate_size = 2; } -Gia_Man_t* runInprocessing(Gia_Man_t * pGia, const eSLIM_ParamStruct* params, unsigned int it) { - Gia_Man_t * tmp = Gia_ManDeepSyn( pGia, 1, ABC_INFINITY, params->timeout_inprocessing, 0, params->seed + it, 0, 0, 0); - if ( Gia_ManAndNum(pGia) > Gia_ManAndNum(tmp) ) { - Gia_ManStop( pGia ); - pGia = tmp; - } else { - Gia_ManStop( tmp ); +namespace eSLIM { + + class DeepsynInprocessor { + public: + DeepsynInprocessor(eSLIMConfig& config); + void setTimeout(int timeout) {this->timeout = timeout;} + void runInprocessing(eSLIMCirMan& es_man); + private: + eSLIMConfig& config; + int timeout; + }; + + class MfsInprocessor { + public: + MfsInprocessor(eSLIMConfig& config) {} + void runInprocessing(eSLIMCirMan& es_man); + }; + + class EmptyInprocessor { + public: + EmptyInprocessor(eSLIMConfig& config) {} + void runInprocessing(eSLIMCirMan& es_man) {} + }; + + template + class DelayInprocessor { + public: + DelayInprocessor(eSLIMConfig& config); + void setTimeout(int timeout) {this->timeout = timeout;} + void runInprocessing(eSLIMCirMan& es_man); + private: + eSLIMConfig& config; + int timeout; + }; + + class Resyn2Inprocessor { + public: + Resyn2Inprocessor(eSLIMConfig& config); + void runInprocessing(eSLIMCirMan& es_man); + private: + // eSLIMConfig& config; + }; + + + template + class eSLIMRun { + + public: + static Gia_Man_t* runeSLIM(Gia_Man_t* cir, const eSLIM_ParamStruct* params); + static Abc_Ntk_t * runeSLIM(Abc_Ntk_t * cir, const eSLIM_ParamStruct* params); + static Abc_Ntk_t * runeSLIMCells(Abc_Ntk_t * cir, const eSLIM_ParamStruct* params); + + private: + eSLIMCirMan es_man; + eSLIMConfig config; + eSLIMLog log; + Inprocessor inprocessor; + + int initial_area; + int initial_delay; + + int area_change_eslim = 0; + int area_change_inprocessing = 0; + + int delay_change_eslim = 0; + int delay_change_inprocessing = 0; + + int nruns = 0; + + eSLIMRun(Gia_Man_t* cir, const eSLIM_ParamStruct* params); + eSLIMRun(Abc_Ntk_t* cir, const eSLIM_ParamStruct* params, bool simplify); + + void setUpInprocessor(); + void runInprocessing(); + + void printSettings(); + void printLog(); + + void run(); + }; + + + template + eSLIMRun::eSLIMRun(Abc_Ntk_t* cir, const eSLIM_ParamStruct* params, bool simplify_circuit) + : es_man(cir, simplify_circuit), config(getCfg(params)), log(params->subcircuit_max_size), inprocessor(config) { + nruns = params->nruns; + config.aig = false; + config.apply_strash = false; } - return pGia; -} -void printSetting(const eSLIM_ParamStruct* params) { - std::cout << "Apply eSLIM (timeout: " << params->timeout << ") "; - if (params->apply_inprocessing) { - std::cout << "+ deepsyn (timeout: " << params->timeout_inprocessing << ") "; + template + eSLIMRun::eSLIMRun(Gia_Man_t* cir, const eSLIM_ParamStruct* params) + : es_man(cir), config(getCfg(params)), log(params->subcircuit_max_size), inprocessor(config) { + nruns = params->nruns; } - std::cout << params->nruns << " times.\n"; - if (params->iterations > 0) { - std::cout << "Stop eSLIM runs after " << params->iterations << " iterations.\n"; + + template + Gia_Man_t* eSLIMRun::runeSLIM(Gia_Man_t* cir, const eSLIM_ParamStruct* params) { + + eSLIMRun eslim_run(cir, params); + // It seems like the variables are freed by abc during preprocessing. Thus,we copy them + char* name = Abc_UtilStrsav( cir->pName ); + char* spec = Abc_UtilStrsav( cir->pSpec ); + eslim_run.run(); + Gia_Man_t* gia_res = eslim_run.es_man.eSLIMCirManToGia(); + gia_res->pName = name; + gia_res->pSpec = spec; + return gia_res; } - std::cout << "Consider subcircuits with up to " << params->subcircuit_size_bound << " gates.\n"; - printf("When expanding subcircuits, select gates with a probability of %.2f %%.\n", 100 * params->expansion_probability); - if (params->forbidden_pairs) { - std::cout << "Consider subcircuits containing gates, which are connected outside of the subcircuit.\n"; + + template + Abc_Ntk_t* eSLIMRun::runeSLIM(Abc_Ntk_t* cir, const eSLIM_ParamStruct* params) { + bool simplify_circuit = true; + eSLIMRun eslim_run(cir, params, simplify_circuit); + eslim_run.config.aig = false; + eslim_run.config.apply_strash = false; + eslim_run.run(); + Abc_Ntk_t* ntk_res = eslim_run.es_man.eSLIMCirManToNtk(); + ntk_res->pName = Extra_UtilStrsav(cir->pName); + ntk_res->pSpec = Extra_UtilStrsav(cir->pSpec); + return ntk_res; } - if (params->mode == 0) { - std::cout << "Use an incremental SAT encoding for exact synthesis and the SAT solver CaDiCaL.\n"; - } else { - std::cout << "Use a one-shot SAT encoding for exact synthesis and the SAT solver Kissat.\n"; + + template + Abc_Ntk_t * eSLIMRun::runeSLIMCells(Abc_Ntk_t * cir, const eSLIM_ParamStruct* params) { + bool simplify_circuit = false; + eSLIMRun eslim_run(cir, params, simplify_circuit); + eslim_run.config.aig = false; + eslim_run.config.apply_strash = false; + eslim_run.run(); + Abc_Ntk_t* ntk_res = eslim_run.es_man.eSLIMCirManToMappedNtk(); + if (!ntk_res) { + return ntk_res; + } + ntk_res->pName = Extra_UtilStrsav(cir->pName); + ntk_res->pSpec = Extra_UtilStrsav(cir->pSpec); + return ntk_res; } -} -Gia_Man_t* applyeSLIM(Gia_Man_t * pGia, const eSLIM_ParamStruct* params) { - eSLIM::eSLIMConfig config = getCfg(params); - config.verbosity_level = params->verbosity_level; + template + void eSLIMRun::run() { + if constexpr ( std::is_same_v || std::is_same_v, Inprocessor> || std::is_same_v, Inprocessor> ) { + int time_out_inprocessing; + if constexpr ( std::is_same_v ) { + time_out_inprocessing = config.timeout * 0.1; + } else { + time_out_inprocessing = config.timeout * 0.02; + } + config.timeout -= time_out_inprocessing; + inprocessor.setTimeout(time_out_inprocessing); + } - int eSLIM_reductions = 0; - int deepsyn_reductions = 0; + initial_area = es_man.getNofGates(); + initial_delay = es_man.getDepth(); + printSettings(); + for (int i = 0; i < nruns; i++) { - Gia_Man_t * pThis = Gia_ManDup(pGia); - eSLIM::eSLIMLog log(params->subcircuit_size_bound); - int initial_size = Gia_ManAndNum(pThis); - if (params->verbosity_level > 0) { - printSetting(params); - std::cout << "Initial size: " << initial_size << "\n"; + int size_iteration_start = es_man.getNofGates(); + int delay_iteration_start = es_man.getDepth(); + if (config.verbosity_level > 0) { + std::cout << "Start eslim run " << i + 1 << "\n"; + } + Approach::applyeSLIM(es_man, config, log); + int size_eslim = es_man.getNofGates(); + int delay_eslim = es_man.getDepth(); + area_change_eslim += size_eslim - size_iteration_start; + delay_change_eslim += delay_eslim - delay_iteration_start; + if (config.verbosity_level > 0) { + std::cout << "eslim -- size: " << size_eslim << " delay: " << delay_eslim << "\n"; + } + runInprocessing(); + if constexpr ( !std::is_same_v ) { + int size_inprocessing = es_man.getNofGates(); + int delay_inprocessing = es_man.getDepth(); + area_change_inprocessing += size_inprocessing - size_eslim; + delay_change_inprocessing += delay_inprocessing - delay_eslim; + if (config.verbosity_level > 0) { + std::cout << "inprocessing -- size: " << size_inprocessing << " delay: " << delay_inprocessing << "\n"; + } + } + if (config.fix_seed) { + config.seed++; + } + } + printLog(); } - for (unsigned int i = 0; i < params->nruns; i++) { - if (config.fix_seed) { - config.seed = params->seed + i; + + template + void eSLIMRun::printSettings() { + std::cout << "Apply eSLIM (timeout: " << config.timeout << ") "; + std::cout << nruns << " times.\n"; + if constexpr ( !std::is_same_v ) { + std::cout << "Apply inprocessing\n"; } - int size_iteration_start = Gia_ManAndNum(pThis); - if (params->verbosity_level > 0) { - std::cout << "Start eslim run " << i + 1 << "\n"; + if (config.iterations > 0) { + std::cout << "Stop eSLIM runs after " << config.iterations << " iterations.\n"; } - pThis = selectApproach(pThis, config, log, params->mode, params->forbidden_pairs); - int size_eslim = Gia_ManAndNum(pThis); - eSLIM_reductions += size_iteration_start - size_eslim; + std::cout << "Consider subcircuits with up to " << config.subcircuit_max_size << " gates.\n"; + printf("When expanding subcircuits, select gates with a probability of %.2f %%.\n", 100 * config.expansion_probability); + } + + template + void eSLIMRun::printLog() { + if (config.verbosity_level > 0) { + int size = es_man.getNofGates(); + int delay = es_man.getDepth(); + int total_area_change = area_change_eslim + area_change_inprocessing; + int total_delay_change = delay_change_eslim + delay_change_inprocessing; + std::cout << "Final size: " << size << " change: " << total_area_change << "\n"; + std::cout << "Final depth: " << delay << " change: " << total_delay_change << "\n"; + + + if constexpr ( !std::is_same_v ) { - if (params->apply_inprocessing) { - if (params->verbosity_level > 0) { - std::cout << "Start inprocessing run " << i + 1 << "\n"; + if (area_change_eslim <= 0) { + std::cout << "#Gates reduced by eSLIM: " << -area_change_eslim << "\n"; + } else { + std::cout << "#Gates introduced by eSLIM: " << area_change_eslim << "\n"; + } + if (area_change_inprocessing <= 0) { + std::cout << "#Gates reduced by inprocessing: " << -area_change_inprocessing << "\n"; + } else { + std::cout << "#Gates introduced by inprocessing: " << area_change_inprocessing << "\n"; + } + if (delay_change_eslim <= 0) { + std::cout << "Delay reduction eSLIM: " << -delay_change_eslim << "\n"; + } else { + std::cout << "Delay increase eSLIM: " << delay_change_eslim << "\n"; + } + if (delay_change_inprocessing <= 0) { + std::cout << "Delay reduction inprocessing: " << -delay_change_inprocessing << "\n"; + } else { + std::cout << "Delay increase inprocessing: " << delay_change_inprocessing << "\n"; + } } - pThis = runInprocessing(pThis, params, i); - if (params->verbosity_level > 0) { - std::cout << "Inprocessing: Initial size: " << size_eslim << " final size: " << Gia_ManAndNum(pThis) << "\n"; + } + if (config.verbosity_level > 1) { + std::cout << "Total #iterations: " << log.iteration_count << "\n"; + std::cout << "Total relation generation time (s): " << log.relation_generation_time << "\n"; + std::cout << "Total synthesis time (s): " << log.synthesis_time << "\n"; + std::cout << "#Iterations with forbidden pairs: " << log.subcircuits_with_forbidden_pairs << "\n"; + printf("Relation generation: #SAT-calls: %lu, Total SAT-time: %.2f sec, Max SAT-time: %.4f sec, Avg. SAT-time: %.4f sec\n", + log.nof_sat_calls_relation_generation, log.cummulative_sat_runtime_relation_generation, log.max_sat_runtime_relation_generation, + log.cummulative_sat_runtime_relation_generation/log.nof_sat_calls_relation_generation); + printf("Synthesis: #SAT-calls: %lu, Total SAT-time: %.2f sec, Max SAT-time: %.4f sec, Avg. SAT-time: %.4f sec\n", + log.nof_sat_calls_synthesis, log.cummulative_sat_runtime_synthesis / 1000, log.max_sat_runtime_synthesis / 1000, + log.cummulative_sat_runtime_synthesis / (log.nof_sat_calls_synthesis*1000)); + } + if (config.verbosity_level > 2) { + for (int i = 2; i < log.nof_analyzed_circuits_per_size.size(); i++) { + int replaced = log.nof_replaced_circuits_per_size.size() > i ? log.nof_replaced_circuits_per_size[i] : 0; + int reduced = log.nof_reduced_circuits_per_size.size() > i ? log.nof_reduced_circuits_per_size[i] : 0; + std::cout << "#gates: " << i << " - #analysed: " << log.nof_analyzed_circuits_per_size[i] << " - #replaced: " << replaced << " - #reduced: " << reduced; + std::cout << " - avg. relation time: "; + if (log.nof_relation_generations_per_size[i] == 0) { + std::cout << "-"; + } else { + printf("%.2f sec", static_cast(log.cummulative_relation_generation_times_per_size[i]) / (log.nof_relation_generations_per_size[i])); + } + std::cout << "\n"; } - deepsyn_reductions += size_eslim - Gia_ManAndNum(pThis); - } - } - if (params->verbosity_level > 0) { - int final_size = Gia_ManAndNum(pThis); - std::cout << "Final size: " << final_size << "\n"; - int total_reductions = eSLIM_reductions + deepsyn_reductions; - double reduction_ratio = 100 * static_cast(total_reductions) / initial_size; - printf("Total reduction: %d (%.2f %%)\n", total_reductions, reduction_ratio); - if (params->apply_inprocessing) { - std::cout << "#Gates reduced by eSLIM: " << eSLIM_reductions << " "; - std::cout << "#Gates reduced by deepsyn: " << deepsyn_reductions << "\n"; - } - } - - if (params->verbosity_level > 1) { - std::cout << "Total #iterations: " << log.iteration_count << "\n"; - std::cout << "Total relation generation time (s): " << log.relation_generation_time << "\n"; - std::cout << "Total synthesis time (s): " << log.synthesis_time << "\n"; - std::cout << "#Iterations with forbidden pairs: " << log.subcircuits_with_forbidden_pairs << "\n"; - } - if (params->verbosity_level > 2) { - for (int i = 2; i < log.nof_analyzed_circuits_per_size.size(); i++) { - int replaced = log.nof_replaced_circuits_per_size.size() > i ? log.nof_replaced_circuits_per_size[i] : 0; - int reduced = log.nof_reduced_circuits_per_size.size() > i ? log.nof_reduced_circuits_per_size[i] : 0; - std::cout << "#gates: " << i << " - #analysed: " << log.nof_analyzed_circuits_per_size[i] << " - #replaced: " << replaced << " - #reduced: " << reduced << "\n"; - } - if (params->mode == 0) { for (int i = 0; i < log.nof_sat_calls_per_size.size(); i++) { + std::cout << "#gates: " << i << " - avg. sat time: "; if (log.nof_sat_calls_per_size[i] == 0) { - std::cout << "#gates: " << i << " - avg. sat time: -\n"; + std::cout << "-"; } else { - std::cout << "#gates: " << i << " - avg. sat time: " << static_cast(log.cummulative_sat_runtimes_per_size[i]) / (1000 * log.nof_sat_calls_per_size[i]) << " sec\n"; + printf("%.2f sec", static_cast(log.cummulative_sat_runtimes_per_size[i]) / (1000 * log.nof_sat_calls_per_size[i])); } - } - for (int i = 0; i < log.nof_unsat_calls_per_size.size(); i++) { + std::cout << " / avg. unsat time: "; if (log.nof_unsat_calls_per_size[i] == 0) { - std::cout << "#gates: " << i << " - avg. unsat time: -\n"; + std::cout << "-"; } else { - std::cout << "#gates: " << i << " - avg. unsat time: " << static_cast(log.cummulative_unsat_runtimes_per_size[i]) / (1000* log.nof_unsat_calls_per_size[i]) << " sec\n"; + printf("%.2f sec", static_cast(log.cummulative_unsat_runtimes_per_size[i]) / (1000 * log.nof_unsat_calls_per_size[i])); } + std::cout << "\n"; + } + } + } + + template + void eSLIMRun::runInprocessing() { + inprocessor.runInprocessing(es_man); + } + + template + void setUpInprocessor(); + + DeepsynInprocessor::DeepsynInprocessor(eSLIMConfig& config) + : config(config) { + } + + void DeepsynInprocessor::runInprocessing(eSLIMCirMan& es_man) { + Gia_Man_t* pGia = es_man.eSLIMCirManToGia(); + Gia_Man_t* tmp = Gia_ManDeepSyn( pGia, 1, ABC_INFINITY, timeout, 0, config.seed , 0, 0, 0); + if ( Gia_ManAndNum(pGia) > Gia_ManAndNum(tmp) ) { + es_man = eSLIMCirMan(tmp); + } + Gia_ManStop( tmp ); + Gia_ManStop( pGia ); + } + + void MfsInprocessor::runInprocessing(eSLIMCirMan& es_man) { + Abc_Ntk_t* pNtk = es_man.eSLIMCirManToNtk(); + int nnodes = Abc_NtkNodeNum(pNtk); + Mfs_Par_t Pars, * pPars = &Pars; + Abc_NtkMfsParsDefault( pPars ); + if ( Abc_NtkMfs( pNtk, pPars ) ) { + Abc_NtkToSop( pNtk, -1, ABC_INFINITY ); + if (Abc_NtkNodeNum(pNtk) <= nnodes) { + es_man = eSLIMCirMan(pNtk, true); } } + Abc_NtkDelete(pNtk); + } + + template + DelayInprocessor::DelayInprocessor(eSLIMConfig& config) + : config(config) { + } + + template + void DelayInprocessor::runInprocessing(eSLIMCirMan& es_man) { + abctime clkStart = Abc_Clock(); + abctime nTimeToStop = clkStart + timeout * CLOCKS_PER_SEC; + Gia_Man_t* pGia = es_man.eSLIMCirManToGia(); + int nand = Gia_ManAndNum(pGia); + int nlevel = Gia_ManLevelNum(pGia); + char * resyn2 = "balance; rewrite; rewrite -z; balance; rewrite -z; balance"; + char Command[2000]; + sprintf( Command, "strash; if -g -K 6; %s; if -K 6", resyn2 ); + bool reset_batch_mode = false ; + if ( !Abc_FrameIsBatchMode() ) { + reset_batch_mode = true; + Abc_FrameSetBatchMode( 1 ); + } + Abc_FrameUpdateGia( Abc_FrameGetGlobalFrame(), pGia ); + if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&put") ) { + return; + } + while (Abc_Clock() <= nTimeToStop) { + if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), Command) ) { + Abc_Print( 1, "Something did not work out with the command \"%s\".\n", Command ); + return; + } + } + if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&get") ) { + return; + } + if (reset_batch_mode) { + Abc_FrameSetBatchMode( 0 ); + } + Gia_Man_t* tmp = Abc_FrameReadGia(Abc_FrameGetGlobalFrame()); + if constexpr (!IncreaseSize) { + if (Gia_ManAndNum(tmp) > nand) { + return; + } + } + if (Gia_ManLevelNum(tmp) <= nlevel) { + es_man = eSLIMCirMan(tmp); + } + } + + Resyn2Inprocessor::Resyn2Inprocessor(eSLIMConfig& config) + // : config(config) + { + } + + void Resyn2Inprocessor::runInprocessing(eSLIMCirMan& es_man) { + Gia_Man_t* pGia = es_man.eSLIMCirManToGia(); + int nand = Gia_ManAndNum(pGia); + int nlevel = Gia_ManLevelNum(pGia); + char * resyn2 = "balance; rewrite; rewrite -z; balance; rewrite -z; balance"; + bool reset_batch_mode = false ; + if ( !Abc_FrameIsBatchMode() ) { + reset_batch_mode = true; + Abc_FrameSetBatchMode( 1 ); + } + Abc_FrameUpdateGia( Abc_FrameGetGlobalFrame(), pGia ); + if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&put") ) { + return; + } + if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), resyn2) ) { + Abc_Print( 1, "Something did not work out with the command \"%s\".\n", resyn2 ); + return; + } + if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&get") ) { + return; + } + if (reset_batch_mode) { + Abc_FrameSetBatchMode( 0 ); + } + Gia_Man_t* tmp = Abc_FrameReadGia(Abc_FrameGetGlobalFrame()); + if (Gia_ManAndNum(tmp) <= nand && Gia_ManLevelNum(tmp) <= nlevel) { + es_man = eSLIMCirMan(tmp); + } + } + + + + template + Circuitrepresentation* runeSLIM(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) { + return eSLIMRun::runeSLIM(cir, params); + } + + template + Circuitrepresentation* toggleWindowing(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) { + if (params->nWindows > 0) { + return runeSLIM>>(cir, params); + } else { + return runeSLIM>>(cir, params); + } + } + + + template + Circuitrepresentation* setTabooApproach(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) { + if (params->use_taboo_list) { + return toggleWindowing(cir, params); + } else { + return toggleWindowing(cir, params); + } + } + + template + Circuitrepresentation* setBiasApproach(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) { + if (params->criticial_path_selection_bias) { + return toggleWindowing(cir, params); + } else { + return setTabooApproach(cir, params); + } + } + + + + template + Circuitrepresentation* setSearchDirection(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) { + if (params->forward_search) { + return setBiasApproach(cir, params); + } else { + return setBiasApproach(cir, params); + } } - return pThis; + + template + Circuitrepresentation* setMinimizer(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) { + switch (params->synthesis_approach) { + case 0: + if (params->apply_inprocessing) { + if constexpr ( std::is_same_v ) { + return setSearchDirection(cir, params); + } else { + return setSearchDirection(cir, params); + } + } + return setSearchDirection(cir, params); + case 1: + if constexpr ( std::is_same_v ) { + if (params->apply_inprocessing) { + return setSearchDirection(cir, params); + } + } + return setSearchDirection(cir, params); + case 2: + if constexpr ( std::is_same_v ) { + if (params->apply_inprocessing) { + return setSearchDirection, AreaDelayMinimizer, SubcircuitNoFBValidator>(cir, params); + } + } + return setSearchDirection(cir, params); + case 3: + if constexpr ( std::is_same_v ) { + if (params->apply_inprocessing) { + return setSearchDirection, DelayMinimizer, SubcircuitNoFBValidator>(cir, params); + } + } + return setSearchDirection(cir, params); + default: + assert(false); + return NULL; + } + } +} + +template +Circuitrepresentation* runeSLIM(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) { + // Windowing does currently not support Deleyconstraints/Delayoptimisation + if (params->nWindows && params->synthesis_approach != 0) { + return NULL; + } + return eSLIM::setMinimizer(cir, params); +} + + +Gia_Man_t* applyeSLIM(Gia_Man_t * pGia, const eSLIM_ParamStruct* params) { + return runeSLIM(pGia, params); } -ABC_NAMESPACE_IMPL_END \ No newline at end of file +Abc_Ntk_t* applyelSLIM(Abc_Ntk_t * ntk, const eSLIM_ParamStruct* params) { + return runeSLIM(ntk, params); +} + +Abc_Ntk_t* applyetSLIM(Abc_Ntk_t * ntk, const eSLIM_ParamStruct* params) { + return runeSLIM(ntk, params); +} + + + + + + +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/eslim/eSLIM.h b/src/opt/eslim/eSLIM.h index 857e32f476..a0e96de71d 100644 --- a/src/opt/eslim/eSLIM.h +++ b/src/opt/eslim/eSLIM.h @@ -12,9 +12,9 @@ Affiliation [University of Freiburg] - Date [Ver. 1.0. Started - March 2025.] + Date [Ver. 1.0. Started - April 2026.] - Revision [$Id: eSLIM.h,v 1.00 2025/03/17 00:00:00 Exp $] + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] ***********************************************************************/ @@ -23,39 +23,52 @@ #include "misc/util/abc_namespaces.h" #include "aig/gia/gia.h" +#include "base/abc/abc.h" ABC_NAMESPACE_HEADER_START typedef struct eSLIM_ParamStruct_ eSLIM_ParamStruct; struct eSLIM_ParamStruct_ { - int forbidden_pairs; // Allow forbidden pairs in the selected subcircuits - int extended_normality_processing; // Additional checks for non normal subcircuits - int fill_subcircuits; // If a subcircuit has fewer than subcircuit_size_bound gates, try to fill it with rejected gates. + int fill_subcircuits; // If a subcircuit has fewer than subcircuit_max_size gates, try to fill it with rejected gates. int apply_strash; int fix_seed; int trial_limit_active; int apply_inprocessing; - + int synthesis_approach; + int seed; + int verbosity_level; + int approximate_relation; + int relation_tfo_bound; + int generate_relation_with_tfi_limit; + int relation_tfi_bound; + int use_taboo_list; + int forward_search; + int nWindows; + int window_size; + + int criticial_path_selection_bias; + + unsigned int timeout; // available time in seconds (soft limit) - unsigned int timeout_inprocessing; unsigned int iterations; // maximal number of iterations. No limit if 0 - unsigned int subcircuit_size_bound; // upper bound for the subcircuit sizes - unsigned int strash_intervall; - unsigned int nselection_trials; - unsigned int nruns; + unsigned int subcircuit_max_size; // upper bound for the subcircuit sizes + unsigned int additional_gates; // number of gates that may be introduced to reduce delay double expansion_probability; // the probability that a node is added to the subcircuit + unsigned int nruns; + + unsigned int strash_intervall; + unsigned int nselection_trials; - int mode; // 0: Cadical Incremental, 1: Kissat Oneshot - int seed; - int verbosity_level; + int gate_size; + int aig; }; void seteSLIMParams(eSLIM_ParamStruct* params); Gia_Man_t* applyeSLIM(Gia_Man_t * pGia, const eSLIM_ParamStruct* params); - Gia_Man_t* applyeSLIMIncremental(Gia_Man_t * pGia, const eSLIM_ParamStruct* params, unsigned int restarts, unsigned int deepsynTimeout); + Abc_Ntk_t* applyelSLIM(Abc_Ntk_t * ntk, const eSLIM_ParamStruct* params); //LUT-eSLIM ABC_NAMESPACE_HEADER_END diff --git a/src/opt/eslim/eSLIMMan.hpp b/src/opt/eslim/eSLIMMan.hpp index 22114dd093..562d33a804 100644 --- a/src/opt/eslim/eSLIMMan.hpp +++ b/src/opt/eslim/eSLIMMan.hpp @@ -26,67 +26,51 @@ #include #include "misc/util/abc_namespaces.h" -#include "misc/vec/vec.h" #include "aig/gia/gia.h" -#include "misc/util/utilTruth.h" // ioResub.h depends on utilTruth.h -#include "base/io/ioResub.h" -#include "aig/miniaig/miniaig.h" + +#include "eslimCirMan.hpp" +#include "relationGeneration.hpp" +#include "selectionStrategies.hpp" +#include "subcircuit.hpp" #include "utils.hpp" -#include "selectionStrategy.hpp" + ABC_NAMESPACE_CXX_HEADER_START namespace eSLIM { - template - class eSLIM_Man { + template + class eSLIM_Man { public: - static Gia_Man_t* applyeSLIM(Gia_Man_t * p, const eSLIMConfig& cfg, eSLIMLog& log); + static void applyeSLIM(eSLIMCirMan& cir, const eSLIMConfig& cfg, eSLIMLog& log); private: - eSLIM_Man(Gia_Man_t * gia_man, const eSLIMConfig& cfg, eSLIMLog& log); - Gia_Man_t* getCircuit(); - - void minimize(); - void findReplacement(); - Mini_Aig_t* findMinimumAig(const Subcircuit& subcir); + eSLIM_Man(eSLIMCirMan& es_man, const eSLIMConfig& cfg, eSLIMLog& log); - Vec_Wrd_t* getSimsIn(Abc_RData_t* relation); - Vec_Wrd_t* getSimsOut(Abc_RData_t* relation); - word getAllFalseBehaviour(const Subcircuit& subcir); - bool getAllFalseBehaviourRec(Gia_Obj_t * pObj); + void optimize(); - std::pair reduce( Vec_Wrd_t* vSimsDiv, Vec_Wrd_t* vSimsOut, const std::unordered_map>& forbidden_pairs, - int nVars, int nDivs, int nOuts, int initial_size); - Mini_Aig_t* computeReplacement( SynthesisEngine& syn_man, int size); - double getDynamicTimeout(int size); + void findReplacement(); + eSLIMCirMan computeReplacement(Subcircuit& subcir); + eSLIMCirMan synthesiseRelation(Subcircuit& subcir, const Relation& rel); - void insertReplacement(Mini_Aig_t* replacement, const Subcircuit& subcir); - std::vector processReplacement(Gia_Man_t* gia_man, Gia_Man_t* pNew, const Subcircuit& subcir, Mini_Aig_t* replacement, std::vector&& to_process, std::vector& replacement_values); - Vec_Int_t * processEncompassing(Gia_Man_t* gia_man, Gia_Man_t* pNew, Vec_Int_t* to_process); - int getInsertionLiteral(Gia_Man_t* gia_man, const Subcircuit& subcir, Mini_Aig_t* replacement, const std::vector& replacement_values, int fanin_lit); - Gia_Man_t* gia_man = nullptr; + eSLIMCirMan& es_man; + SelectionStrategy selection_strategy; const eSLIMConfig& cfg; eSLIMLog& log; - SelectionEngine subcircuit_selection; - + double relation_generation_time ; double synthesis_time ; - bool stopeSLIM=false; + bool stopeSLIM = false; + }; - template - inline Gia_Man_t* eSLIM_Man::getCircuit() { - return gia_man; - } } - ABC_NAMESPACE_CXX_HEADER_END #include "eSLIMMan.tpp" diff --git a/src/opt/eslim/eSLIMMan.tpp b/src/opt/eslim/eSLIMMan.tpp index 8eb46db5ca..f3f26c8034 100644 --- a/src/opt/eslim/eSLIMMan.tpp +++ b/src/opt/eslim/eSLIMMan.tpp @@ -14,79 +14,60 @@ Date [Ver. 1.0. Started - March 2025.] - Revision [$Id: eSLIMMan.tpp,v 1.00 2025/03/17 00:00:00 Exp $] + Revision [$Id: eSLIMMan.hpp,v 1.00 2025/03/17 00:00:00 Exp $] ***********************************************************************/ -#include -#include -#include -#include -#include -#include #include #include "eSLIMMan.hpp" -#include "synthesisEngine.hpp" -#include "relationGeneration.hpp" - - -ABC_NAMESPACE_HEADER_START - Mini_Aig_t * Mini_AigDupCompl( Mini_Aig_t * p, int ComplIns, int ComplOuts ); - int Exa6_ManFindPolar( word First, int nOuts ); - Vec_Wrd_t * Exa6_ManTransformInputs( Vec_Wrd_t * vIns ); - Vec_Wrd_t * Exa6_ManTransformOutputs( Vec_Wrd_t * vOuts, int nOuts ); -ABC_NAMESPACE_HEADER_END +#include "synthesisEngines.hpp" ABC_NAMESPACE_CXX_HEADER_START - namespace eSLIM { - template - Gia_Man_t* eSLIM_Man::applyeSLIM(Gia_Man_t * p, const eSLIMConfig& cfg, eSLIMLog& log) { - eSLIM_Man eslim(p, cfg, log); - eslim.minimize(); - return eslim.getCircuit(); + template + void eSLIM_Man::applyeSLIM(eSLIMCirMan& cir, const eSLIMConfig& cfg, eSLIMLog& log) { + eSLIM_Man eslim(cir, cfg, log); + eslim.optimize(); } - - - template - eSLIM_Man::eSLIM_Man(Gia_Man_t * pGia, const eSLIMConfig& cfg, eSLIMLog& log) - : gia_man(pGia), cfg(cfg), log(log), - subcircuit_selection(gia_man, cfg, log) { + template + eSLIM_Man::eSLIM_Man(eSLIMCirMan& es_man, const eSLIMConfig& cfg, eSLIMLog& log) + : es_man(es_man), selection_strategy(this->es_man, cfg, log), cfg(cfg), log(log) { if (cfg.fix_seed) { - subcircuit_selection.setSeed(cfg.seed); + selection_strategy.setSeed(cfg.seed); } relation_generation_time = log.relation_generation_time; synthesis_time = log.synthesis_time; } - - template - void eSLIM_Man::minimize() { + + + + template + void eSLIM_Man::optimize() { abctime clkStart = Abc_Clock(); abctime nTimeToStop = clkStart + cfg.timeout * CLOCKS_PER_SEC; unsigned int iteration = 0; unsigned int iterMax = cfg.iterations ? cfg.iterations - 1 : UINT_MAX; - int current_size = Gia_ManAndNum(gia_man); + int current_size = es_man.getNofGates(); + const char* node_type = cfg.aig ? "#and" : "#nd"; while (Abc_Clock() <= nTimeToStop && iteration <= iterMax && !stopeSLIM) { iteration++; findReplacement(); - if (Gia_ManHasDangling(gia_man)) { - Gia_Man_t* pTemp = gia_man; - gia_man = Gia_ManCleanup( pTemp ); - Gia_ManStop( pTemp ); - } if (cfg.apply_strash && iteration % cfg.strash_intervall == 0) { - Gia_Man_t* pTemp = gia_man; - gia_man = Gia_ManRehash( pTemp, 0 ); + Gia_Man_t* pTemp = es_man.eSLIMCirManToGia(); + Gia_Man_t* gia_man = Gia_ManRehash( pTemp, 0 ); Gia_ManStop( pTemp ); + es_man = eSLIMCirMan(gia_man); + Gia_ManStop( gia_man ); + selection_strategy.reset(); } if (cfg.verbosity_level > 0) { - int sz = Gia_ManAndNum(gia_man); + int sz = es_man.getNofGates(); if (sz < current_size) { current_size = sz; - printf("\rIteration %8d : #and = %7d elapsed time = %7.2f sec\n", iteration, sz, (float)1.0*(Abc_Clock() - clkStart)/CLOCKS_PER_SEC); + printf("\rIteration %8d : %s = %7d elapsed time = %7.2f sec\n", iteration, node_type, sz, (float)1.0*(Abc_Clock() - clkStart)/CLOCKS_PER_SEC); } else { printf("\rIteration %8d", iteration); fflush(stdout); @@ -95,8 +76,8 @@ namespace eSLIM { } log.iteration_count += iteration; if (cfg.verbosity_level > 0) { - int sz = Gia_ManAndNum(gia_man); - printf("\r#Iterations %8d #and = %7d elapsed time = %7.2f sec\n", iteration, sz, (float)1.0*(Abc_Clock() - clkStart)/CLOCKS_PER_SEC); + int sz = es_man.getNofGates(); + printf("\r#Iterations %8d %s = %7d elapsed time = %7.2f sec\n", iteration, node_type, sz, (float)1.0*(Abc_Clock() - clkStart)/CLOCKS_PER_SEC); if (cfg.verbosity_level > 1) { printf("Relation generation time: %.2f sec\n", log.relation_generation_time - relation_generation_time); printf("Synthesis time: %.2f sec\n", log.synthesis_time - synthesis_time); @@ -104,379 +85,57 @@ namespace eSLIM { } } - template - Vec_Wrd_t* eSLIM_Man::getSimsIn(Abc_RData_t* relation) { - Vec_Wrd_t* vSimsDiv = Vec_WrdStart( relation->nPats ); - for (int k = 0; k < relation->nIns; k++) { - for (int i = 0; i < relation->nPats; i++) { - if (Abc_RDataGetIn(relation, k, i)) { - // The first bit corresponds to the constant divisor thus we have +1 - Abc_InfoSetBit((unsigned *)Vec_WrdEntryP(vSimsDiv, i), 1+k); - } - } - } - return vSimsDiv; - } - - template - Vec_Wrd_t* eSLIM_Man::getSimsOut(Abc_RData_t* relation) { - Vec_Wrd_t* vSimsOut = Vec_WrdStart(relation->nPats); - for (int k = 0; k < (1 << relation->nOuts); k++) { - for (int i = 0; i < relation->nPats; i++) { - if (Abc_RDataGetOut(relation, k, i)) { - Abc_InfoSetBit((unsigned *)Vec_WrdEntryP(vSimsOut, i), k); - } - } - } - return vSimsOut; - } - - template - void eSLIM_Man::findReplacement() { - Subcircuit subcir = subcircuit_selection.getSubcircuit(); - if (!subcircuit_selection.getStatus()) { - if (cfg.trial_limit_active) { - stopeSLIM = true; - } - return; - } - Mini_Aig_t* replacement = findMinimumAig(subcir); - if (replacement != nullptr) { - insertReplacement(replacement, subcir); - Mini_AigStop(replacement); - } - subcir.free(); - } - - // Based on Exa_ManExactSynthesis6Int and Exa_ManExactSynthesis6 - template - Mini_Aig_t* eSLIM_Man::findMinimumAig(const Subcircuit& subcir) { - - if (subcir.forbidden_pairs.size() > 0) { - log.subcircuits_with_forbidden_pairs++; - } - + template + eSLIMCirMan eSLIM_Man::computeReplacement(Subcircuit& subcir) { abctime relation_generation_start = Abc_Clock(); - Abc_RData_t* relation = RelationGenerator::computeRelation(gia_man, subcir); + Relation rel = RelationGenerator::computeRelation(es_man, subcir, cfg, log); log.relation_generation_time += (double)1.0*(Abc_Clock() - relation_generation_start)/CLOCKS_PER_SEC; - - int nDivs = 0; - int nOuts = relation->nOuts; - int nVars = relation->nIns; - if ( nVars == 0 ) { - return nullptr; + log.cummulative_relation_generation_times_per_size[subcir.nodes.size()] += (double)1.0*(Abc_Clock() - relation_generation_start) / CLOCKS_PER_SEC; + log.nof_relation_generations_per_size[subcir.nodes.size()]++; + if (!rel.getStatus()) { + return eSLIMCirMan(0); } - // assert (nVars <= 8); - Vec_Wrd_t* vSimsDiv = getSimsIn(relation); - Vec_Wrd_t* vSimsOut = getSimsOut(relation); - - word original_all_false_behaviour = vSimsOut->pArray[0]; - // If the original circuit computed a non-normal circuit, but the relations allows to set the outputs to false in the all inputs false case - // ABC does not "negate" the relation but forces the circuit to yield (0,...,0) for (0,...,0). - // As a consequence it is not necessarily possible to find a same sized circuit. - // In order to prevent this behaviour, we first modify the relation such that for the (0,...,0) input pattern only the original output patterns is allowed. - vSimsOut->pArray[0] = getAllFalseBehaviour(subcir); - - int DivCompl = (int)Vec_WrdEntry(vSimsDiv, 0) >> 1; - int OutCompl = Exa6_ManFindPolar( Vec_WrdEntry(vSimsOut, 0), nOuts ); - Vec_Wrd_t* vSimsDiv2 = Exa6_ManTransformInputs( vSimsDiv ); - Vec_Wrd_t* vSimsOut2 = Exa6_ManTransformOutputs( vSimsOut, nOuts ); - Mini_Aig_t* pMini = nullptr; - int original_size = Vec_IntSize(subcir.nodes); - int size = original_size; - - if (log.nof_analyzed_circuits_per_size.size() < original_size + 1) { - log.nof_analyzed_circuits_per_size.resize(original_size + 1, 0); - log.nof_replaced_circuits_per_size.resize(original_size + 1, 0); - log.nof_reduced_circuits_per_size.resize(original_size + 1, 0); - } - log.nof_analyzed_circuits_per_size[original_size]++; - abctime synthesis_start = Abc_Clock(); - std::tie(size, pMini) = reduce(vSimsDiv2, vSimsOut2, subcir.forbidden_pairs, nVars, nDivs, nOuts, size); + eSLIMCirMan replacement = synthesiseRelation(subcir, rel); log.synthesis_time += (double)1.0*(Abc_Clock() - synthesis_start)/CLOCKS_PER_SEC; - - if (size > original_size) { - // Could not find a replacement. This can be caused by a timeout. - Abc_RDataStop(relation); - Vec_WrdFreeP( &vSimsDiv ); - Vec_WrdFreeP( &vSimsOut ); - Vec_WrdFreeP( &vSimsDiv2 ); - Vec_WrdFreeP( &vSimsOut2 ); - return nullptr; - } - - // It is possible that a better replacement is obtained with a non normal behaviour. - bool check_original_all_false_behaviour = cfg.extended_normality_processing && vSimsOut->pArray[0] != original_all_false_behaviour; - if (check_original_all_false_behaviour) { - vSimsOut->pArray[0] = original_all_false_behaviour; - int OutCompl_alt = Exa6_ManFindPolar( Vec_WrdEntry(vSimsOut, 0), nOuts ); - Vec_Wrd_t* vSimsOut2_alt = Exa6_ManTransformOutputs( vSimsOut, nOuts ); - abctime synthesis_start = Abc_Clock(); - // auto [sz, mini] = reduce(vSimsDiv2, vSimsOut2_alt, subcir.forbidden_pairs, nVars, nDivs, nOuts, size - 1, true); - auto result = reduce(vSimsDiv2, vSimsOut2_alt, subcir.forbidden_pairs, nVars, nDivs, nOuts, size - 1); - int sz = result.first; - Mini_Aig_t* mini = result.second; - log.synthesis_time += (double)1.0*(Abc_Clock() - synthesis_start)/CLOCKS_PER_SEC; - if (mini != nullptr) { - size = sz; - if (pMini) { - Mini_AigStop(pMini); - } - pMini = mini; - OutCompl = OutCompl_alt; - } - } - Abc_RDataStop(relation); - - if (pMini != nullptr) { - Mini_Aig_t* pTemp = pMini; - pMini = Mini_AigDupCompl(pTemp, DivCompl, OutCompl); - Mini_AigStop(pTemp); - - log.nof_replaced_circuits_per_size[original_size]++; - if (size < original_size) { - log.nof_reduced_circuits_per_size[original_size]++; - } - } - Vec_WrdFreeP( &vSimsDiv ); - Vec_WrdFreeP( &vSimsOut ); - Vec_WrdFreeP( &vSimsDiv2 ); - Vec_WrdFreeP( &vSimsOut2 ); - return pMini; + return replacement; } - template - word eSLIM_Man::getAllFalseBehaviour(const Subcircuit& subcir) { - Gia_ManIncrementTravId(gia_man); - Gia_Obj_t * pObj; - int i; - Gia_ManForEachObjVec( subcir.nodes, gia_man, pObj, i ) { - Gia_ObjSetTravIdCurrent(gia_man, pObj); - } - long assm_idx = 0; - Gia_ManForEachObjVecStart(subcir.io, gia_man, pObj, i, subcir.nof_inputs) { - int bit_idx = i - subcir.nof_inputs; - if (getAllFalseBehaviourRec(pObj)) { - assm_idx |= (1 << bit_idx); - } - } - return 1L << assm_idx; - } - template - bool eSLIM_Man::getAllFalseBehaviourRec(Gia_Obj_t * pObj) { - bool valuefanin0, valuefanin1; - if (Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin0(pObj))) { - valuefanin0 = getAllFalseBehaviourRec(Gia_ObjFanin0(pObj)); - } else { - valuefanin0 = false; - } - valuefanin0 = valuefanin0 != pObj->fCompl0; - if (Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin1(pObj))) { - valuefanin1 = getAllFalseBehaviourRec(Gia_ObjFanin1(pObj)); - } else { - valuefanin1 = false; - } - valuefanin1 = valuefanin1 != pObj->fCompl1; - return valuefanin0 && valuefanin1; + template + eSLIMCirMan eSLIM_Man::synthesiseRelation(Subcircuit& subcir, const Relation& rel) { + return SynthesisEngine::optimize(es_man, rel, subcir, cfg, log); } - template - int eSLIM_Man::getInsertionLiteral(Gia_Man_t* gia_man, const Subcircuit& subcir, Mini_Aig_t* replacement, const std::vector& replacement_values, int fanin_lit) { - int fanin_idx = Abc_Lit2Var(fanin_lit); - bool fanin_negated = Abc_LitIsCompl(fanin_lit); - int fanin_value; - if (fanin_idx == 0) { // constant fanin - fanin_value = 0; - } else if (fanin_idx <= subcir.nof_inputs) { - Gia_Obj_t* pObj = Gia_ManObj(gia_man, Vec_IntEntry(subcir.io, fanin_idx - 1)); - if (Gia_ObjIsTravIdCurrent(gia_man, pObj)) { //the node has already been added - fanin_value = Gia_ManObj(gia_man, Vec_IntEntry(subcir.io, fanin_idx - 1))->Value; - } else { - return -1; - } - } else { - if (replacement_values[fanin_idx - subcir.nof_inputs - 1] == -1) { - return -1; - } - fanin_value = replacement_values[fanin_idx - subcir.nof_inputs - 1]; - } - return Abc_LitNotCond( fanin_value, fanin_negated ); - } - template - std::vector eSLIM_Man::processReplacement(Gia_Man_t* gia_man, Gia_Man_t* pNew, const Subcircuit& subcir, Mini_Aig_t* replacement, std::vector&& to_process, std::vector& replacement_values) { - std::vector to_process_next_it; - for (int id : to_process) { - int fanin0 = getInsertionLiteral(gia_man, subcir, replacement, replacement_values, Mini_AigNodeFanin0( replacement, id)); - int fanin1 = getInsertionLiteral(gia_man, subcir, replacement, replacement_values, Mini_AigNodeFanin1( replacement, id)); - if (fanin0 == -1 || fanin1 == -1) { - to_process_next_it.push_back(id); - } else { - int idx = id - subcir.nof_inputs - 1; - replacement_values[idx] = Gia_ManAppendAnd2(pNew, fanin0, fanin1); - } + template + void eSLIM_Man::findReplacement() { + bool status = true; + if constexpr (SelectionStrategy::requiresRemainingTimes() || isDelayEngine::value) { + es_man.setupTimings(); } - int i, j = 0; - Mini_AigForEachPo(replacement, i) { - int old_id = Vec_IntEntry(subcir.io, j + subcir.nof_inputs); - j++; - Gia_Obj_t* obj = Gia_ManObj(gia_man, old_id); - int fanin0_lit = Mini_AigNodeFanin0( replacement, i); - int fanin0_idx = Abc_Lit2Var(fanin0_lit); - if (fanin0_idx == 0) { // constant fanin - obj->Value = Gia_ManConst0(gia_man)->Value; - } else if (fanin0_idx <= subcir.nof_inputs) { - Gia_Obj_t* fanin_obj = Gia_ManObj(gia_man, Vec_IntEntry(subcir.io, fanin0_idx - 1)); - if (Gia_ObjIsTravIdCurrent(gia_man, fanin_obj)) { - obj->Value = fanin_obj->Value; - } else { - continue; - } - } else { - if (replacement_values[fanin0_idx - subcir.nof_inputs - 1] == -1 ) { - continue; - } else { - obj->Value = replacement_values[fanin0_idx - subcir.nof_inputs - 1]; - } - } - Gia_ObjSetTravIdCurrent(gia_man, obj); - bool fanin0_negated = Abc_LitIsCompl(fanin0_lit); - if (fanin0_negated) { - obj->fMark0 = 1; - } - } - return to_process_next_it; - } - - template - Vec_Int_t * eSLIM_Man::processEncompassing(Gia_Man_t* gia_man, Gia_Man_t* pNew, Vec_Int_t * to_process) { - int i; - Gia_Obj_t * pObj; - Vec_Int_t * to_process_remaining = Vec_IntAlloc( Vec_IntSize(to_process) ); - Gia_ManForEachObjVec( to_process, gia_man, pObj, i ) { - if (Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin0(pObj)) && Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin1(pObj))) { - Gia_ObjSetTravIdCurrent(gia_man, pObj); - bool fanin0_negated = Gia_ObjFaninC0(pObj) ^ Gia_ObjFanin0(pObj)->fMark0; - bool fanin1_negated = Gia_ObjFaninC1(pObj) ^ Gia_ObjFanin1(pObj)->fMark0; - int fanin0 = Abc_LitNotCond(Gia_ObjFanin0(pObj)->Value, fanin0_negated); - int fanin1 = Abc_LitNotCond(Gia_ObjFanin1(pObj)->Value, fanin1_negated); - pObj->Value = Gia_ManAppendAnd2( pNew, fanin0, fanin1); - } else { - Vec_IntPush(to_process_remaining, Gia_ObjId(gia_man, pObj)); - } - } - Vec_IntFree(to_process); - return to_process_remaining; - } - - template - void eSLIM_Man::insertReplacement(Mini_Aig_t* replacement, const Subcircuit& subcir) { - // A miniaig contains a constant node, nodes for each PI/PO and for each and. - int repalcement_size = Mini_AigNodeNum(replacement) - Vec_IntSize(subcir.io) - 1; - int size_diff = Vec_IntSize(subcir.nodes) - repalcement_size; - Gia_Man_t * pNew = Gia_ManStart( Gia_ManObjNum(gia_man) - size_diff ); - pNew->pName = Abc_UtilStrsav( gia_man->pName ); - pNew->pSpec = Abc_UtilStrsav( gia_man->pSpec ); - Gia_Obj_t * pObj; - int i; - Gia_ManConst0(gia_man)->Value = 0; - Gia_ManForEachPi( gia_man, pObj, i ) { - pObj->Value = Gia_ManAppendCi(pNew); - } - Gia_ManIncrementTravId(gia_man); - Gia_ManForEachObjVec( subcir.nodes, gia_man, pObj, i ) { - Gia_ObjSetTravIdCurrent(gia_man, pObj); - } - Gia_ManIncrementTravId(gia_man); - Gia_ManForEachPi( gia_man, pObj, i ) { - Gia_ObjSetTravIdCurrent(gia_man, pObj); - } - Vec_Int_t * to_process_encompassing = Vec_IntAlloc( gia_man->nObjs ); - Gia_ManForEachAnd( gia_man, pObj, i ) { - // nodes from the subcircuit shall not be added - if (Gia_ObjIsTravIdPrevious(gia_man, pObj)) { - continue; - } else if (Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin0(pObj)) && Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin1(pObj))) { - pObj->Value = Gia_ManAppendAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); - Gia_ObjSetTravIdCurrent(gia_man, pObj); - } else { - Vec_IntPush(to_process_encompassing, Gia_ObjId(gia_man, pObj)); - } - } - std::vector to_process_replacement; - Mini_AigForEachAnd(replacement, i) { - to_process_replacement.push_back(i); - } - std::vector replacement_values(to_process_replacement.size(), -1); - - while (Vec_IntSize(to_process_encompassing) > 0 || to_process_replacement.size() > 0) { - to_process_replacement = processReplacement(gia_man, pNew, subcir, replacement, std::move(to_process_replacement), replacement_values); - to_process_encompassing = processEncompassing(gia_man, pNew, to_process_encompassing); - } - Vec_IntFree(to_process_encompassing); - int po_idx = 0; - Mini_AigForEachPo(replacement, i) { - Gia_Obj_t* pObj = Gia_ManObj(gia_man, Vec_IntEntry(subcir.io, subcir.nof_inputs + po_idx)); - if (!Gia_ObjIsTravIdCurrent(gia_man, pObj)) { - int fanin_lit = Mini_AigNodeFanin0( replacement, i); - int fanin_idx = Abc_Lit2Var(fanin_lit); - assert (fanin_idx <= subcir.nof_inputs); - Gia_Obj_t* fanin_obj = Gia_ManObj(gia_man, Vec_IntEntry(subcir.io, fanin_idx - 1)); - pObj->Value = fanin_obj->Value; - if (Abc_LitIsCompl(fanin_lit)) { - pObj->fMark0 = 1; - } - Gia_ObjSetTravIdCurrent(gia_man, pObj); - } - po_idx++; - } - - Gia_ManForEachPo( gia_man, pObj, i ) { - assert(Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin0(pObj))); - bool fanin_negated = Gia_ObjFaninC0(pObj) ^ Gia_ObjFanin0(pObj)->fMark0; - int fanin0 = Abc_LitNotCond(Gia_ObjFanin0(pObj)->Value, fanin_negated); - pObj->Value = Gia_ManAppendCo( pNew, fanin0 ); - } - Gia_ManStop(gia_man); - gia_man = pNew; - } - - template - std::pair eSLIM_Man::reduce(Vec_Wrd_t* vSimsDiv, Vec_Wrd_t* vSimsOut, const std::unordered_map>& forbidden_pairs, - int nVars, int nDivs, int nOuts, int size) { - Y synth_man(vSimsDiv,vSimsOut,nVars,1+nVars+nDivs,nOuts,size, forbidden_pairs, log, cfg); - Mini_Aig_t* result = nullptr; - while (size >= 0) { - Mini_Aig_t* pTemp = computeReplacement(synth_man, size); - if (pTemp) { - if (result) { - Mini_AigStop(result); - } - result = pTemp; - size--; - } else { - break; + Subcircuit subcir = selection_strategy.getSubcircuit(status); + if (!status) { + if (cfg.trial_limit_active) { + stopeSLIM = true; } + return; } - return std::make_pair(size + 1, result); - } + eSLIMCirMan replacement = computeReplacement(subcir); - template - double eSLIM_Man::getDynamicTimeout(int size) { - if (this->log.nof_sat_calls_per_size[size] > cfg.minimum_dynamic_timeout_sample_size) { - return std::max(static_cast(cfg.minimum_sat_timeout), static_cast(log.cummulative_sat_runtimes_per_size[size]) / (1000*log.nof_sat_calls_per_size[size])); //logged timings are given in ms - } else { - return cfg.base_sat_timeout; + if (log.nof_analyzed_circuits_per_size.size() < subcir.nodes.size() + 1) { + log.nof_analyzed_circuits_per_size.resize(subcir.nodes.size() + 1, 0); + log.nof_replaced_circuits_per_size.resize(subcir.nodes.size() + 1, 0); + log.nof_reduced_circuits_per_size.resize(subcir.nodes.size() + 1, 0); } - } - - template - Mini_Aig_t* eSLIM_Man::computeReplacement(Y& syn_man, int size) { - return syn_man.getCircuit(size, getDynamicTimeout(size)); + log.nof_analyzed_circuits_per_size[subcir.nodes.size()]++; + log.subcircuits_with_forbidden_pairs += !subcir.forbidden_pairs.empty(); + if (replacement.getNofObjs() > 1) { //replacement found + log.nof_replaced_circuits_per_size[subcir.nodes.size()]++; + log.nof_reduced_circuits_per_size[subcir.nodes.size()] += (replacement.getNofGates() < subcir.nodes.size()); + es_man.replace(replacement, subcir); + } } } - ABC_NAMESPACE_CXX_HEADER_END \ No newline at end of file diff --git a/src/opt/eslim/eslimCirMan.cpp b/src/opt/eslim/eslimCirMan.cpp new file mode 100644 index 0000000000..38e1635cd1 --- /dev/null +++ b/src/opt/eslim/eslimCirMan.cpp @@ -0,0 +1,1121 @@ +/**CFile**************************************************************** + + FileName [eslimCirMan.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Internal circuit representation.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#include +#include +#include +#include + +#include "eslimCirMan.hpp" +#include "subcircuit.hpp" +#include "tabooList.hpp" +#include "utils.hpp" + +#include "map/mio/mio.h" + +ABC_NAMESPACE_HEADER_START + void Mio_IntallSimpleLibrary(); + void * Abc_FrameReadLibGen(); +ABC_NAMESPACE_HEADER_END + +ABC_NAMESPACE_IMPL_START +namespace eSLIM { + + eSLIMCirObj::eSLIMCirObj(int id, std::vector&& fanins) + : node_id(id), fanins(fanins) { + } + + int eSLIMCirObj::getNFanins() const{ + return fanins.size(); + } + + int eSLIMCirObj::getTTLength() const { + return static_cast(1) << getNFanins(); + } + + unsigned int eSLIMCirObj::getId() const { + return id; + } + + + eSLIMCirMan::eSLIMCirMan(int nObjEstimate) { + nodes.reserve(1 + nObjEstimate); + std::unique_ptr pobj = std::make_unique(0, std::vector()); // const false node + nodes.push_back(std::move(pobj)); + } + + eSLIMCirMan::eSLIMCirMan(eSLIMCirMan& es_man, const Subcircuit& scir) + : eSLIMCirMan(scir.inputs.size() + scir.outputs.size() + scir.nodes.size()) { + for (int i = 0; i < scir.inputs.size(); i++) { + es_man.nodes[scir.inputs[i]]->value1 = addPi(); + } + for (int nd : scir.nodes) { + std::vector fanins; + for (auto f : es_man.nodes[nd]->fanins) { + fanins.push_back(f->value1); + } + es_man.nodes[nd]->value1 = addNode(fanins, es_man.nodes[nd]->tt); + } + for (int i = 0; i < scir.outputs.size(); i++) { + addPo(es_man.nodes[scir.outputs[i]]->value1,false); + } + } + + eSLIMCirMan::eSLIMCirMan(Gia_Man_t * pGia) : eSLIMCirMan(Gia_ManObjNum(pGia)) { + Gia_ManConst0(pGia)->Value = 0; + Gia_Obj_t * pObj; + int i; + Gia_ManForEachPi( pGia, pObj, i ) { + pObj->Value = addPi(); + } + Gia_ManIncrementTravId(pGia); + + Gia_ManForEachAnd( pGia, pObj, i ) { + ABC_UINT64_T tt = ttFromGiaObj(pGia, pObj); + if (!ttisnormal(tt)) { + Gia_ObjSetTravIdCurrent(pGia, pObj); // marked nodes are considered negated + tt = negateTT(tt, 2); + } + pObj->Value = addNode({static_cast(Gia_ObjFanin0(pObj)->Value), static_cast(Gia_ObjFanin1(pObj)->Value)}, tt); + } + + Gia_ManForEachPo( pGia, pObj, i ) { + bool po_is_negated = Gia_ObjFaninC0(pObj) != Gia_ObjIsTravIdCurrent(pGia, Gia_ObjFanin0(pObj)); + pObj->Value = addPo(Gia_ObjFanin0(pObj)->Value, po_is_negated); + } + } + + eSLIMCirMan::eSLIMCirMan(Abc_Ntk_t * pNtk, bool simplify) : eSLIMCirMan(Abc_NtkObjNum(pNtk)) { + assert(Abc_NtkIsLogic(pNtk)); + assert(Abc_NtkHasSop(pNtk)); + + Abc_NtkIncrementTravId(pNtk); + + std::unordered_map ids; + Abc_Obj_t * pObj; + int i; + Abc_NtkForEachPi(pNtk, pObj, i) { + ids[pObj->Id] = addPi(); + } + + // Nodes are not necessarily topologically sorted. + // A po can be driven by another po + Abc_NtkForEachPo(pNtk, pObj, i) { + if (!Abc_ObjIsPo(Abc_ObjChild0(pObj))) { + insertNtkNodes(pNtk, Abc_ObjChild0(pObj)->Id, ids, simplify); + } + } + + // POs shall occur after the gates: + Abc_NtkForEachPo(pNtk, pObj, i) { + bool negated = false; + do { + int fc0 = Abc_ObjFaninC0(pObj); + pObj = Abc_ObjChild0(pObj); + negated ^= (fc0 ^ Abc_NodeIsTravIdCurrent(pObj)); + } while (Abc_ObjIsPo(pObj)); + addPo(ids.at(pObj->Id), negated); + } + } + + int eSLIMCirMan::insertNtkNodes(Abc_Ntk_t * pNtk, int node_id, std::unordered_map& ids, bool simplify) { + auto it = ids.find(node_id); + if (it != ids.end()) { + return it->second; + } + Abc_Obj_t * pObj = Abc_NtkObj(pNtk, node_id); + if (Vec_IntSize(&pObj->vFanins) == 0 ) { + ABC_UINT64_T tt = ttFromNtkObj(pNtk, pObj); + if (ttisnormal(tt)) { // const0 + ids[pObj->Id] = 0; + return 0; + } else if (simplify) { + ids[pObj->Id] = 0; + Abc_NodeSetTravIdCurrent(pObj); + return 0; + } + } + + std::vector fanins (Vec_IntSize(&pObj->vFanins), 0); + int j, f; + Vec_IntForEachEntry(&(pObj->vFanins), f, j) { + auto it = ids.find(f); + if (it == ids.end()) { + fanins[j] = insertNtkNodes(pNtk, f, ids, simplify); + } else { + fanins[j] = it->second; + } + } + + // the truth table must be computed after we traversed the fanins in order to take account for normalised gates + ABC_UINT64_T tt = ttFromNtkObj(pNtk, pObj); + if (simplify && Vec_IntSize(&pObj->vFanins) == 1) { + int id = fanins[0]; + if (!ttisnormal(tt)) { + Abc_NodeSetTravIdCurrent(pObj); + } + ids[pObj->Id] = id; + return id; + } + + // We normalise each gate + if (simplify && !ttisnormal(tt)) { + tt = negateTT(tt, Vec_IntSize(&pObj->vFanins)); + Abc_NodeSetTravIdCurrent(pObj); + } + int id = addNode(fanins, tt); + ids[pObj->Id] = id; + return id; + } + + + eSLIMCirObj* eSLIMCirMan::getpObj(int id) { + return nodes[id].get(); + } + + const eSLIMCirObj* eSLIMCirMan::getpObj(int id) const { + return nodes[id].get(); + } + + eSLIMCirObj* eSLIMCirMan::getPo(int id) { + return nodes[getNofObjs() - nof_pos - 1 + id].get(); + } + + const eSLIMCirObj& eSLIMCirMan::getObj(int id) const { + return *nodes[id].get(); + } + + int eSLIMCirMan::addPi() { + nof_pis++; + int id = nodes.size(); + std::unique_ptr pobj = std::make_unique(id, std::vector()); + pobj->tt = 2; + pobj->depth = 0; + pobj->id = last_node_id++; + nodes.push_back(std::move(pobj)); + return id; + } + + // If the fanin is negated we use the tt-member to indicate this negation + int eSLIMCirMan::addPo(int fanin, bool negated) { + nof_pos++; + // 1 represents negation and 2 projection + ABC_UINT64_T tt = negated ? 1 : 2; + // return addNode({fanin}, tt); + int id = addNode({fanin}, tt); + depth = std::max(depth, nodes.back()->depth); + return id; + } + + int eSLIMCirMan::addNode(const std::vector& fanins, ABC_UINT64_T tt) { + int id = nodes.size(); + std::vector pfan; + unsigned int max_fan_depth = 0; + for (int f : fanins) { + pfan.push_back(getpObj(f)); + max_fan_depth = pfan.back()->depth > max_fan_depth ? pfan.back()->depth : max_fan_depth; + } + std::unique_ptr pobj = std::make_unique(id, std::move(pfan)); + pobj->tt = tt; + pobj->depth = max_fan_depth + 1; + pobj->id = last_node_id++; + for (int f : fanins) { + getpObj(f)->fanouts.insert(pobj.get()); + } + nodes.push_back(std::move(pobj)); + return id; + } + + int eSLIMCirMan::getDepth() const { + return depth; + } + + void eSLIMCirMan::setupTimings() { + for (int i = 0; i < nodes.size(); i++) { + getpObj(i)->remaining_time = 0; + } + for (int i = nodes.size() - nof_pos - 1; i > 0; i--) { + eSLIMCirObj* pe = getpObj(i); + pe->remaining_time++; + for (int j = 0; j < pe->fanins.size(); j++) { + (pe->fanins[j])->remaining_time = std::max((pe->fanins[j])->remaining_time, pe->remaining_time); + } + } + } + + bool eSLIMCirMan::isConst(int id) const { + return id == 0; + } + + bool eSLIMCirMan::isConst(const eSLIMCirObj& obj) const { + return obj.node_id == 0; + } + + bool eSLIMCirMan::isPi(int id) const { + return id <= nof_pis; // 0 is the const0 node + } + + bool eSLIMCirMan::isPi(const eSLIMCirObj& obj) const { + return obj.node_id <= nof_pis; + } + + bool eSLIMCirMan::isOnCricticalPath(const eSLIMCirObj& obj) const { + return obj.depth + obj.remaining_time == depth; + } + + bool eSLIMCirMan::isPo(int id) const { + return id >= nodes.size() - nof_pos; + } + + bool eSLIMCirMan::isGate(int id) const { + return !isPi(id) && !isPo(id); + } + + int eSLIMCirMan::getNofObjs() const { + return nodes.size(); + } + + int eSLIMCirMan::getNofPis() const { + return nof_pis; + } + + int eSLIMCirMan::getNofPos() const { + return nof_pos; + } + + int eSLIMCirMan::getNofGates() const { + return nodes.size() - nof_pis - nof_pos - 1; + } + + void eSLIMCirMan::setTraversalId(eSLIMCirObj& obj) { + obj.trav_id = traversal_id; + } + + void eSLIMCirMan::setTraversalId(int n) { + getpObj(n)->trav_id = traversal_id; + } + + bool eSLIMCirMan::isCurrentTraversalId(int n) const { + return getpObj(n)->trav_id == traversal_id; + } + + bool eSLIMCirMan::isCurrentTraversalId(const eSLIMCirObj& obj) const { + return obj.trav_id == traversal_id; + } + + void eSLIMCirMan::setInTFI(eSLIMCirObj& obj) { + obj.inTFI = traversal_id; + } + + void eSLIMCirMan::setInTFO(eSLIMCirObj& obj) { + obj.inTFO = traversal_id; + } + + void eSLIMCirMan::setInSubcircuit(eSLIMCirObj& obj) { + obj.inSubcircuit = traversal_id; + } + + + bool eSLIMCirMan::inTFI(const eSLIMCirObj& obj) const { + return obj.inTFI == traversal_id; + } + + bool eSLIMCirMan::inTFO(const eSLIMCirObj& obj) const { + return obj.inTFO == traversal_id; + } + + bool eSLIMCirMan::inSubcircuit(const eSLIMCirObj& obj) const { + return obj.inSubcircuit == traversal_id; + } + + bool eSLIMCirMan::isTaboo(const eSLIMCirObj& obj, int taboo_time) const { + return obj.isTaboo != 0 && (traversal_id - obj.isTaboo < taboo_time); + } + + void eSLIMCirMan::setTaboo(eSLIMCirObj& obj) { + obj.isTaboo = traversal_id; + } + + void eSLIMCirMan::markSubcircuit(const std::vector& nodes) { + for (int n : nodes) { + setInSubcircuit(*getpObj(n)); + } + } + + void eSLIMCirMan::markCones(const std::vector& nodes) { + int min_node = *std::min_element(nodes.begin(), nodes.end()); + int max_node = *std::max_element(nodes.begin(), nodes.end()); + // mark TFI + for (int i = max_node; i >= getNofPis(); i--) { + if (inSubcircuit(*getpObj(i)) || inTFI(*getpObj(i))) { + for (auto& f : getpObj(i)->fanins) { + if (!inSubcircuit(*f)) { + setInTFI(*f); + } + } + } + } + // mark tfo + for (int i = min_node; i < getNofObjs(); i++) { + if (!inSubcircuit(*getpObj(i))) { + for (auto& f : getpObj(i)->fanins) { + if (inSubcircuit(*f) || inTFO(*f)) { + setInTFO(*getpObj(i)); + } + } + } + } + } + + ABC_UINT64_T eSLIMCirMan::ttFromGiaObj(Gia_Man_t * pGia, Gia_Obj_t * pObj) { + assert(Gia_ObjIsAnd(pObj)); + // A fanin is negated if is either negated in pGia or (exclusive) if it is marked + if (Gia_ObjFaninC0(pObj) != Gia_ObjIsTravIdCurrent(pGia, Gia_ObjFanin0(pObj))) { + if (Gia_ObjFaninC1(pObj) != Gia_ObjIsTravIdCurrent(pGia, Gia_ObjFanin1(pObj))) { + return 1; + } else { + return 4; + } + } else { + if (Gia_ObjFaninC1(pObj) != Gia_ObjIsTravIdCurrent(pGia, Gia_ObjFanin1(pObj))) { + return 2; + } else { + return 8; + } + } + } + + ABC_UINT64_T eSLIMCirMan::ttFromNtkObj(Abc_Ntk_t * pNtk, Abc_Obj_t * pObj) { + int j, f; + char * pSop = (char *)pObj->pData; + ABC_UINT64_T tt = sop2tt(pSop); + Vec_IntForEachEntry(&(pObj->vFanins), f, j) { + if (Abc_NodeIsTravIdCurrent(Abc_NtkObj(pNtk, f))) { + tt = ttNegateFanin(tt, j); + } + } + return tt; + } + + bool eSLIMCirMan::ttisnormal(ABC_UINT64_T tt) { + return !(tt & 1); + } + + ABC_UINT64_T eSLIMCirMan::negateTT(ABC_UINT64_T tt, unsigned int nfanin) { + assert (nfanin <= 6); + constexpr ABC_UINT64_T filters[7] {0x1ull, 0x3ull, 0xFull, 0xFFull, 0xFFFFull, 0xFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull}; + return ~tt & filters[nfanin]; + } + + ABC_UINT64_T eSLIMCirMan::ttNegateFanin(ABC_UINT64_T tt, unsigned int fan) { + assert (fan < 6); + constexpr ABC_UINT64_T filters1[6] = {0xAAAAAAAAAAAAAAAAull, 0xCCCCCCCCCCCCCCCCull, 0xF0F0F0F0F0F0F0F0ull, 0xFF00FF00FF00FF00ull, 0xFFFF0000FFFF0000ull, 0xFFFFFFFF00000000ull}; + constexpr ABC_UINT64_T filters2[6] = {0x5555555555555555ull, 0x3333333333333333ull, 0xF0F0F0F0F0F0F0Full, 0xFF00FF00FF00FFull, 0xFFFF0000FFFFull, 0xFFFFFFFFull}; + constexpr int shifts[6] = {1,2,4,8,16,32}; + ABC_UINT64_T x = tt & filters1[fan]; + ABC_UINT64_T y = tt & filters2[fan]; + return (x >> shifts[fan]) | (y << shifts[fan]); + } + + ABC_UINT64_T eSLIMCirMan::sop2tt(char * pSop) { + constexpr ABC_UINT64_T filters[7] {0x1ull, 0x3ull, 0xFull, 0xFFull, 0xFFFFull, 0xFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull}; + int nVars = Abc_SopGetVarNum(pSop); + ABC_UINT64_T tt = Abc_SopToTruth(pSop, nVars); + return tt & filters[nVars]; + } + + char* eSLIMCirMan::tt2sop(ABC_UINT64_T tt, int nVars, Mem_Flex_t * pMan) { + int bit_count = popcount(tt); + if (bit_count == 0) { + char* pSopCover = Abc_SopCreateConst0(pMan); + return pSopCover; + } else if (bit_count == 1 << nVars) { + char* pSopCover = Abc_SopCreateConst1(pMan); + return pSopCover; + } + char* pSopCover = Abc_SopStart( pMan, bit_count, nVars ); + int sop_id = 0; + for (int i = 0; i < (1 << nVars); i++) { + if ((tt >> i) & 1) { + for (int j = 0; j < nVars; j++) { + char bit = ((i >> j) & 1) ? '1' : '0'; + pSopCover[sop_id*(nVars + 3) + j] = bit; + } + pSopCover[sop_id*(nVars + 3) + nVars] = ' '; + pSopCover[sop_id*(nVars + 3) + nVars + 1] = '1'; + pSopCover[sop_id*(nVars + 3) + nVars + 2] = '\n'; + sop_id++; + } + } + pSopCover[sop_id*(nVars + 3)] = 0; + return pSopCover; + } + + + int eSLIMCirMan::addGiaGate(Gia_Man_t * pGia, int node_id, std::vector& node_ids, std::vector& is_node_negated) { + eSLIMCirObj* pe = getpObj(node_id); + // assert (pe->getNFanins() == 2); + int bit_count = popcount(pe->tt); + assert(ttisnormal(pe->tt)); + assert (bit_count > 0 || bit_count < 4); // the circuit shall not contain constants + ABC_UINT64_T tt = pe->tt; + bool negate_and = false; + bool is_xor = false; + bool fan1negated = false, fan2negated = false; + if (bit_count == 3) { // or gates are represented by negated and gates + tt = negateTT(pe->tt, 2); + negate_and = true; + fan1negated = true; + fan2negated = true; + } else if (pe->tt == 2) { + fan1negated = false; + fan2negated = true; + } else if (pe->tt == 4) { + fan1negated = true; + fan2negated = false; + } else if (pe->tt == 8) { + fan1negated = false; + fan2negated = false; + } else { + // XOR + assert (pe->tt == 6); + is_xor = true; + } + + int fan1 = node_ids[(pe->fanins[0])->node_id]; + int fan2 = node_ids[(pe->fanins[1])->node_id]; + + fan1negated = (fan1negated != is_node_negated[(pe->fanins[0])->node_id]); + fan2negated = (fan2negated != is_node_negated[(pe->fanins[1])->node_id]); + + int id; + if (is_xor) { + id = Gia_ManAppendXor(pGia, Abc_LitNotCond(fan1, fan1negated), Abc_LitNotCond(fan2, fan2negated)); + // id = Gia_ManAppendXorReal(pGia, Abc_LitNotCond(fan1, fan1negated), Abc_LitNotCond(fan2, fan2negated)); + } else { + id = Gia_ManAppendAnd(pGia, Abc_LitNotCond(fan1, fan1negated), Abc_LitNotCond(fan2, fan2negated)); + } + + + node_ids[node_id] = id; + is_node_negated[node_id] = negate_and; + + return id; + } + + + Gia_Man_t* eSLIMCirMan::eSLIMCirManToGia() { + simplifyDuplicateFanins(); + Gia_Man_t * pNew = Gia_ManStart( getNofObjs() ); + + std::vector node_ids(nodes.size(), 0); + std::vector is_node_negated(nodes.size(), false); + + for (int i = 1; i <= nof_pis; i++) { + int id = Gia_ManAppendCi(pNew); + node_ids[i] = id; + is_node_negated[i] = false; + } + + for (int i = nof_pis + 1; i < nodes.size() - nof_pos; i++) { + // It is possible (but rather unlikley) that a gate has only a single fanin + // For instance it is possible that internally a gate has duplicate fanins. + // simplifyDuplicateFanins removes duplicate fanins + if (nodes[i]->getNFanins() == 1) { + eSLIMCirObj* pe = getpObj(i); + node_ids[i] = node_ids[(pe->fanins[0])->node_id]; + is_node_negated[i] = pe->tt == 1; + continue; + } + assert(nodes[i]->getNFanins() == 2); + addGiaGate( pNew, i, node_ids, is_node_negated ); + } + + for (int i = nodes.size() - nof_pos; i < nodes.size(); i++) { + eSLIMCirObj* pobj = getpObj(i); + bool fan_negated = ((pobj->tt == 1) != is_node_negated.at((pobj->fanins[0])->node_id)); + int fanin0 = Abc_LitNotCond(node_ids[(pobj->fanins[0])->node_id], fan_negated); + int id = Gia_ManAppendCo( pNew, fanin0 ); + node_ids[i] = id; + } + + return pNew; + } + + Abc_Ntk_t* eSLIMCirMan::eSLIMCirManToNtk() { + simplifyDuplicateFanins(); + Abc_Ntk_t* pNtk = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_SOP, 1 ); + std::vector ntk_objs; + // constant0 + Abc_Obj_t* pObj = Abc_NtkCreateNodeConst0(pNtk); + ntk_objs.push_back(pObj); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + + for (int i = 1; i <= nof_pis; i++) { + Abc_Obj_t* pObj = Abc_NtkCreatePi(pNtk); + ntk_objs.push_back(pObj); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + } + for (int i = nof_pis + 1; i < nodes.size() - nof_pos; i++) { + Abc_Obj_t* pObj = Abc_NtkCreateNode( pNtk ); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + ntk_objs.push_back(pObj); + char* sop = tt2sop(nodes[i]->tt, nodes[i]->fanins.size(), (Mem_Flex_t *)pNtk->pManFunc); + pObj->pData = sop; + for (auto f : nodes[i]->fanins) { + int fid = f->node_id; + Abc_ObjAddFanin(pObj, ntk_objs[fid]); + } + } + for (int i = nodes.size() - nof_pos; i < nodes.size(); i++) { + Abc_Obj_t* fobj; + int fid = nodes[i]->fanins[0]->node_id; + // bool use_pi_name = false; + if (nodes[i]->tt == 1) { // a negated po + char* sop = tt2sop(1, 1, (Mem_Flex_t *)pNtk->pManFunc); + fobj = Abc_NtkCreateNode( pNtk ); + Nm_ManStoreIdName( pNtk->pManName, fobj->Id, fobj->Type, Abc_ObjName(fobj), NULL ); + fobj->pData = sop; + Abc_ObjAddFanin(fobj, ntk_objs[fid]); + } else { + // We add buffer to simplify the handling of the SimpleCos condition + char* sop = tt2sop(2, 1, (Mem_Flex_t *)pNtk->pManFunc); + fobj = Abc_NtkCreateNode( pNtk ); + Nm_ManStoreIdName( pNtk->pManName, fobj->Id, fobj->Type, Abc_ObjName(fobj), NULL ); + fobj->pData = sop; + Abc_ObjAddFanin(fobj, ntk_objs[fid]); + } + Abc_Obj_t* pObj = Abc_NtkCreatePo(pNtk); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + Abc_ObjAddFanin(pObj, fobj); + } + + assert( Abc_NtkCiNum(pNtk) == nof_pis ); + assert( Abc_NtkCoNum(pNtk) == nof_pos ); + assert( Abc_NtkLatchNum(pNtk) == 0 ); + assert( Abc_NtkCheck(pNtk) ); + assert( Abc_NtkLogicHasSimpleCos(pNtk)); + return pNtk; + } + + Abc_Ntk_t* eSLIMCirMan::eSLIMCirManToMappedNtk() { + + Mio_IntallSimpleLibrary(); + Mio_Library_t * pLib = (Mio_Library_t *)Abc_FrameReadLibGen(); + + Abc_Ntk_t* pNtk = Abc_NtkAlloc( ABC_NTK_LOGIC, ABC_FUNC_MAP, 1 ); + std::vector ntk_objs; + Abc_Obj_t* pObj = Abc_NtkCreateNode( pNtk ); + pObj->pData = Mio_LibraryReadGateByName( pLib, "zero", NULL ); + ntk_objs.push_back(pObj); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + Abc_Obj_t* const1 = Abc_NtkCreateNode( pNtk ); + const1->pData = Mio_LibraryReadGateByName( pLib, "one", NULL ); + // ntk_objs.push_back(pObj); + Nm_ManStoreIdName( pNtk->pManName, const1->Id, const1->Type, Abc_ObjName(const1), NULL ); + + for (int i = 1; i <= nof_pis; i++) { + Abc_Obj_t* pObj = Abc_NtkCreatePi(pNtk); + ntk_objs.push_back(pObj); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + } + + for (int i = nof_pis + 1; i < nodes.size() - nof_pos; i++) { + // Replace an inverted const false by const true + if (nodes[i]->tt == 1 && nodes[i]->fanins.size() == 1 && nodes[i]->fanins[0]->node_id == 0) { + ntk_objs.push_back(const1); + continue; + } + // if a gate has no fanins it must be const true (const false is handled separatley) + if (nodes[i]->fanins.size() == 0) { + assert (nodes[i]->tt == 1); + ntk_objs.push_back(const1); + continue; + } + Abc_Obj_t* pObj = Abc_NtkCreateNode( pNtk ); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + ntk_objs.push_back(pObj); + for (auto f : nodes[i]->fanins) { + int fid = f->node_id; + Abc_ObjAddFanin(pObj, ntk_objs[fid]); + } + switch (nodes[i]->tt) { + case 1: + if (nodes[i]->fanins.size() == 1) { + pObj->pData = Mio_LibraryReadGateByName( pLib, "inv", NULL ); + } else if (nodes[i]->fanins.size() == 2) { + pObj->pData = Mio_LibraryReadGateByName( pLib, "nor2", NULL ); + } else { + pObj->pData = Mio_LibraryReadGateByName( pLib, "nor3", NULL ); + } + break; + case 2: + pObj->pData = Mio_LibraryReadGateByName( pLib, "buf", NULL ); + break; + case 7: + if (nodes[i]->fanins.size() == 2) { + pObj->pData = Mio_LibraryReadGateByName( pLib, "nand2", NULL ); + } else { + pObj->pData = Mio_LibraryReadGateByName( pLib, "aoi21", NULL ); + } + break; + case 31: + pObj->pData = Mio_LibraryReadGateByName( pLib, "oai21", NULL ); + break; + case 127: + pObj->pData = Mio_LibraryReadGateByName( pLib, "nand3", NULL ); + break; + case 1911: + pObj->pData = Mio_LibraryReadGateByName( pLib, "aoi22", NULL ); + break; + case 4383: + pObj->pData = Mio_LibraryReadGateByName( pLib, "oai22", NULL ); + break; + + default: + std::cerr << "Error: A node with a function not part of the library was detected." << std::endl; + Abc_NtkDelete( pNtk ); + return NULL; + } + } + std::unordered_map negated_pos; + std::unordered_map buffers; + + for (int i = nodes.size() - nof_pos; i < nodes.size(); i++) { + Abc_Obj_t* fobj; + int fid = nodes[i]->fanins[0]->node_id; + if (nodes[i]->tt == 1) { // a negated po + if (fid == 0) { + fobj = const1; + } else { + if (negated_pos.find(fid) != negated_pos.end()) { + negated_pos[fid] = ntk_objs.size(); + Abc_Obj_t* pObj = Abc_NtkCreateNode( pNtk ); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + ntk_objs.push_back(pObj); + pObj->pData = Mio_LibraryReadGateByName( pLib, "inv", NULL ); + Abc_ObjAddFanin(pObj, ntk_objs[fid]); + } + fobj = ntk_objs[negated_pos[fid]]; + } + } else { + if (0 < fid && fid <= nof_pis) { + if (buffers.find(fid) == buffers.end()) { + Abc_Obj_t* pObj = Abc_NtkCreateNode( pNtk ); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + buffers[fid] = ntk_objs.size(); + ntk_objs.push_back(pObj); + pObj->pData = Mio_LibraryReadGateByName( pLib, "buf", NULL ); + Abc_ObjAddFanin(pObj, ntk_objs[fid]); + } + fobj = ntk_objs[buffers[fid]]; + } else { + fobj = ntk_objs[fid]; + } + } + Abc_Obj_t* pObj = Abc_NtkCreatePo(pNtk); + Nm_ManStoreIdName( pNtk->pManName, pObj->Id, pObj->Type, Abc_ObjName(pObj), NULL ); + Abc_ObjAddFanin(pObj, fobj); + } + + assert( Abc_NtkCiNum(pNtk) == nof_pis ); + assert( Abc_NtkCoNum(pNtk) == nof_pos ); + assert( Abc_NtkLatchNum(pNtk) == 0 ); + assert( Abc_NtkCheck(pNtk) ); + assert( Abc_NtkLogicHasSimpleCos(pNtk)); + return pNtk; + } + + void eSLIMCirMan::setMarks(eSLIMCirObj& pObj, int id) { + pObj.node_id = id; + pObj.depth = 0; + for (const auto& f : pObj.fanins) { + if (f->depth >= pObj.depth) { + pObj.depth = f->depth + 1; + } + } + setTraversalId(pObj); + } + + void eSLIMCirMan::moveNode(std::vector>& vec, std::unique_ptr& pobj) { + int id = vec.size(); + vec.push_back(std::move(pobj)); + pobj = nullptr; + setMarks(*vec.back(), id); + } + + void eSLIMCirMan::addFans(eSLIMCirObj* out, eSLIMCirObj* in, int fin_id) { + out->fanouts.insert(in); + in->fanins[fin_id] = out; + } + + void eSLIMCirMan::processNonGateRepFanin( eSLIMCirObj* obj, eSLIMCirObj* fan, int fid, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec) { + if (replacement.isConst(*fan)) { + addFans(sorted[0].get(), obj, fid); + } else { + int in_id = fan->node_id - 1; + addFans(in_vec[in_id], obj, fid); + insertSortedRec(in_vec[in_id], sorted, replacement, out_map, in_vec); + } + } + + void eSLIMCirMan::processRepFanin(eSLIMCirObj* obj, eSLIMCirObj* fan, int fid, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec) { + if (isCurrentTraversalId(*fan)) { + addFans(fan, obj, fid); + } else if (replacement.isPi(*fan)) { + processNonGateRepFanin(obj, fan, fid, sorted, replacement, out_map, in_vec); + } else { + fan->fanouts.clear(); // the ids changed, so we cannot reuse the set + addFans(fan, obj, fid); + insertSortedRecRep(fan, sorted, replacement, out_map, in_vec); + } + } + + void eSLIMCirMan::processRepPo( eSLIMCirObj* obj, eSLIMCirObj* scir_out, int fid, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec) { + int out_id = out_map.at(scir_out->node_id); + int out_node_id = replacement.nodes.size() - replacement.getNofPos() + out_id; + auto rf = replacement.nodes[out_node_id]->fanins[0]; + processRepFanin(obj, rf, fid, sorted, replacement, out_map, in_vec); + } + + void eSLIMCirMan::insertSorted( eSLIMCirObj* obj, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec) { + auto f = obj->fanins[0]; + if (inSubcircuit(*f)) { + processRepPo(obj, f, 0, sorted, replacement, out_map, in_vec); + } else { + insertSortedRec(f, sorted, replacement, out_map, in_vec); + } + } + + void eSLIMCirMan::insertSortedRec(eSLIMCirObj* obj, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec) { + if (isPi(*obj) || isCurrentTraversalId(*obj)) { + return; + } + for (int i = 0; i < obj->fanins.size(); i++) { + auto f = obj->fanins[i]; + if (inSubcircuit(*f)) { + processRepPo(obj, f, i, sorted, replacement, out_map, in_vec); + } else { + insertSortedRec(f, sorted, replacement, out_map, in_vec); + } + } + moveNode(sorted, nodes[obj->node_id]); + } + + void eSLIMCirMan::insertSortedRecRep( eSLIMCirObj* obj, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec) { + assert(!isCurrentTraversalId(*obj)); + for (int i = 0; i < obj->fanins.size(); i++) { + auto f = obj->fanins[i]; + if (!isCurrentTraversalId(*f)) { + if (replacement.isPi(*f)) { + processNonGateRepFanin(obj, f, i, sorted, replacement, out_map, in_vec); + } else { + f->fanouts.clear(); + f->fanouts.insert(obj); + insertSortedRecRep(f, sorted, replacement, out_map, in_vec); + } + } else { + f->fanouts.insert(obj); + } + } + if (taboo != nullptr) { + taboo->addNode(replacement.nodes[obj->node_id].get()); + } + moveNode(sorted, replacement.nodes[obj->node_id]); + } + + // We want to preserve the invariant that nodes are topological sorted. + // Even if we replace a subcircuit by a same sized circuit it is not necessarily possible + // to simply replace the old nodes by the new nodes, while preserving the order. + // We can only process circuits with thousands (maybe tenthousends) nodes. + // For such sizes the replacement should be reasonable fast. + void eSLIMCirMan::replace(eSLIMCirMan& replacement, const Subcircuit& subcir) { + clearFanouts(subcir); + setupMarksForReplacement(replacement); + if (taboo != nullptr) { + taboo->discardSubcircuit(subcir); + } + std::vector> nodes_aux = replaceInternal(replacement, subcir); + int size_diff = subcir.nodes.size() - replacement.getNofGates(); + if (nodes_aux.size() + size_diff != nodes.size()) { + int nredundant = processRedundant(subcir); + assert(nodes_aux.size() + size_diff + nredundant == nodes.size()); + } + std::swap(nodes, nodes_aux); + } + + void eSLIMCirMan::setupMarksForReplacement(eSLIMCirMan& replacement) { + for (int i = replacement.getNofPis() + 1; i < replacement.getNofObjs() - replacement.getNofPos(); i++) { + replacement.nodes[i]->isTaboo = traversal_id; + replacement.nodes[i]->id = last_node_id++; + } + } + + void eSLIMCirMan::clearFanouts(const Subcircuit& subcir) { + for (int n : subcir.nodes) { + eSLIMCirObj* obj = nodes[n].get(); + nodes[0]->fanouts.erase(obj); + for (int i : subcir.inputs) { + nodes[i]->fanouts.erase(obj); + } + } + } + + std::vector> eSLIMCirMan::replaceInternal(eSLIMCirMan& replacement, const Subcircuit& subcir) { + std::unordered_map out_map; + for (int i = 0; i < subcir.outputs.size(); i++) { + out_map[subcir.outputs[i]] = i; + } + std::vector invec; + for (int i = 0; i < subcir.inputs.size(); i++) { + invec.push_back(nodes[subcir.inputs[i]].get()); + } + + int size_diff = subcir.nodes.size() - replacement.getNofGates(); + std::vector> nodes_aux; + nodes_aux.reserve(getNofObjs() - size_diff); + for (int i = 0; i <= getNofPis(); i++) { // "<=" because of the constant node + moveNode(nodes_aux, nodes[i]); + } + + // it is possible that after the insertion no Po is reachable from some nodes. + // These nodes can be remove. + for (int i = 1; i <= getNofPos(); i++) { + insertSorted(nodes[nodes.size() - i].get(), nodes_aux, replacement, out_map, invec); + } + assert (nodes_aux.size() <= nodes.size()); + int current_depth = 0; + for (int i = 0; i < getNofPos(); i++) { + int po_id = getNofObjs() - getNofPos() + i; + moveNode(nodes_aux, nodes[po_id]); + current_depth = std::max(current_depth, nodes_aux.back()->depth); + } + depth = current_depth; + return nodes_aux; + } + + int eSLIMCirMan::processRedundant(const Subcircuit& subcir) { + int nredundant = 0; + std::vector to_process (subcir.inputs.begin(), subcir.inputs.end()); + std::unordered_set seen (subcir.inputs.begin(), subcir.inputs.end()); + while (to_process.size() > 0) { + int nd = to_process.back(); + to_process.pop_back(); + if (nodes[nd] != nullptr && !inSubcircuit(*nodes[nd])) { + // This is a redundant node that can be discarded + nredundant++; + auto ptr = nodes[nd].get(); + if (taboo != nullptr) { + taboo->removeRedundantNode(ptr); + } + for (auto& f : nodes[nd]->fanins) { + f->fanouts.erase(nodes[nd].get()); + // in the dfs traversal the node was not copied + if (!isCurrentTraversalId(*f)) { + auto [it, inserted] = seen.insert(f->node_id); + if (inserted) { // the node was not seen before + to_process.push_back(f->node_id); + } + } + } + } + } + return nredundant; + } + + void eSLIMCirMan::applyLevelBasedOrdering() { + std::sort(nodes.begin() + getNofPis() + 1, nodes.begin() + getNofObjs() - getNofPos(), + [](const std::unique_ptr& a, const std::unique_ptr& b) { + return a->depth < b->depth || (a->depth == b->depth && a->id < b->id); + } + ); + } + + // Reorders the nodes to an alternative topological order. + void eSLIMCirMan::applyDFSOrdering(const std::vector& po_nodes) { + incrementTraversalId(); + std::vector> nodes_reordered; + nodes_reordered.reserve(nodes.size()); + for (int i = 0; i <= nof_pis; i++) { + setTraversalId(*nodes[i]); + nodes_reordered.push_back(std::move(nodes[i])); + } + for (int i = 0; i < nof_pos; i++) { + int po_id = po_nodes[i]; + applyDFSOrderingRec(nodes[po_id]->fanins[0], nodes_reordered); + } + for (int i = 0; i < nof_pos; i++) { + int po_id = getNofObjs() - getNofPos() + i; + nodes_reordered.push_back(std::move(nodes[po_id])); + } + std::swap(nodes, nodes_reordered); + } + + void eSLIMCirMan::applyDFSOrderingRec(eSLIMCirObj* obj, std::vector>& sorted) { + if (!isCurrentTraversalId(*obj)) { + setTraversalId(*obj); + for (auto& fan : obj->fanins) { + applyDFSOrderingRec(fan, sorted); + } + auto& pobj = nodes[obj->node_id]; + pobj->node_id = sorted.size(); + sorted.push_back(std::move(pobj)); + } + } + + void eSLIMCirMan::print() const { + std::cout << "Inputs:"; + for (int i = 1; i <= nof_pis; i++) { + std::cout << " " << i; + } + std::cout << "\n"; + for (int i = nof_pis + 1; i < getNofObjs() - getNofPos(); i++) { + std::cout << nodes[i]->node_id << " :"; + for (auto& f : nodes[i]->fanins) { + std::cout << " " << f->node_id; + } + std::cout << " - " << nodes[i]->tt << "\n"; + } + std::cout << "Outputs:"; + for (int i = getNofObjs() - getNofPos(); i < getNofObjs(); i++) { + std::cout << " " << nodes[i]->fanins[0]->node_id; + } + std::cout << "\n"; + } + + void eSLIMCirMan::print(const Subcircuit& scir) const { + std::cout << "Inputs:"; + for (int i : scir.inputs) { + std::cout << " " << i; + } + std::cout << "\n"; + for (int nd : scir.nodes) { + std::cout << nodes[nd]->node_id << " :"; + for (auto& f : nodes[nd]->fanins) { + std::cout << " " << f->node_id; + } + std::cout << " - " << nodes[nd]->tt << "\n"; + } + std::cout << "Outputs:"; + for (int o : scir.outputs) { + std::cout << " " << o; + } + std::cout << "\nForbidden pairs:"; + for (const auto & [out,inps] : scir.forbidden_pairs) { + std::cout << "\n" << out << " :"; + for (int inp : inps) { + std::cout << " " << inp; + } + } + std::cout << "\n"; + } + + eSLIMCirMan eSLIMCirMan::duplicate() const { + eSLIMCirMan dup(getNofObjs()); + for (int i = 0; i < getNofPis(); i++) { + dup.addPi(); + } + for (int i = getNofPis() + 1; i < getNofObjs() - getNofPos(); i++) { + std::vector fanins; + for (const auto& f : nodes[i]->fanins) { + fanins.push_back(f->node_id); + } + dup.addNode(fanins, nodes[i]->tt); + } + for (int i = getNofObjs() - getNofPos(); i < getNofObjs(); i++) { + const auto& obj = nodes[i]->fanins[0]; + dup.addPo(obj->node_id, obj->tt == 1); + } + return dup; + } + + void eSLIMCirMan::simplifyDuplicateFanins() { + std::unordered_map fanins; + for (int i = getNofPis() + 1; i < getNofObjs() - getNofPos(); i++) { + fanins.clear(); + for (auto it = nodes[i]->fanins.begin(); it != nodes[i]->fanins.end(); ) { + int fanin_id = std::distance(nodes[i]->fanins.begin(), it); + if (!fanins[(*it)->node_id]) { + fanins[(*it)->node_id] = fanin_id + 1; // 0 should indicate that this node_id was not yet considered + it++; + } else { + nodes[i]->tt = removeInputFromTT(nodes[i]->tt, fanins[(*it)->node_id] - 1, fanin_id); + // In general, we do not expect many duplicate fanins. + // Hence, we immediatley delete from the vector. + it = nodes[i]->fanins.erase(it); + } + } + } + } + + ABC_UINT64_T eSLIMCirMan::compressTT(ABC_UINT64_T tt, unsigned int input) { + static constexpr ABC_UINT64_T filters1[] {0x1111111111111111ull, 0x303030303030303ull, 0xF000F000F000Full, 0xFF000000FFull, 0xFFFFull}; + static constexpr ABC_UINT64_T filters2[] {0x4444444444444444ull, 0x3030303030303030ull, 0xF000F000F000F00ull, 0xFF000000FF0000ull, 0xFFFF00000000ull}; + static constexpr int shifts[] {1,2,4,8,16}; + if (input == 5) { + return tt & 0xFFFFFFFFull; + } + for (int i = input; i < 5; i++) { + tt = (tt & filters1[i]) | ((tt & filters2[i]) >> shifts[i]); + } + return tt; + } + + // we drop the lines from the tt where the values for inputs first_occurrence and second_occurrence do not coincide. Then we compress the tt. + ABC_UINT64_T eSLIMCirMan::removeInputFromTT(ABC_UINT64_T tt, unsigned int first_occurrence, unsigned int second_occurrence) { + assert (first_occurrence < 6 && second_occurrence && first_occurrence < second_occurrence); // we can only represent 6-input functions with the 64-bit ints + + // filter that gives all the tt-lines with the ith input is true + static constexpr ABC_UINT64_T isTruePatterns[] {0xAAAAAAAAAAAAAAAAull, 0xCCCCCCCCCCCCCCCCull, 0xF0F0F0F0F0F0F0F0ull, 0xFF00FF00FF00FF00ull, 0xFFFF0000FFFF0000ull, 0xFFFFFFFF00000000ull}; + // filter that gives all the tt-lines with the ith input is false + static constexpr ABC_UINT64_T isFalsePatterns[] {0x5555555555555555ull, 0x3333333333333333ull, 0xF0F0F0F0F0F0F0Full, 0xFF00FF00FF00FFull, 0xFFFF0000FFFFull, 0xFFFFFFFFull}; + // the first tt-line where the ith input is true + static constexpr unsigned char firstTrueIn1[] {1, 2, 4, 8, 16, 32}; + + ABC_UINT64_T true_filter = isTruePatterns[first_occurrence] & isTruePatterns[second_occurrence]; + ABC_UINT64_T false_filter = isFalsePatterns[first_occurrence] & isFalsePatterns[second_occurrence]; + // the first line of the tt where both considered inputs are true + unsigned int shift = lsb(true_filter) - firstTrueIn1[first_occurrence]; + + ABC_UINT64_T true_positions = tt & true_filter; + ABC_UINT64_T false_positions = tt & false_filter; + ABC_UINT64_T tt_new = false_positions | (true_positions >> shift); + + return compressTT(tt_new, second_occurrence); + } + + + void eSLIMCirMan::registerTabooList(TabooList* taboo) { + this->taboo = taboo; + } + + void eSLIMCirMan::unregisterTabooList() { + this->taboo = nullptr; + } + + +} +ABC_NAMESPACE_IMPL_END \ No newline at end of file diff --git a/src/opt/eslim/eslimCirMan.hpp b/src/opt/eslim/eslimCirMan.hpp new file mode 100644 index 0000000000..49a8adce84 --- /dev/null +++ b/src/opt/eslim/eslimCirMan.hpp @@ -0,0 +1,234 @@ +/**CFile**************************************************************** + + FileName [eslimCirMan.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Internal circuit representation.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__ESLIMCIRMAN_hpp +#define ABC__OPT__ESLIM__ESLIMCIRMAN_hpp + +#include +#include +#include +#include + +#include "aig/gia/gia.h" +#include "base/abc/abc.h" +#include "misc/util/abc_namespaces.h" +#include "misc/util/abc_global.h" + + + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class Subcircuit; + class eSLIMCirMan; + class TabooList; + template class WindowMan; + + // Nodes must not have more than 6 fanins + class eSLIMCirObj { + + private: + + // This comparator is used in sets of pointers to nodes. + // As the order does not really matter, the comparator could be replaced by comparing addresses. + // This would slightly simplify the code as we would not need to change the fanouts of replacement circuits + // (unlike to the ids the addresses of the nodes in the replacement do not change during the replacement). + // Nevertheless, we use this iterator to make iteration orders more predictable. + struct eSLIMObjComp { + bool operator()(eSLIMCirObj* a, eSLIMCirObj* b) const { + return a->getId() < b->getId(); + } + }; + + public: + + eSLIMCirObj(int id, std::vector&& fanins); + int getNFanins() const; + int getTTLength() const; + unsigned int getId() const; + + int node_id; + std::vector fanins; + std::set fanouts; + ABC_UINT64_T tt; //truth table representing the function + int depth = 0; // the step from a input/gate node to an output node is also counted -> depth differs from depths in abc + int remaining_time = 0; + + int value1 = 0; + + private: + unsigned int id = 0; + // set traversal_id + unsigned int trav_id = 0; + unsigned int inSubcircuit = 0; + unsigned int inTFI = 0; + unsigned int inTFO = 0; + unsigned int isTaboo = 0; + + friend eSLIMCirMan; + friend TabooList; + }; + + class eSLIMCirMan { + + public: + + eSLIMCirMan(int nObjEstimate); + eSLIMCirMan(Gia_Man_t * pGia); + eSLIMCirMan(Abc_Ntk_t * pNtk, bool simplify); + eSLIMCirMan(eSLIMCirMan& es_man, const Subcircuit& scir); + + // normalises the aig + // static eSLIMCirMan eSLIMCirManFromGia(Gia_Man_t * pGia); + static ABC_UINT64_T negateTT(ABC_UINT64_T tt, unsigned int nfanin); + static ABC_UINT64_T ttNegateFanin(ABC_UINT64_T tt, unsigned int fan); + static bool ttisnormal(ABC_UINT64_T tt); + + void registerTabooList(TabooList* taboo); + void unregisterTabooList(); + + void replace(eSLIMCirMan& replacement, const Subcircuit& subcir); + + + Gia_Man_t* eSLIMCirManToGia(); + Abc_Ntk_t* eSLIMCirManToNtk(); + Abc_Ntk_t* eSLIMCirManToMappedNtk(); + void setupTimings(); + + bool isConst(int id) const; + bool isPi(int id) const; // or constant node + bool isPo(int id) const; + bool isGate(int id) const; + + bool isConst(const eSLIMCirObj&) const; + bool isPi(const eSLIMCirObj&) const; + + bool isOnCricticalPath(const eSLIMCirObj&) const; + + int getNofObjs() const; + int getNofPis() const; + int getNofPos() const; + int getNofGates() const; + + int getDepth() const; + + const eSLIMCirObj& getObj(int id) const; + + void incrementTraversalId() {traversal_id++;} + void setTraversalId(eSLIMCirObj& obj); + void setTraversalId(int n); + bool isCurrentTraversalId(const eSLIMCirObj& obj) const; + bool isCurrentTraversalId(int n) const; + + void setInSubcircuit(eSLIMCirObj& obj); + void setInTFI(eSLIMCirObj& obj); + void setInTFO(eSLIMCirObj& obj); + bool inSubcircuit(const eSLIMCirObj& obj) const; + bool inTFI(const eSLIMCirObj& obj) const; + bool inTFO(const eSLIMCirObj& obj) const; + + void markSubcircuit(const std::vector& nodes); + void markCones(const std::vector& nodes); + + int addPi(); + int addPo(int fanin, bool negated); + int addNode(const std::vector& fanins, ABC_UINT64_T tt); + + void print() const; + void print(const Subcircuit& scir) const; + eSLIMCirMan duplicate() const; + + private: + + bool isTaboo(const eSLIMCirObj& obj, int taboo_time) const; + void setTaboo(eSLIMCirObj& obj); + + eSLIMCirObj* getpObj(int id); + const eSLIMCirObj* getpObj(int id) const; + + eSLIMCirObj* getPo(int id); + + static ABC_UINT64_T ttFromGiaObj(Gia_Man_t * pGia, Gia_Obj_t * pObj); + static ABC_UINT64_T ttFromNtkObj(Abc_Ntk_t * pNtk, Abc_Obj_t * pObj); + + std::vector> replaceInternal(eSLIMCirMan& replacement, const Subcircuit& subcir); + void setupMarksForReplacement(eSLIMCirMan& replacement); + void clearFanouts(const Subcircuit& subcir); + + void insertSorted(eSLIMCirObj* obj, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec); + void insertSortedRec( eSLIMCirObj* obj, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec); + + void insertSortedRecRep(eSLIMCirObj* obj, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec); + void processRepFanin( eSLIMCirObj* obj, eSLIMCirObj* fan, int fid, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec); + void processRepPo( eSLIMCirObj* obj, eSLIMCirObj* fan, int fid, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec); + void processNonGateRepFanin(eSLIMCirObj* obj, eSLIMCirObj* fan, int fid, std::vector>& sorted, + eSLIMCirMan& replacement, const std::unordered_map& out_map, const std::vector& in_vec); + void addFans(eSLIMCirObj* out, eSLIMCirObj* in, int fin_id); + + + int addGiaGate(Gia_Man_t * pGia, int node_id, std::vector& node_ids, std::vector& is_node_negated); + static ABC_UINT64_T sop2tt(char * pSop); + static char* tt2sop(ABC_UINT64_T tt, int nVars, Mem_Flex_t * pMan); + + int insertNtkNodes(Abc_Ntk_t * pNtk, int node_id, std::unordered_map& ids, bool simplify); + + int processRedundant(const Subcircuit& subcir); + void moveNode(std::vector>& vec, std::unique_ptr& pobj); + void setMarks(eSLIMCirObj& pObj, int id); + + // The nodes vector contains nodes in topological order. + // This does not necessarily ensure that if a < b we have lv(a) < lv(b) (lv: the level/depth of the node) + // If nodes are sorted according to their levels i.e. a <= b iff lv(a) <= lv(b) then the nodes are also topologically sorted. + void applyLevelBasedOrdering(); + void applyDFSOrdering(const std::vector& po_nodes); // po_nodes must contain the node_id of each primary output + void applyDFSOrderingRec(eSLIMCirObj* obj, std::vector>& sorted); + + // remove repeated fanins + // The Gia representation does not allow repeated fanins. + void simplifyDuplicateFanins(); + ABC_UINT64_T removeInputFromTT(ABC_UINT64_T tt, unsigned int first_occurrence, unsigned int second_occurrence); + ABC_UINT64_T compressTT(ABC_UINT64_T tt, unsigned int input); + + unsigned int nof_pis = 0; + unsigned int nof_pos = 0; + std::vector> nodes; + unsigned int last_node_id = 0; + + static constexpr int const_false_id = 0; + unsigned int traversal_id = 0; + + int depth = 0; + + TabooList* taboo = nullptr; + + friend TabooList; + template friend class WindowMan; + + }; + +} +ABC_NAMESPACE_CXX_HEADER_END + +#endif \ No newline at end of file diff --git a/src/opt/eslim/module.make b/src/opt/eslim/module.make index f300a42315..e9106a941b 100644 --- a/src/opt/eslim/module.make +++ b/src/opt/eslim/module.make @@ -1,2 +1,7 @@ -SRC += src/opt/eslim/relationGeneration.cpp \ - src/opt/eslim/eSLIM.cpp \ No newline at end of file +SRC += src/opt/eslim/eSLIM.cpp \ + src/opt/eslim/eslimCirMan.cpp \ + src/opt/eslim/relationGeneration.cpp \ + src/opt/eslim/subcircuit.cpp \ + src/opt/eslim/relationSynthesiser.cpp \ + src/opt/eslim/areaEngine.cpp \ + src/opt/eslim/delayEngine.cpp \ diff --git a/src/opt/eslim/relationGeneration.cpp b/src/opt/eslim/relationGeneration.cpp index 66dc11effb..89fc4fd4c9 100644 --- a/src/opt/eslim/relationGeneration.cpp +++ b/src/opt/eslim/relationGeneration.cpp @@ -6,167 +6,419 @@ PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - Synopsis [Procedures for computing Boolean relations.] + Synopsis [SAT-based computation of Boolean relations.] Author [Franz-Xaver Reichl] Affiliation [University of Freiburg] - Date [Ver. 1.0. Started - March 2025.] + Date [Ver. 1.0. Started - April 2026.] - Revision [$Id: relationGeneration.cpp,v 1.00 2025/03/17 00:00:00 Exp $] + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] ***********************************************************************/ #include +#include -#include "misc/vec/vec.h" -#include "sat/cnf/cnf.h" -#include "sat/bsat/satSolver.h" +#include "misc/util/abc_global.h" #include "relationGeneration.hpp" -ABC_NAMESPACE_HEADER_START - void * Mf_ManGenerateCnf( Gia_Man_t * pGia, int nLutSize, int fCnfObjIds, int fAddOrCla, int fMapping, int fVerbose ); - void * Cnf_DataWriteIntoSolver( Cnf_Dat_t * p, int nFrames, int fInit ); - Vec_Int_t * Gia_ManCollectNodeTfos( Gia_Man_t * p, int * pNodes, int nNodes ); - Vec_Int_t * Gia_ManCollectNodeTfis( Gia_Man_t * p, Vec_Int_t * vNodes ); -ABC_NAMESPACE_HEADER_END ABC_NAMESPACE_IMPL_START +namespace eSLIM { -namespace eSLIM { + Relation::Relation(int nfanins) : output_patterns(1 << nfanins) { - RelationGeneratorABC::RelationGeneratorABC(Gia_Man_t* pGia, const Subcircuit& subcir) - : RelationGenerator(pGia, subcir) { + } - // for (const auto& [out, inputs] : subcir.forbidden_pairs) { - for (const auto& it: subcir.forbidden_pairs) { - const auto& inputs = it.second; - inputs_in_forbidden_pairs.insert(inputs.begin(), inputs.end()); + void Relation::addOutputPattern(int inputidx, std::vector&& pattern) { + lines_with_pattern += output_patterns[inputidx].empty(); + output_patterns[inputidx].push_back(pattern); + } + + RelationGenerator::RelationGenerator( const eSLIMCirMan& cir, const Subcircuit& subcir, + const eSLIMConfig& cfg, eSLIMLog& log) + : cir(cir), subcir(subcir), cfg(cfg), log(log), + gate2varref(cir.getNofObjs() - cir.getNofPos()), + gate2varcone(cir.getNofObjs() - cir.getNofPos()) { + setupEncoding(); + } + + Relation RelationGenerator::computeRelation(const eSLIMCirMan& cir, const Subcircuit& subcir, + const eSLIMConfig& cfg, eSLIMLog& log) { + RelationGenerator rgen(cir, subcir, cfg, log); + return rgen.getRelation(); + } + + int RelationGenerator::getNewVar() { + return max_var++; } -} - Vec_Int_t* RelationGeneratorABC::getRelationImpl() { - abctime clkStart = Abc_Clock(); - int nTimeOut = 600, nConfLimit = 1000000; - int i, iNode, iSatVar, Iter, Mask, nSolutions = 0, RetValue = 0; - // --------------- Modification --------------- - Gia_Man_t* pMiter = generateMiter(); - // --------------- Modification --------------- - Cnf_Dat_t * pCnf = (Cnf_Dat_t*)Mf_ManGenerateCnf( pMiter, 8, 0, 0, 0, 0 ); - sat_solver * pSat = (sat_solver*)Cnf_DataWriteIntoSolver( pCnf, 1, 0 ); - int iLit = Abc_Var2Lit( 1, 0 ); // enumerating the care set (the miter output is 1) - int status = sat_solver_addclause( pSat, &iLit, &iLit + 1 ); assert( status ); - Vec_Int_t * vSatVars = Vec_IntAlloc( Vec_IntSize(subcir.io) ); - Vec_IntForEachEntry( subcir.io, iNode, i ) - Vec_IntPush( vSatVars, i < subcir.nof_inputs ? 2+i : pCnf->nVars-Vec_IntSize(subcir.io)+i ); - Vec_Int_t * vLits = Vec_IntAlloc( 100 ); - Vec_Int_t * vRes = Vec_IntAlloc( 1000 ); - for ( Iter = 0; Iter < 1000000; Iter++ ) - { - int status = sat_solver_solve( pSat, NULL, NULL, (ABC_INT64_T)nConfLimit, 0, 0, 0 ); - if ( status == l_False ) { RetValue = 1; break; } - if ( status == l_Undef ) { RetValue = 0; break; } - nSolutions++; - // extract SAT assignment - Mask = 0; - Vec_IntClear( vLits ); - Vec_IntForEachEntry( vSatVars, iSatVar, i ) { - Vec_IntPush( vLits, Abc_Var2Lit(iSatVar, sat_solver_var_value(pSat, iSatVar)) ); - if ( sat_solver_var_value(pSat, iSatVar) ) - Mask |= 1 << (Vec_IntSize(subcir.io)-1-i); + void RelationGenerator::setupEncoding() { + if (cfg.approximate_relation) { + if (cfg.generate_relation_with_tfi_limit) { + encodeCircuitApproximativeBoundedTFI(); + } else { + encodeCircuitApproximative(); + } + } else { + // encodeCircuitFull(); + encodeCircuitAffected(); + } + } + + void RelationGenerator::encodeGate(const eSLIMCirObj& obj, int var, const std::vector& gate2var) { + int bc = popcount(obj.tt); + int tt_length = obj.getTTLength(); + if (bc == 0) { + solver.addClause({-var}); + } else if (bc == tt_length) { + solver.addClause({var}); + } else if (bc == 1) { + return getGateEncodingUnique(obj, var, gate2var, true); + } else if (bc == tt_length - 1) { + return getGateEncodingUnique(obj, var, gate2var, false); + } else { + return getGateEncodingNaive(obj, var, gate2var); + } + } + + void RelationGenerator::getGateEncodingUnique(const eSLIMCirObj& obj, int var, const std::vector& gate2var, bool unique_bit) { + ABC_UINT64_T tt = obj.tt; + if (!unique_bit) { + tt = eSLIMCirMan::negateTT(tt, obj.getNFanins()); + } + int bit_index = lsb(tt); + std::vector clause; + int lit = unique_bit ? -var : var; + for (int i = 0; i < obj.getNFanins(); i++) { + if ((bit_index >> i) & 1 ) { + clause.push_back(-gate2var[obj.fanins[i]->node_id]); + solver.addClause({gate2var[obj.fanins[i]->node_id], lit}); + } else { + clause.push_back(gate2var[obj.fanins[i]->node_id]); + solver.addClause({-gate2var[obj.fanins[i]->node_id], lit}); + } + } + clause.push_back(-lit); + solver.addClause(clause); + } + + void RelationGenerator::getGateEncodingNaive(const eSLIMCirObj& obj, int var, const std::vector& gate2var) { + int tt_length = obj.getTTLength(); + int nfans = obj.getNFanins(); + std::vector clause; + clause.reserve(nfans+1); + for (int i = 0; i < tt_length; i++) { + clause.clear(); + for (int j = 0; j < nfans; j++) { + if ((i >> j) & 1 ) { + clause.push_back(-gate2var[obj.fanins[j]->node_id]); + } else { + clause.push_back(gate2var[obj.fanins[j]->node_id]); } - Vec_IntPush( vRes, Mask ); - if ( !sat_solver_addclause( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits) ) ) - { RetValue = 1; break; } - if ( nTimeOut && (Abc_Clock() - clkStart)/CLOCKS_PER_SEC >= nTimeOut ) { RetValue = 0; break; } - } - // complement the set of input/output minterms - Vec_Int_t * vBits = Vec_IntStart( 1 << Vec_IntSize(subcir.io) ); - Vec_IntForEachEntry( vRes, Mask, i ) - Vec_IntWriteEntry( vBits, Mask, 1 ); - Vec_IntClear( vRes ); - Vec_IntForEachEntry( vBits, Mask, i ) - if ( !Mask ) - Vec_IntPush( vRes, i ); - Vec_IntFree( vBits ); - // cleanup - Vec_IntFree( vLits ); - Vec_IntFree( vSatVars ); - sat_solver_delete( pSat ); - Gia_ManStop( pMiter ); - Cnf_DataFree( pCnf ); - if ( RetValue == 0 ) - Vec_IntFreeP( &vRes ); - return vRes; - } - - Gia_Man_t * RelationGeneratorABC::generateMiter() { - Vec_Int_t * vInsOuts = subcir.io; - int nIns = subcir.nof_inputs; - - Vec_Int_t * vTfo = Gia_ManCollectNodeTfos( pGia, Vec_IntEntryP(vInsOuts, nIns), Vec_IntSize(vInsOuts)-nIns ); - Vec_Int_t * vTfi = Gia_ManCollectNodeTfis( pGia, vTfo ); - Vec_Int_t * vInLits = Vec_IntAlloc( nIns ); - Vec_Int_t * vOutLits = Vec_IntAlloc( Vec_IntSize(vInsOuts) - nIns ); - Gia_Man_t * pNew, * pTemp; Gia_Obj_t * pObj; int i, iLit = 0; - Gia_ManFillValue( pGia ); - pNew = Gia_ManStart( 1000 ); - pNew->pName = Abc_UtilStrsav( pGia->pName ); - Gia_ManHashAlloc( pNew ); - Gia_ManForEachObjVec( vTfi, pGia, pObj, i ) - if ( Gia_ObjIsCi(pObj) ) - pObj->Value = Gia_ManAppendCi(pNew); - for ( i = 0; i < Vec_IntSize(vInsOuts)-nIns; i++ ) - Vec_IntPush( vInLits, Gia_ManAppendCi(pNew) ); - Gia_ManForEachObjVec( vTfi, pGia, pObj, i ) - if ( Gia_ObjIsAnd(pObj) ) - pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); - Gia_ManForEachObjVec( vTfo, pGia, pObj, i ) - if ( Gia_ObjIsCo(pObj) ) - pObj->Value = Gia_ObjFanin0Copy(pObj); - Gia_ManForEachObjVec( vInsOuts, pGia, pObj, i ) - if ( i < nIns ) - Vec_IntPush( vOutLits, pObj->Value ); - else - pObj->Value = Vec_IntEntry( vInLits, i-nIns ); + } + bool polarity = (obj.tt >> i) & 1; + if (polarity) { + clause.push_back(var); + } else { + clause.push_back(-var); + } + solver.addClause(clause); + } + } + + + void RelationGenerator::encodeCircuitAffected() { + setupConstantGate(); + std::vector compare_nodes; + for (int i = cir.getNofObjs() - cir.getNofPos(); i < cir.getNofObjs(); i++) { + const auto obj = cir.getObj(i); + int fid = obj.fanins[0]->node_id; + // multiple POs can use the same fanin + // if gate2varref[fid] is not 0 we have already seen it + if (cir.inTFO(obj) && !gate2varref[fid]) { + gate2varref[fid] = getNewVar(); + gate2varcone[fid] = getNewVar(); + compare_nodes.push_back(fid); + } + } + setupEqualityConstraints(compare_nodes); + + for (int i = cir.getNofObjs() - cir.getNofPos() - 1; i > cir.getNofPis(); i--) { + if (gate2varref[i]) { // the gate is connected to a PO that is in the TFO of the subcircuit + const auto obj = cir.getObj(i); + for (const auto& f : obj.fanins) { + int fid = f->node_id; + if (gate2varref[fid] == 0) { + gate2varref[fid] = getNewVar(); + } + } + encodeGate(obj, gate2varref[i], gate2varref); + + if (cir.inTFO(obj) && !cir.inSubcircuit(obj)) { + // For an input in a forbidden pair no successors needs to be processed before + gate2varcone[i] = gate2varcone[i] ? gate2varcone[i] : getNewVar(); + for (const auto& f : obj.fanins) { + int fid = f->node_id; + if (cir.inSubcircuit(*f) || cir.inTFO(*f)) { + if (gate2varcone[fid] == 0) { + gate2varcone[fid] = getNewVar(); + } + } else { + gate2varcone[fid] = gate2varref[fid]; + } + } + encodeGate(obj, gate2varcone[i], gate2varcone); + } else if (gate2varcone[i] == 0) { + gate2varcone[i] = gate2varref[i]; + } + } + } - Gia_ManIncrementTravId(pGia); - Gia_ManForEachObjVec( subcir.nodes, pGia, pObj, i ) { - Gia_ObjSetTravIdCurrent(pGia, pObj); - } - - Gia_ManForEachObjVec( vTfo, pGia, pObj, i ) - // in case there are forbidden pairs we have to ensure that nodes from the sucircuit are not added - if ( Gia_ObjIsAnd(pObj) && !Gia_ObjIsTravIdCurrent(pGia, pObj) ) - pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); - Gia_ManForEachObjVec( vTfo, pGia, pObj, i ) - if ( Gia_ObjIsCo(pObj) ) - iLit = Gia_ManHashOr( pNew, iLit, Gia_ManHashXor(pNew, Gia_ObjFanin0Copy(pObj), pObj->Value) ); - // --------------- Modification --------------- - // In case there are forbidden pairs, the outputs of the subcircuit influence the inputs of the subcircuit. - // Therefore, we need the values of the inputs in the version of the circuit that is affected by the new outputs. - for (int inp : inputs_in_forbidden_pairs) { - int node_id = Vec_IntEntry(vInsOuts, inp); - pObj = Gia_ManObj(pGia, node_id); - Vec_IntWriteEntry(vOutLits, inp, pObj->Value); - } - // --------------- Modification --------------- - Gia_ManAppendCo( pNew, iLit ); - Vec_IntForEachEntry( vOutLits, iLit, i ) - Gia_ManAppendCo( pNew, iLit ); - Vec_IntFree( vTfo ); - Vec_IntFree( vTfi ); - Vec_IntFree( vInLits ); - Vec_IntFree( vOutLits ); - pNew = Gia_ManCleanup( pTemp = pNew ); - Gia_ManStop( pTemp ); - Gia_ManSetRegNum( pNew, Gia_ManRegNum(pGia) ); - return pNew; - } - -} + for (int i = 1; i <= cir.getNofPis(); i++) { + if (gate2varref[i] != 0) { + cone_input_variables.push_back(gate2varref[i]); + // Simplifies handling of variables + if (gate2varcone[i] == 0) { + gate2varcone[i] = gate2varref[i]; + } + } + } + } + + + void RelationGenerator::encodeCircuitApproximative() { + setupConstantGate(); + std::vector compare_nodes = markRestrictedCone(); + int max_node = *std::max_element(compare_nodes.begin(), compare_nodes.end()); + for (int i = max_node; i > cir.getNofPis(); i--) { + const auto obj = cir.getObj(i); + if (gate2varref[i] != 0) { + for (auto& f : obj.fanins ) { + if (gate2varref[f->node_id] == 0) { + gate2varref[f->node_id] = getNewVar(); + gate2varcone[f->node_id] = gate2varref[f->node_id]; + } + } + encodeGate(obj, gate2varref[i], gate2varref); + if (!cir.inSubcircuit(obj) && gate2varcone[i] != gate2varref[i]) { + encodeGate(obj, gate2varcone[i], gate2varcone); + } + } + } + for (int i = 1; i <= cir.getNofPis(); i++) { + if (gate2varref[i] != 0) { + cone_input_variables.push_back(gate2varref[i]); + } + } + setupEqualityConstraints(compare_nodes); + } + void RelationGenerator::encodeCircuitApproximativeBoundedTFI() { + setupConstantGate(); + std::vector compare_nodes = markRestrictedCone(); + std::vector counts(cir.getNofObjs() - cir.getNofPos()); + int max_node = *std::max_element(compare_nodes.begin(), compare_nodes.end()); + for (int i = max_node; i > cir.getNofPis(); i--) { + const auto obj = cir.getObj(i); + if (gate2varref[i] != 0) { + if (cir.inTFO(obj) || cir.inSubcircuit(obj)) { + counts[i] = cfg.relation_tfi_bound + 1; + } else if (counts[i] == 0) { + cone_input_variables.push_back(gate2varref[i]); + continue; + } + for (auto& f : obj.fanins ) { + if (gate2varref[f->node_id] == 0) { + gate2varref[f->node_id] = getNewVar(); + gate2varcone[f->node_id] = gate2varref[f->node_id]; + counts[f->node_id] = std::max(counts[f->node_id], static_cast(counts[i] - 1)); + } + } + encodeGate(obj, gate2varref[i], gate2varref); + if (!cir.inSubcircuit(obj) && gate2varcone[i] != gate2varref[i]) { + encodeGate(obj, gate2varcone[i], gate2varcone); + } + } + } + for (int i = 1; i <= cir.getNofPis(); i++) { + if (gate2varref[i] != 0) { + cone_input_variables.push_back(gate2varref[i]); + } + } + setupEqualityConstraints(compare_nodes); + } + + std::vector RelationGenerator::markRestrictedCone() { + std::vector counts(cir.getNofObjs() - cir.getNofPos()); + for (int i : subcir.outputs) { + gate2varref[i] = getNewVar(); + gate2varcone[i] = getNewVar(); + counts[i] = cfg.relation_tfo_bound; + } + if (cfg.relation_tfo_bound == 0) { + std::vector nodes(subcir.outputs.begin(), subcir.outputs.end()); + return nodes; + } + std::vector nodes; + for (int i = subcir.outputs[0] + 1; i < cir.getNofObjs() - cir.getNofPos(); i++) { //outputs are ordered + const auto obj = cir.getObj(i); + if (!cir.inTFO(obj) || cir.inSubcircuit(obj)) { + continue; + } + unsigned char x = 0; + for (auto& f : obj.fanins) { + x = std::max(x, counts[f->node_id]); + } + if (x > 0) { + gate2varcone[i] = getNewVar(); + gate2varref[i] = getNewVar(); + counts[i] = x - 1; + if (x == 1) { + nodes.push_back(i); + } + } + } + std::set used; + for (int i = cir.getNofObjs() - cir.getNofPos(); i < cir.getNofObjs(); i++) { + const auto obj = cir.getObj(i); + if (cir.inTFO(obj) && counts[obj.fanins[0]->node_id] > 0 && used.find(obj.fanins[0]->node_id) == used.end()) { + nodes.push_back(obj.fanins[0]->node_id); + used.insert(obj.fanins[0]->node_id); + } + } + return nodes; + } + + void RelationGenerator::setupConstantGate() { + int const_var = getNewVar(); + gate2varref[0] = const_var; + gate2varcone[0] = const_var; + solver.addClause({-const_var}); + } + + void RelationGenerator::setupEqualityConstraints(const std::vector& nodes) { + mode_selection_variable = getNewVar(); + std::vector one_different {-mode_selection_variable}; + for (int cnd : nodes) { + int eq_var = getNewVar(); + equality_variables.push_back(eq_var); + getEqualityClauses(gate2varref[cnd], gate2varcone[cnd], eq_var); + one_different.push_back(-eq_var); + } + solver.addClause(one_different); + } + + + void RelationGenerator::getEqualityClauses(int x, int y, int aux) { + solver.addClause({x, -y, -aux}); + solver.addClause({-x, y, -aux}); + solver.addClause({x, y, aux}); + solver.addClause({-x, -y, aux}); + } + + int RelationGenerator::findConflict(double& timeout, int mode_selection) { + abctime time_start = Abc_Clock(); + int solver_status = solver.solve(timeout, {mode_selection}); + double solver_time = (double)1.0*(Abc_Clock() - time_start)/CLOCKS_PER_SEC; + log.cummulative_sat_runtime_relation_generation += solver_time; + log.max_sat_runtime_relation_generation = std::max(log.max_sat_runtime_relation_generation, solver_time); + // log.cummulative_sat_runtime_relation_generation += solver.getRunTime(); + log.nof_sat_calls_relation_generation++; + return solver_status; + } + + int RelationGenerator::reduceConflict(double& timeout, const std::vector& cone_input, + const std::vector& subcircuit_output, const std::vector& equality) { + solver.assume(cone_input); + solver.assume(subcircuit_output); + solver.assume(equality); + abctime time_start = Abc_Clock(); + int status = solver.solve(timeout); + double solver_time = (double)1.0*(Abc_Clock() - time_start)/CLOCKS_PER_SEC; + log.cummulative_sat_runtime_relation_generation += solver_time; + log.max_sat_runtime_relation_generation = std::max(log.max_sat_runtime_relation_generation, solver_time); + // log.cummulative_sat_runtime_relation_generation += solver.getRunTime(); + log.nof_sat_calls_relation_generation++; + return status; + } + + Relation RelationGenerator::getRelation() { + std::vector subcircuit_input_variables; + subcircuit_input_variables.reserve(subcir.inputs.size()); + for (int sin : subcir.inputs) { + int var = gate2varcone[sin]; + subcircuit_input_variables.push_back(var); + } + std::vector subcircuit_output_variables; + subcircuit_output_variables.reserve(subcir.outputs.size()); + // outputs in forbidden pairs must not removed + std::vector outputs_to_keep(subcir.outputs.size(), false); + for (int i = 0; i < subcir.outputs.size(); i++) { + int sout = subcir.outputs[i]; + int var = gate2varcone[sout]; + subcircuit_output_variables.push_back(var); + if (subcir.forbidden_pairs.find(sout) != subcir.forbidden_pairs.end()) { + outputs_to_keep[i] = true; + } + } + + Relation rel(subcir.inputs.size()); + + int solver_status; + double timeout = cfg.relation_generation_timeout; + abctime time_start = Abc_Clock(); + while( (solver_status=findConflict(timeout, mode_selection_variable)) == 10) { + timeout -= (double)1.0*(Abc_Clock() - time_start)/CLOCKS_PER_SEC; + time_start = Abc_Clock(); + if (timeout <= 0) { + rel.status = false; + return rel; + } + std::vector cone_in_assm = solver.getValues(cone_input_variables); + std::vector sin_assm = solver.getValues(subcircuit_input_variables); + std::vector sout_assm = solver.getValues(subcircuit_output_variables); + int status = reduceConflict(timeout, cone_in_assm, sout_assm, equality_variables); + if (status != 20) { + assert (status != 10); + rel.status = false; + return rel; + } + assert (status == 20); + std::vector blocking_clause {-mode_selection_variable}; + for (int i = 0; i < subcir.inputs.size(); i++) { + blocking_clause.push_back(-sin_assm[i]); + } + std::vector pattern (2*subcir.outputs.size(), false); + for (int i = 0; i < subcir.outputs.size(); i++) { + if (solver.isFailed(sout_assm[i]) || outputs_to_keep[i]) { + pattern[2*i] = true; + pattern[2*i + 1] = sout_assm[i] < 0; // at least one output must be assigned differently in order to preserve the function + blocking_clause.push_back(-sout_assm[i]); + } + } + solver.addClause(blocking_clause); + rel.addOutputPattern(assignment2bvindex(sin_assm), std::move(pattern)); + } + if (solver_status != 20) { + rel.status = false; + } + return rel; + } + + int RelationGenerator::assignment2bvindex(const std::vector& assm) { + int idx = 0; + for (int i = 0; i < assm.size(); i++) { + if (assm[i] > 0) { + idx |= 1 << i; + } + } + return idx; + } + +} ABC_NAMESPACE_IMPL_END \ No newline at end of file diff --git a/src/opt/eslim/relationGeneration.hpp b/src/opt/eslim/relationGeneration.hpp index 28d8dc512b..c2d95d499c 100644 --- a/src/opt/eslim/relationGeneration.hpp +++ b/src/opt/eslim/relationGeneration.hpp @@ -6,190 +6,114 @@ PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - Synopsis [Procedures for computing Boolean relations.] + Synopsis [SAT-based computation of Boolean relations.] Author [Franz-Xaver Reichl] Affiliation [University of Freiburg] - Date [Ver. 1.0. Started - March 2025.] + Date [Ver. 1.0. Started - April 2026.] - Revision [$Id: relationGeneration.hpp,v 1.00 2025/03/17 00:00:00 Exp $] + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] ***********************************************************************/ -#ifndef ABC__OPT__ESLIM__RELATIONGENERATION_h -#define ABC__OPT__ESLIM__RELATIONGENERATION_h +#ifndef ABC__OPT__ESLIM__RELATIONGENERATION_hpp +#define ABC__OPT__ESLIM__RELATIONGENERATION_hpp #include #include -#include #include "misc/util/abc_namespaces.h" -#include -#include "aig/gia/gia.h" -#include "misc/util/utilTruth.h" // ioResub.h depends on utilTruth.h -#include "base/io/ioResub.h" +#include "eslimCirMan.hpp" +#include "cadicalSolver.hpp" +#include "subcircuit.hpp" #include "utils.hpp" -// #include "satInterfaces.hpp" - ABC_NAMESPACE_CXX_HEADER_START - namespace eSLIM { - template + class RelationGenerator; + + class Relation{ + + public: + Relation(int nfanins); + const std::vector>& getPattern(int id) const {return output_patterns[id];} + int getNPatterns() const {return output_patterns.size();} + int getPatternSize(int id) const {return output_patterns[id].size();} + unsigned int getNLinesWithPattern() const {return lines_with_pattern;} + bool getStatus() const {return status;} + + + private: + + // Each entry of the first vector level corresponds to an input assignment. + // The second level lists all conflicting output assignments. + // The third lists the individual conflicting output assignments. + // One output is represented by two bits, the first bit determines if this output is present the conflicting output assignments + // and the second indicate if it is true or false + void addOutputPattern(int inputidx, std::vector&& pattern); + std::vector>> output_patterns; + + unsigned int lines_with_pattern = 0; + bool status = true; + + + friend RelationGenerator; + }; + class RelationGenerator { public: - static Abc_RData_t* computeRelation(Gia_Man_t* gia_man, const Subcircuit& subcir); + static Relation computeRelation(const eSLIMCirMan& cir, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log); private: - Gia_Man_t* pGia; + const eSLIMCirMan& cir; const Subcircuit& subcir; - - RelationGenerator(Gia_Man_t* pGia, const Subcircuit& subcir); - void setup(); - Vec_Int_t* getRelation(); - - static Abc_RData_t* constructABCRelationRepresentation(Vec_Int_t * patterns, int nof_inputs, int nof_outputs); - // Reimplementation with disabled prints - // We do not want to modify code in other parts of ABC - static Abc_RData_t * Abc_RData2Rel( Abc_RData_t * p ); - - friend Derived; - }; + const eSLIMConfig& cfg; + eSLIMLog& log; - // The class almost entirely duplicates the functionality provided by Gia_ManGenIoCombs in giaQBF.c - // But the implementation in this class allows to take forbidden pairs into account. - class RelationGeneratorABC : public RelationGenerator { + int max_var = 1; + + std::vector gate2varref; + std::vector gate2varcone; + std::vector equality_variables; + std::vector cone_input_variables; + int mode_selection_variable; - private: - RelationGeneratorABC(Gia_Man_t* pGia, const Subcircuit& subcir); - void setupImpl() {}; - Vec_Int_t* getRelationImpl(); - Gia_Man_t * generateMiter(); - std::unordered_set inputs_in_forbidden_pairs; - - friend RelationGenerator; - }; + CadicalSolver solver; + RelationGenerator(const eSLIMCirMan& cir, const Subcircuit& subcir, const eSLIMConfig& cfg, eSLIMLog& log); + void setupEncoding(); - // Add other engine for the generation of relations - // class MyRelationGenerator : public RelationGenerator { - // private: - // void setupImpl() - // Vec_Int_t* getRelationImpl(); - // friend RelationGenerator; - // }; - - - template - inline RelationGenerator::RelationGenerator(Gia_Man_t* pGia, const Subcircuit& subcir) - : pGia(pGia), subcir(subcir) { - } - - template - inline Abc_RData_t* RelationGenerator::computeRelation(Gia_Man_t* gia_man, const Subcircuit& subcir) { - T generator(gia_man, subcir); - generator.setup(); - Vec_Int_t* relation_patterns_masks = generator.getRelation(); - if ( relation_patterns_masks == NULL ) { - return nullptr; - } - int nof_inputs = subcir.nof_inputs; - int nof_outputs = Vec_IntSize(subcir.io) - subcir.nof_inputs; - Abc_RData_t* r = constructABCRelationRepresentation(relation_patterns_masks, nof_inputs, nof_outputs); - Vec_IntFree(relation_patterns_masks); - return r; - } - - template - inline Abc_RData_t* RelationGenerator::constructABCRelationRepresentation(Vec_Int_t * patterns, int nof_inputs, int nof_outputs) { - int i, mask; - int nof_vars = nof_inputs + nof_outputs; - Abc_RData_t* p = Abc_RDataStart( nof_inputs, nof_outputs, Vec_IntSize(patterns) ); - Vec_IntForEachEntry( patterns, mask, i ) { - for ( int k = 0; k < nof_vars; k++ ) { - if ( (mask >> (nof_vars-1-k)) & 1 ) { - if ( k < nof_inputs ) { - Abc_RDataSetIn( p, k, i ); - } else { - Abc_RDataSetOut( p, 2*(k-nof_inputs)+1, i ); - } - } else { - if ( k >= nof_inputs ) { - Abc_RDataSetOut( p, 2*(k-nof_inputs), i ); - } - } - } - } - Abc_RData_t* p2 = Abc_RData2Rel(p); - Abc_RDataStop(p); - return p2; - } - - template - inline Abc_RData_t * RelationGenerator::Abc_RData2Rel( Abc_RData_t * p ) { - assert( p->nIns < 64 ); - assert( p->nOuts < 32 ); - int w; - Vec_Wrd_t * vSimsIn2 = Vec_WrdStart( 64*p->nSimWords ); - Vec_Wrd_t * vSimsOut2 = Vec_WrdStart( 64*p->nSimWords ); - for ( w = 0; w < p->nIns; w++ ) - Abc_TtCopy( Vec_WrdEntryP(vSimsIn2, w*p->nSimWords), Vec_WrdEntryP(p->vSimsIn, w*p->nSimWords), p->nSimWords, 0 ); - for ( w = 0; w < p->nOuts; w++ ) - Abc_TtCopy( Vec_WrdEntryP(vSimsOut2, w*p->nSimWords), Vec_WrdEntryP(p->vSimsOut, (2*w+1)*p->nSimWords), p->nSimWords, 0 ); - Vec_Wrd_t * vTransIn = Vec_WrdStart( 64*p->nSimWords ); - Vec_Wrd_t * vTransOut = Vec_WrdStart( 64*p->nSimWords ); - Extra_BitMatrixTransposeP( vSimsIn2, p->nSimWords, vTransIn, 1 ); - Extra_BitMatrixTransposeP( vSimsOut2, p->nSimWords, vTransOut, 1 ); - Vec_WrdShrink( vTransIn, p->nPats ); - Vec_WrdShrink( vTransOut, p->nPats ); - Vec_Wrd_t * vTransInCopy = Vec_WrdDup(vTransIn); - Vec_WrdUniqify( vTransInCopy ); - // if ( Vec_WrdSize(vTransInCopy) == p->nPats ) - // printf( "This resub problem is not a relation.\n" ); - // create the relation - Abc_RData_t * pNew = Abc_RDataStart( p->nIns, 1 << (p->nOuts-1), Vec_WrdSize(vTransInCopy) ); - pNew->nOuts = p->nOuts; - int i, k, n, iLine = 0; word Entry, Entry2; - Vec_WrdForEachEntry( vTransInCopy, Entry, i ) { - for ( n = 0; n < p->nIns; n++ ) - if ( (Entry >> n) & 1 ) - Abc_InfoSetBit( (unsigned *)Vec_WrdEntryP(pNew->vSimsIn, n*pNew->nSimWords), iLine ); - Vec_WrdForEachEntry( vTransIn, Entry2, k ) { - if ( Entry != Entry2 ) - continue; - Entry2 = Vec_WrdEntry( vTransOut, k ); - assert( Entry2 < (1 << p->nOuts) ); - Abc_InfoSetBit( (unsigned *)Vec_WrdEntryP(pNew->vSimsOut, Entry2*pNew->nSimWords), iLine ); - } - iLine++; - } - assert( iLine == pNew->nPats ); - Vec_WrdFree( vTransOut ); - Vec_WrdFree( vTransInCopy ); - Vec_WrdFree( vTransIn ); - Vec_WrdFree( vSimsIn2 ); - Vec_WrdFree( vSimsOut2 ); - return pNew; - } - - template - inline void RelationGenerator::setup() { - static_cast(this)->setupImpl(); - } - - template - inline Vec_Int_t* RelationGenerator::getRelation() { - return static_cast(this)->getRelationImpl(); - } -} + void encodeCircuitAffected(); + void encodeCircuitApproximative(); + void encodeCircuitApproximativeBoundedTFI(); + std::vector markRestrictedCone(); + void setupConstantGate(); + void setupEqualityConstraints(const std::vector& nodes); + + Relation getRelation(); + void encodeGate(const eSLIMCirObj& obj, int var, const std::vector& gate2var); + void getGateEncodingUnique(const eSLIMCirObj& obj, int var, const std::vector& gate2var, bool unique_bit); + void getGateEncodingNaive(const eSLIMCirObj& obj, int var, const std::vector& gate2var); + void getEqualityClauses(int x, int y, int aux); + int assignment2bvindex(const std::vector& assm); + + int findConflict(double& timeout, int mode_selection); + int reduceConflict(double& timeout, const std::vector& cone_input, const std::vector& subcircuit_output, const std::vector& equality); + + + int getNewVar(); + + }; + +} ABC_NAMESPACE_CXX_HEADER_END -#endif +#endif \ No newline at end of file diff --git a/src/opt/eslim/relationSynthesiser.cpp b/src/opt/eslim/relationSynthesiser.cpp new file mode 100644 index 0000000000..6e639fc04e --- /dev/null +++ b/src/opt/eslim/relationSynthesiser.cpp @@ -0,0 +1,612 @@ +/**CFile**************************************************************** + + FileName [relationSynthesiser.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Base class for SAT-based synthesis] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#include + +#include "relationSynthesiser.hpp" + + +ABC_NAMESPACE_IMPL_START +namespace eSLIM { + + RelationSynthesiser::RelationSynthesiser(const Relation& relation, const Subcircuit& subcir, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log) + : relation(relation), subcir(subcir), + max_size(max_size), config(cfg), log(log) { + setupEncoding(); + } + + double RelationSynthesiser::getDynamicTimeout(int size) { + if (log.nof_sat_calls_per_size[size] > config.minimum_dynamic_timeout_sample_size) { + return std::max(static_cast(config.minimum_sat_timeout), static_cast(log.cummulative_sat_runtimes_per_size[size]) / (1000*log.nof_sat_calls_per_size[size])); //logged timings are given in ms + } else { + return config.base_sat_timeout; + } + } + + unsigned int RelationSynthesiser::getSizeFromActivationVars(const std::vector& model) { + unsigned int sz = 0; + for (int i = 0; i < max_size; i++) { + if (model[gate_activation_variables[i]]) { + sz++; + } else { + break; + } + } + return sz; + } + + unsigned int RelationSynthesiser::getSizeFromActivationVars() { + unsigned int sz = 0; + for (int i = 0; i < max_size; i++) { + if (solver.getValue(gate_activation_variables[i])) { + sz++; + } else { + break; + } + } + return sz; + } + + void RelationSynthesiser::logRun(int status, unsigned int size) { + log.cummulative_sat_runtime_synthesis += solver.getRunTime(); + log.max_sat_runtime_synthesis = std::max(log.max_sat_runtime_synthesis, solver.getRunTime()); + + log.nof_sat_calls_synthesis++; + if (status == 10) { + log.cummulative_sat_runtimes_per_size[size] += solver.getRunTime(); + log.nof_sat_calls_per_size[size] ++; + } else if (status == 20) { //if the solver does not report sat the formula is unsat or there was a timeout + log.cummulative_unsat_runtimes_per_size[size] += solver.getRunTime(); + log.nof_unsat_calls_per_size[size] ++; + } + } + + std::vector RelationSynthesiser::getSizeAssumption(int size) { + std::vector assumptions; + assumptions.reserve(max_size - size); + for (int i = size; i < max_size; i++) { + assumptions.push_back(-gate_activation_variables[i]); + } + return assumptions; + } + + eSLIMCirMan RelationSynthesiser::getReplacement(const std::vector& model, int size) { + assert (model.size() > 0); + eSLIMCirMan circ(subcir.inputs.size() + subcir.outputs.size() + size); + for (int i = 0; i < subcir.inputs.size(); i++) { + circ.addPi(); + } + + for (int i = 0; i < size; i++) { + std::vector fanins; + for (int j = 0; j < selection_variables[i].size(); j++) { + if (model[selection_variables[i][j]]) { + fanins.push_back(j+1); + } + } + ABC_UINT64_T tt = 0; + for (int j = 0; j < gate_definition_variables[i].size(); j++) { + int idx = j + 1; + if (model[gate_definition_variables[i][j]]) { + tt |= static_cast(1) << idx; + } + } + circ.addNode(fanins, tt); + } + + // outputs + for (int i = 0; i < subcir.outputs.size(); i++) { + for (int j = 0; j < max_size; j++) { + if (model[gate_output_variables[j][i]]) { + circ.addPo(subcir.inputs.size() + j + 1, false); + goto end; + } + } + for (int j = 0; j < subcir.inputs.size(); j++) { + if (model[gate_output_variables[max_size + j][i]]) { + circ.addPo(j + 1, false); + goto end; + } + } + if (model[gate_output_variables.back()[i]]) { + circ.addPo(0, false); + } + end:; + } + return circ; + } + + void RelationSynthesiser::setupEncoding() { + setupVariables(); + setupStructuralConstraints(); + setupSymmetryBreaking(); + setupEquivalenceConstraints(); + } + + void RelationSynthesiser::setupVariables() { + gate_activation_variables.reserve(max_size); + selection_variables.reserve(max_size); + gate_definition_variables.reserve(max_size); + gate_variables.reserve(max_size); + is_ith_fanin_variable.reserve(max_size); + for (int i = 0; i < max_size; i++) { + gate_activation_variables.push_back(getNewVariable()); + selection_variables.push_back(getNewVariableVector(subcir.inputs.size() + i)); + gate_definition_variables.push_back(getNewVariableVector(pow2(config.gate_size) - 1)); + // The replacement circuit shall be normal so we ignore the all-false-behaviour + int normality_offset = relation.getPatternSize(0) > 0; + gate_variables.push_back(getNewVariableVector(relation.getNLinesWithPattern() - normality_offset)); + + std::vector> fanin_vars; + for (int j = 0; j < config.gate_size; j++) { + std::vector fvs(subcir.inputs.size() + i, 0); + for (int k = j; k < subcir.inputs.size() + i - config.gate_size + j + 1; k++) { + fvs[k] = getNewVariable(); + } + fanin_vars.push_back(fvs); + } + is_ith_fanin_variable.push_back(fanin_vars); + } + gate_output_variables.reserve(max_size + subcir.inputs.size() + 1); + for (int i = 0; i < max_size + subcir.inputs.size() + 1; i++) { + gate_output_variables.push_back(getNewVariableVector(subcir.outputs.size())); + } + } + + void RelationSynthesiser::setupStructuralConstraints() { + constrainSelectionVariables(); + constraintGateOutputVariables(); + setupActivationCompatibilityConstraints(); + if (config.aig) { + setupAigerConstraints(); + } + } + + void RelationSynthesiser::setupSymmetryBreaking() { + addNonTrivialConstraint(); + addUseAllStepsConstraint(); + // addNoReapplicationConstraint(); + // addOrderedStepsConstraint(); + } + + void RelationSynthesiser::setupIsFaninVariables(int gtidx, const std::vector>& counter_vars) { + // If we have 3 inputs and gates with two fanins then the first fanin of the first gate can be input 1 or 2 but not input 3. + int noptions = subcir.inputs.size() + gtidx - config.gate_size + 1; + for (int i = 0; i < config.gate_size; i++) { + solver.addClause({-counter_vars[i][i], is_ith_fanin_variable[gtidx][i][i]}); + solver.addClause({counter_vars[i][i], -is_ith_fanin_variable[gtidx][i][i]}); + int offset = (i == config.gate_size - 1); // we do not use block variables for the last selection variables + for (int j = i + 1; j < i + noptions - offset; j++) { + solver.addClause({counter_vars[j - 1][i], counter_vars[j][i], -is_ith_fanin_variable[gtidx][i][j]}); + solver.addClause({counter_vars[j - 1][i], -counter_vars[j][i], is_ith_fanin_variable[gtidx][i][j]}); + } + } + solver.addClause({selection_variables[gtidx].back(), -is_ith_fanin_variable[gtidx].back().back()}); + solver.addClause({-selection_variables[gtidx].back(), is_ith_fanin_variable[gtidx].back().back()}); + } + + void RelationSynthesiser::constrainSelectionVariables() { + int start = 0; + // if the subcircuit has k inputs and gates have k fanins each input is a fanin of the first gate + if (selection_variables[0].size() == config.gate_size) { + start = 1; + for (int j = 0; j < config.gate_size; j++) { + solver.addClause({selection_variables[0][j]}); + solver.addClause({is_ith_fanin_variable[0][j][j]}); + } + } + for (int i = start; i < max_size; i++) { + std::vector> counter_vars = addSequentialCounter(selection_variables[i], config.gate_size); + setupIsFaninVariables(i, counter_vars); + } + } + + void RelationSynthesiser::constraintGateOutputVariables() { + // If the activation variable is false then the gate must not be an output + for (int i = 0; i < max_size; i++) { + for (int v : gate_output_variables[i]) { + solver.addClause({gate_activation_variables[i], -v}); + } + } + for (int i = 0; i < subcir.outputs.size(); i++) { + std::vector clause; + clause.reserve(max_size + subcir.inputs.size() + 1); + for (int j = 0; j < max_size + subcir.inputs.size() + 1; j++) { + clause.push_back(gate_output_variables[j][i]); + } + addCardinalityConstraint(clause, 1); + } + } + + void RelationSynthesiser::addGateValueConstraint(int gtidx, int gt_val, const std::vector& fanin_variables) { + // If all fanins are 0, then the gate yields 0 + std::vector clause {-gate_activation_variables[gtidx]}; + clause.reserve(config.gate_size + 3); + for (int j = 0; j < config.gate_size; j++) { + clause.push_back(fanin_variables[j]); + } + clause.push_back(-gt_val); + solver.addClause(clause); + clause.push_back(0); + + for (int i = 1; i < pow2(config.gate_size); i++) { // we consider normal gates -> the all-0-behaviour does not need to be considered + for (int j = 0; j < config.gate_size; j++) { + clause[j + 1] = abs(clause[j + 1]) * (1-2*getTTValue(i, j)); + } + int gdv = gate_definition_variables[gtidx][i - 1]; // we do not use a gdv for the all-0-behaviour + clause[config.gate_size + 1] = -gdv; + clause[config.gate_size + 2] = gt_val; + solver.addClause(clause); + clause[config.gate_size + 1] = gdv; + clause[config.gate_size + 2] = -gt_val; + solver.addClause(clause); + } + } + + std::vector RelationSynthesiser::setupFaninVariables(int gtidx, int tt_line, int pidx) { + int noptions = subcir.inputs.size() + gtidx - config.gate_size + 1; + std::vector fanin_variables = getNewVariableVector(config.gate_size); + for (int i = 0; i < config.gate_size; i++) { + int bound = i + noptions < subcir.inputs.size() ? i + noptions : subcir.inputs.size(); + for (int j = i; j < bound; j++) { + if (getTTValue(tt_line, j)) { + solver.addClause({-is_ith_fanin_variable[gtidx][i][j], fanin_variables[i]}); + } else { + solver.addClause({-is_ith_fanin_variable[gtidx][i][j], -fanin_variables[i]}); + } + } + for (int j = subcir.inputs.size(); j < i + noptions; j++) { + solver.addClause({-is_ith_fanin_variable[gtidx][i][j], gate_variables[j - subcir.inputs.size()][pidx], -fanin_variables[i]}); + solver.addClause({-is_ith_fanin_variable[gtidx][i][j], -gate_variables[j - subcir.inputs.size()][pidx], fanin_variables[i]}); + } + } + return fanin_variables; + } + + void RelationSynthesiser::setupGateValues() { + int pidx = 0; + for (int i = 1; i < relation.getNPatterns(); i++) { + if (relation.getPatternSize(i) > 0) { + for (int j = 0; j < max_size; j++) { + std::vector fanin_variables = setupFaninVariables(j, i, pidx); + addGateValueConstraint(j, gate_variables[j][pidx], fanin_variables); + } + pidx++; + } + } + } + + void RelationSynthesiser::setupEquivalenceConstraints() { + setupGateValues(); + // normal circuit: all-0-behaviour is ignored + int pidx = 0; + for (int i = 1; i < relation.getNPatterns(); i++) { + std::vector output_vars (subcir.outputs.size(), 0); + const auto& patterns = relation.getPattern(i); + for (const auto& p : patterns) { + assert(2 * subcir.outputs.size() == p.size()); + std::vector cl; + for (int j = 0; j < subcir.outputs.size(); j++) { + if (p[2*j]) { + if (output_vars[j] == 0) { + output_vars[j] = setupOutputVariable(j, i, pidx); + } + if (p[2*j + 1]) { + cl.push_back(output_vars[j]); + } else { + cl.push_back(-output_vars[j]); + } + } + } + solver.addClause(cl); + } + pidx += !patterns.empty(); + } + } + + void RelationSynthesiser::setupActivationCompatibilityConstraints() { + for (int i = 1; i < max_size; i++) { + solver.addClause({-gate_activation_variables[i], gate_activation_variables[i-1]}); + } + } + + int RelationSynthesiser::setupOutputVariable(int output_index, int tt_index, int pattern_idx) { + int var = getNewVariable(); + std::vector clause; + for (int i = 0; i < max_size; i++) { + clause.assign({-gate_activation_variables[i], -gate_output_variables[i][output_index], -gate_variables[i][pattern_idx], var}); + solver.addClause(clause); + clause[2] = -clause[2]; + clause[3] = -clause[3]; + solver.addClause(clause); + } + + for (int i = 0; i < subcir.inputs.size(); i++) { + if (getTTValue(tt_index, i)) { + clause.assign({-gate_output_variables[i + max_size][output_index], var}); + } else { + clause.assign({-gate_output_variables[i + max_size][output_index], -var}); + } + solver.addClause(clause); + } + + clause.assign({-gate_output_variables.back()[output_index], -var}); + solver.addClause(clause); + return var; + } + + void RelationSynthesiser::setupAigerConstraints() { + assert (config.gate_size == 2); + for (int i = 0; i < max_size; i++) { + solver.addClause({-gate_definition_variables[i][0], -gate_definition_variables[i][1], gate_definition_variables[i][2]}); + } + } + + void RelationSynthesiser::addCardinalityConstraint(const std::vector& vars, unsigned int cardinality, int activator) { + assert (cardinality > 0); + assert (vars.size() >= cardinality); + if (vars.size() == cardinality) { + std::vector clause; + if (activator != 0) { + clause.push_back(activator); + } + for (int var : vars) { + clause.push_back(var); + solver.addClause(clause); + clause.pop_back(); + } + } else { + // CHECK: is the naive encoding better? + if (activator == 0) { + if (cardinality == 1) { + addNaiveIs1CardinalityConstraint(vars); + } else if (cardinality == 2) { + addNaiveIs2CardinalityConstraint(vars); + } else { + addSequentialCounter(vars, cardinality, activator); + } + } else { + addSequentialCounter(vars, cardinality, activator); + } + } + } + + // see Carsten Sinz: Towards an Optimal CNF Encoding of Boolean Cardinality Constraints + // As we want that exactly cardinality many variables are set to true we need additional constraints. + // Because of this reason we cannot use the single polarity encoding presented in the paper + std::vector> RelationSynthesiser::addSequentialCounter(const std::vector& vars, unsigned int cardinality, int activator) { + std::vector> component_outputs; + component_outputs.push_back({vars.front()}); + assert (vars.size() > cardinality); + for (int i = 1; i < vars.size() - 1; i++) { + std::vector current_component_outputs; + int v = vars[i]; + int in_var = v; + for (int j = 0; j < component_outputs.back().size(); j++) { + int orv = getNewVariable(); + defineDisjunction(orv, {in_var, component_outputs.back()[j]}, activator); + current_component_outputs.push_back(orv); + if (j < cardinality - 1) { + in_var = getNewVariable(); + defineConjunction(in_var, {v, component_outputs.back()[j]}, activator); + } else { + solver.addClause({-component_outputs.back().back(), -v}); + } + } + if (component_outputs.back().size() < cardinality) { + current_component_outputs.push_back(in_var); + } + component_outputs.push_back(current_component_outputs); + } + + solver.addClause({-component_outputs.back().back(), -vars.back()}); + + // Either subcircuit_outputs.back() is true or subcircuit_outputs[-2] and vars.back() is true + std::vector clause {component_outputs.back().back(), vars.back()}; + solver.addClause(clause); + if (cardinality > 1) { + clause.pop_back(); + clause.push_back(component_outputs.back()[component_outputs.back().size() - 2]); + solver.addClause(clause); + } + return component_outputs; + } + + void RelationSynthesiser::addNaiveIs1CardinalityConstraint(const std::vector& vars) { + solver.addClause(vars); + for (int i = 0; i < vars.size(); i++) { + for (int j = i + 1; j < vars.size(); j++) { + solver.addClause({-vars[i], -vars[j]}); + } + } + } + + void RelationSynthesiser::addNaiveIs2CardinalityConstraint(const std::vector& vars) { + std::vector cl; + cl.reserve(vars.size() - 1); + for (int i = 0; i < vars.size(); i++) { + for (int j = 0; j < i; j++) { + cl.push_back(vars[j]); + } + for (int j = i + 1; j < vars.size(); j++) { + cl.push_back(vars[j]); + } + solver.addClause(cl); + cl.clear(); + + for (int j = i + 1; j < vars.size(); j++) { + for (int k = j + 1; k < vars.size(); k++) { + solver.addClause({-vars[i], -vars[j], -vars[k]}); + } + } + } + } + + void RelationSynthesiser::defineConjunction(int var, std::vector&& literals, int activator) { + std::vector big_clause, small_clause; + if (activator != 0) { + big_clause.reserve(literals.size() + 2); + small_clause.reserve(3); + big_clause.push_back(activator); + small_clause.push_back(activator); + } else { + big_clause.reserve(literals.size() + 1); + small_clause.reserve(2); + } + big_clause.push_back(var); + small_clause.push_back(-var); + for (int lit : literals) { + big_clause.push_back(-lit); + small_clause.push_back(lit); + solver.addClause(small_clause); + small_clause.pop_back(); + } + solver.addClause(big_clause); + } + + void RelationSynthesiser::defineDisjunction(int var, std::vector&& literals, int activator) { + std::vector clause; + literals.push_back(-var); + if (activator != 0) { + literals.push_back(activator); + solver.addClause(literals); + literals.pop_back(); + clause.reserve(3); + clause.push_back(activator); + } else { + clause.reserve(2); + solver.addClause(literals); + } + literals.pop_back(); + clause.push_back(var); + for (int lit : literals) { + clause.push_back(-lit); + solver.addClause(clause); + clause.pop_back(); + } + } + + void RelationSynthesiser::addNonTrivialConstraint() { + for (int i = 0; i < max_size; i++) { + // We do not allow constant gates + solver.addClause(gate_definition_variables[i]); + // We exclude gates representing the projection to one of its inputs + for (int j = 0; j < config.gate_size; j++) { + int start = pow2(config.gate_size - j); + int block_length = pow2(config.gate_size - j - 1); + std::vector clause; + clause.reserve(pow2(config.gate_size) - 1); + for (int k = 0; k < pow2(j); k++) { + for (int l = k * start - (k == 0 ? 0 : 1); l < k * start + block_length -1; l++) { + clause.push_back(gate_definition_variables[i][l]); + } + for (int l = k * start + block_length - 1; l < (k + 1) * start - 1; l++) { + clause.push_back(-gate_definition_variables[i][l]); + } + } + solver.addClause(clause); + } + } + } + + // Every gate is either an output or an input of another gate + void RelationSynthesiser::addUseAllStepsConstraint() { + for (int i = 0; i < max_size; i++) { + std::vector clause (gate_output_variables[i].begin(), gate_output_variables[i].end()); + clause.reserve(max_size - i + 1); + for (int j = i + 1; j < max_size; j++) { + clause.push_back(selection_variables[j][subcir.inputs.size() + i]); + } + clause.push_back(-gate_activation_variables[i]); + solver.addClause(clause); + } + } + + + // Suppose gate i has inputs i1,...,in and gate j uses i as an input. + // Then the other n-1 inputs of j shall not be contained in the inputs of i. + // Otherwise j could be directly represented by a gate with inputs i1,...,in + void RelationSynthesiser::addNoReapplicationConstraint() { + std::vector filter(config.gate_size, 1); + // A 1 at index i indicates that the ith node is used as an input. + // By using prev_permutation we get all possible permutations of the filter + filter.resize(subcir.inputs.size() + max_size - 1); + std::vector indices(config.gate_size,0); + // int tt_size = RelationSynthesiser::pow2(subcir.inputs.size()) - 1; + do { + indices.clear(); + for (int i = 0; i < subcir.inputs.size() + max_size - 1; i++) { + if (filter[i]) { + indices.push_back(i); + } + } + int first_gate = getPotentialSuccessors(indices); + for (int i = first_gate; i < max_size; i++) { + std::vector clause; + clause.reserve(subcir.inputs.size() + max_size); + for (int idx : indices) { + clause.push_back(-selection_variables[i][idx]); + } + int start_with = clause.size(); + for (int j = i + 1; j < max_size; j++) { + // gate j has inputs.size() + j potential inputs + clause.resize(subcir.inputs.size() + j + 1, 0); + clause[start_with] = -selection_variables[j][subcir.inputs.size() + i]; + clause[start_with + 1] = -gate_activation_variables[j]; + int k = 0, l = 0, m = 0; + // while (k < inputs.size() + j && l < indices.size()) { + while (k < subcir.inputs.size() + j) { + if (l >= indices.size() || k < indices[l]) { + if (k != subcir.inputs.size() + i) { + clause[start_with + m + 2] = selection_variables[j][k]; + m++; + } + k++; + } else if (k > indices[l]) { + l++; + } else { + k++; + l++; + } + } + solver.addClause(clause); + } + } + } while (std::prev_permutation(filter.begin(), filter.end())); + } + + // Order steps according to their inputs. + // If step i has input j then step i+1 must have an input >=j + void RelationSynthesiser::addOrderedStepsConstraint() { + for (int i = 0; i < max_size - 1; i++) { + for (int j = 0; j < subcir.inputs.size() + i; j++) { + std::vector clause {-gate_activation_variables[i+1], -selection_variables[i][j]}; + clause.reserve(subcir.inputs.size() + i - j + 3); + for (int k = j; k < subcir.inputs.size() + i + 1; k++) { + clause.push_back(selection_variables[i+1][k]); + } + solver.addClause(clause); + } + } + } + + +} +ABC_NAMESPACE_IMPL_END \ No newline at end of file diff --git a/src/opt/eslim/relationSynthesiser.hpp b/src/opt/eslim/relationSynthesiser.hpp new file mode 100644 index 0000000000..c1f170c7f1 --- /dev/null +++ b/src/opt/eslim/relationSynthesiser.hpp @@ -0,0 +1,145 @@ +/**CFile**************************************************************** + + FileName [relationSynthesiser.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Base class for SAT-based synthesis] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__RELATIONSYNTHESISER_hpp +#define ABC__OPT__ESLIM__RELATIONSYNTHESISER_hpp + +#include +#include + +#include "misc/util/abc_global.h" + +#include "relationGeneration.hpp" +#include "subcircuit.hpp" +#include "cadicalSolver.hpp" +#include "utils.hpp" + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class RelationSynthesiser { + + protected: + CadicalSolver solver; + const Subcircuit& subcir; + unsigned int max_size; // The maximal circuit size that can be checked + + std::vector gate_activation_variables; + std::vector> selection_variables; + std::vector> gate_output_variables; + + RelationSynthesiser(const Relation& relation, const Subcircuit& subcir, unsigned int max_size, const eSLIMConfig& cfg, eSLIMLog& log); + int getNewVariable(); + std::vector getNewVariableVector(unsigned int size); + + std::vector getSizeAssumption(int size); + double getDynamicTimeout(int size); + unsigned int getSizeFromActivationVars(const std::vector& model); + unsigned int getSizeFromActivationVars(); + void logRun(int status, unsigned int size); + eSLIMCirMan getReplacement(const std::vector& model, int size); + + + private: + const Relation& relation; + const eSLIMConfig& config; + eSLIMLog& log; + + int max_var = 0; // The maximal variable occurring in inputs/outputs + + std::vector>> is_ith_fanin_variable; + std::vector> gate_definition_variables; + std::vector> gate_variables; + + + void setupEncoding(); + void setupVariables(); + void setupStructuralConstraints(); + + void setupSymmetryBreaking(); + void setupGateValues(); + std::vector setupFaninVariables(int gtidx, int tt_line, int pidx); + void setupEquivalenceConstraints(); + void setupActivationCompatibilityConstraints(); + void setupIsFaninVariables(int gtidx, const std::vector>& counter_vars); + + void constrainSelectionVariables(); + void constraintGateOutputVariables(); + void addGateValueConstraint(int gtidx, int gt_val, const std::vector& fanin_variables); + void setupCycleConstraints(); + void setupAigerConstraints(); + + int setupOutputVariable(int output_index, int tt_index, int pattern_idx); + + void addNonTrivialConstraint(); + void addUseAllStepsConstraint(); + void addNoReapplicationConstraint(); + void addOrderedStepsConstraint(); + + // activator = 0: do not use an activation variable + void addCardinalityConstraint(const std::vector& vars, unsigned int cardinality, int activator = 0); + std::vector> addSequentialCounter(const std::vector& vars, unsigned int cardinality, int activator = 0); + + void addNaiveIs1CardinalityConstraint(const std::vector& vars); + void addNaiveIs2CardinalityConstraint(const std::vector& vars); + + void defineConjunction(int var, std::vector&& literals, int activator); + void defineDisjunction(int var, std::vector&& literals, int activator); + + // computes the index of the first gate that can used the indexed nodes as inputs + // indices must be ordered + int getPotentialSuccessors(const std::vector& indices) const; + + static bool getTTValue(int tt_line, int var); + static unsigned int pow2(int x); + + }; + + inline int RelationSynthesiser::getNewVariable() { + return ++max_var; + } + + inline std::vector RelationSynthesiser::getNewVariableVector(unsigned int size) { + std::vector vars; + vars.reserve(size); + for (int i = 0; i < size; i++) { + vars.push_back(getNewVariable()); + } + return vars; + } + + inline int RelationSynthesiser::getPotentialSuccessors(const std::vector& indices) const { + if (indices.back() < subcir.inputs.size()) { + return 0; + } + return indices.back() - subcir.inputs.size() + 1; + } + + inline bool RelationSynthesiser::getTTValue(int tt_line, int var) { + return (tt_line >> var) & 1; + } + + inline unsigned int RelationSynthesiser::pow2(int x) { + return 1u << x; + } + +} +ABC_NAMESPACE_CXX_HEADER_END +#endif \ No newline at end of file diff --git a/src/opt/eslim/satInterfaces.hpp b/src/opt/eslim/satInterfaces.hpp deleted file mode 100644 index 3300d9e593..0000000000 --- a/src/opt/eslim/satInterfaces.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/**CFile**************************************************************** - - FileName [satInterfaces.hpp] - - SystemName [ABC: Logic synthesis and verification system.] - - PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - - Synopsis [Interface to SAT solvers.] - - Author [Franz-Xaver Reichl] - - Affiliation [University of Freiburg] - - Date [Ver. 1.0. Started - March 2025.] - - Revision [$Id: satInterfaces.hpp,v 1.00 2025/03/17 00:00:00 Exp $] - -***********************************************************************/ - -#ifndef ABC__OPT__ESLIM__SATINTERFACE_h -#define ABC__OPT__ESLIM__SATINTERFACE_h - -#include -#include -#include - -#include "misc/util/abc_namespaces.h" -#include "misc/vec/vec.h" -#include "sat/kissat/kissat.h" -#include "sat/cadical/cadical.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace eSLIM { - - class CadicalSolver { - public: - void addClause(int * pLits, int nLits ); - void assume(const std::vector& assumptions); - int solve(double timeout); - int solve(); - Vec_Int_t* getModelVec(); - double getRunTime() const; - - private: - int val(int variable); - double last_runtime; - CaDiCaL::Solver solver; - - public: - class TimeoutTerminator : public CaDiCaL::Terminator { - public: - TimeoutTerminator(double max_runtime); - bool terminate(); - - private: - double max_runtime; //in seconds - std::chrono::time_point start = std::chrono::steady_clock::now(); - }; - }; - - class KissatSolver { - public: - KissatSolver(); - ~KissatSolver(); - void init(int max_var); - void addClause(int * pLits, int nLits ); - int solve(); - // int solve(int timeout); - Vec_Int_t* getModelVec(); - - private: - - int max_var = 0; - int val(int variable); - kissat * solver = nullptr; - }; - - inline CadicalSolver::TimeoutTerminator::TimeoutTerminator(double max_runtime) : max_runtime(max_runtime) {} - - inline bool CadicalSolver::TimeoutTerminator::terminate() { - auto current_time = std::chrono::steady_clock::now(); - std::chrono::duration elapsed_seconds = current_time - start; - return elapsed_seconds.count() > max_runtime; - } - - inline double CadicalSolver::getRunTime() const { - return last_runtime; - } - - inline void CadicalSolver::addClause(int * pLits, int nLits ) { - for ( int i = 0; i < nLits; i++ ) { - if (pLits[i] == 0) { - continue;; - } - solver.add(Abc_LitIsCompl(pLits[i]) ? -Abc_Lit2Var(pLits[i]) : Abc_Lit2Var(pLits[i])); - } - solver.add(0); - } - - inline void CadicalSolver::assume(const std::vector& assumptions) { - for (auto& l: assumptions) { - solver.assume(l); - } - } - - inline int CadicalSolver::solve() { - std::chrono::time_point start = std::chrono::steady_clock::now(); - int status = solver.solve(); - last_runtime = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); - return status; - } - - inline int CadicalSolver::solve(double timeout) { - std::chrono::time_point start = std::chrono::steady_clock::now(); - TimeoutTerminator terminator(timeout); - solver.connect_terminator(&terminator); - int status = solver.solve(); - last_runtime = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); - if (solver.state() == CaDiCaL::INVALID) { - std::cerr<<"Solver is in invalid state"< 0 ); - } - return assignment; - } - - inline int CadicalSolver::val(int variable) { - auto l = solver.val(variable); - auto v = abs(l); - if (v == variable) { - return l; - } else { - return variable; - } - } - - inline KissatSolver::KissatSolver() { - solver = kissat_init(); - } - - inline KissatSolver::~KissatSolver() { - kissat_release(solver); - } - - inline void KissatSolver::init(int max_var) { - this->max_var = max_var; - kissat_reserve(solver, max_var); - } - - inline void KissatSolver::addClause(int * pLits, int nLits ) { - for ( int i = 0; i < nLits; i++ ) { - if (pLits[i] == 0) { - continue; - } - kissat_add (solver, Abc_LitIsCompl(pLits[i]) ? -Abc_Lit2Var(pLits[i]) : Abc_Lit2Var(pLits[i])); - } - kissat_add (solver, 0); - } - - inline int KissatSolver::solve() { - int status = kissat_solve(solver); - return status; - } - - inline int KissatSolver::val(int variable) { - int l = kissat_value (solver, variable); - auto v = abs(l); - if (v == variable) { - return l; - } else { - return variable; - } - } - - inline Vec_Int_t* KissatSolver::getModelVec() { - Vec_Int_t* assignment = Vec_IntAlloc( max_var ); - for (int v = 1; v <= max_var; v++) { - Vec_IntSetEntryFull( assignment, Abc_AbsInt(val(v)), val(v) > 0 ); - } - return assignment; - } -} - -ABC_NAMESPACE_CXX_HEADER_END - -#endif \ No newline at end of file diff --git a/src/opt/eslim/selectionStrategies.hpp b/src/opt/eslim/selectionStrategies.hpp new file mode 100644 index 0000000000..105ef3be1b --- /dev/null +++ b/src/opt/eslim/selectionStrategies.hpp @@ -0,0 +1,181 @@ +/**CFile**************************************************************** + + FileName [selectionStrategies.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Subcircuit selection] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__SELECTIONSTRATEGIES_hpp +#define ABC__OPT__ESLIM__SELECTIONSTRATEGIES_hpp + +#include +#include +#include + +#include "misc/util/abc_namespaces.h" + +#include "eslimCirMan.hpp" +#include "subcircuit.hpp" +#include "utils.hpp" +#include "tabooList.hpp" + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + struct SubcircuitValidator { + public: + SubcircuitValidator(const eSLIMConfig& cf); + bool check(const Subcircuit& candidate) const; + + private: + const eSLIMConfig& cfg; + static constexpr unsigned int min_size = 2; + static constexpr unsigned int max_ninputs = 10; + + }; + + struct SubcircuitNoFBValidator : public SubcircuitValidator { + public: + using SubcircuitValidator::SubcircuitValidator; + bool check(const Subcircuit& candidate) const; + }; + + class ForwardSearch { + public: + ForwardSearch(eSLIMCirMan& es_man); + auto getNextBegin(const eSLIMCirObj& obj); + auto getNextEnd(const eSLIMCirObj& obj); + bool check(const eSLIMCirObj& obj) const; + private: + eSLIMCirMan& es_man; + }; + + class BackwardSearch { + public: + BackwardSearch(eSLIMCirMan& es_man); + auto getNextBegin(const eSLIMCirObj& obj); + auto getNextEnd(const eSLIMCirObj& obj); + bool check(const eSLIMCirObj& obj) const; + private: + eSLIMCirMan& es_man; + }; + + class BaseRootSelector { + public: + BaseRootSelector(eSLIMCirMan& es_man, const eSLIMConfig& cfg); + int getRoot(std::mt19937& rng); + void reset() {} + + private: + unsigned int getUniformRandomNumber(std::mt19937& rng, unsigned int lower, unsigned int upper); // requires lower < upper + + eSLIMCirMan& es_man; + // const eSLIMConfig& cfg; + std::uniform_int_distribution udistr; + }; + + class TabooRootSelector { + public: + TabooRootSelector(eSLIMCirMan& es_man, const eSLIMConfig& cfg); + ~TabooRootSelector(); + int getRoot(std::mt19937& rng); + void reset(); + + private: + eSLIMCirMan& es_man; + + + protected: + const eSLIMConfig& cfg; + TabooList taboo; + + }; + + class CriticalPathSelector : public TabooRootSelector { + public: + using TabooRootSelector::TabooRootSelector; + int getRoot(std::mt19937& rng); + }; + + struct BaseCandidateNodeConstraint { + static bool check(int to_check, const eSLIMCirMan& cir, const std::set& subcircuit); + }; + + struct SingleOutputCandidateNodeConstraint { + static bool check(int to_check, const eSLIMCirMan& cir, const std::set& subcircuit); + }; + + struct NoBiasSelector { + static bool useBias(const eSLIMCirMan& es_man, const eSLIMCirObj& obj) {return false;} + }; + + struct CriticalPathBiasSelector { + static bool useBias(const eSLIMCirMan& es_man, const eSLIMCirObj& obj) {return es_man.isOnCricticalPath(obj);} + }; + + template + class RandomizedBFSSelection { + + public: + RandomizedBFSSelection(eSLIMCirMan& es_man, const eSLIMConfig& cfg, eSLIMLog& log); + Subcircuit getSubcircuit(bool& status); // status indicates if the search was successful + void setSeed(int seed); + void reset(); + + static constexpr bool requiresRemainingTimes() {return std::is_same_v;} + + private: + + std::set computeSubcircuitCandidate(); + int selectRoot(); + void processCandidateNode(int candidate, std::set& subcircuit, std::queue& candidates); + bool getRandomBool(); + bool getBiasedRandomBool(); + + eSLIMCirMan& es_man; + const eSLIMConfig& cfg; + static constexpr unsigned int min_size = 2; + static constexpr unsigned int max_ninputs = 10; + + eSLIMLog& log; + Validator validator; + RootSelector root_selector; + SearchDirection search_direction; + + std::mt19937 rng; + std::bernoulli_distribution bdist; + std::bernoulli_distribution biased_bdist; + }; + + + + using RamdomizedBFS=RandomizedBFSSelection; + using RamdomizedBFSForward=RandomizedBFSSelection; + using RandomizedBFSNoFB=RandomizedBFSSelection; + using RamdomizedBFSTaboo=RandomizedBFSSelection; + using RamdomizedBFSTabooForward=RandomizedBFSSelection; + using RamdomizedBFSTabooNoFB=RandomizedBFSSelection; + + using SingleOutputBFS = RandomizedBFSSelection; + + +} +ABC_NAMESPACE_CXX_HEADER_END + + +#include "selectionStrategies.tpp" + +#endif \ No newline at end of file diff --git a/src/opt/eslim/selectionStrategies.tpp b/src/opt/eslim/selectionStrategies.tpp new file mode 100644 index 0000000000..085b6126ef --- /dev/null +++ b/src/opt/eslim/selectionStrategies.tpp @@ -0,0 +1,226 @@ +/**CFile**************************************************************** + + FileName [selectionStrategies.tpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Subcircuit selection] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#include "selectionStrategies.hpp" + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + inline SubcircuitValidator::SubcircuitValidator(const eSLIMConfig& cfg) : cfg(cfg) { + + } + + inline bool SubcircuitValidator::check(const Subcircuit& candidate) const { + return candidate.inputs.size() <= max_ninputs && candidate.inputs.size() >= cfg.gate_size; + } + + inline bool SubcircuitNoFBValidator::check(const Subcircuit& candidate) const { + return SubcircuitValidator::check(candidate) && candidate.forbidden_pairs.empty(); + } + + inline ForwardSearch::ForwardSearch(eSLIMCirMan& es_man) : es_man(es_man) { + } + + inline auto ForwardSearch::getNextBegin(const eSLIMCirObj& obj) { + return obj.fanouts.begin(); + } + + inline auto ForwardSearch::getNextEnd(const eSLIMCirObj& obj) { + return obj.fanouts.end(); + } + + inline bool ForwardSearch::check(const eSLIMCirObj& obj) const { + return !es_man.isPo(obj.node_id); + } + + inline BackwardSearch::BackwardSearch(eSLIMCirMan& es_man) : es_man(es_man) { + } + + inline auto BackwardSearch::getNextBegin(const eSLIMCirObj& obj) { + return obj.fanins.begin(); + } + + inline auto BackwardSearch::getNextEnd(const eSLIMCirObj& obj) { + return obj.fanins.end(); + } + + inline bool BackwardSearch::check(const eSLIMCirObj& obj) const { + return !es_man.isPi(obj.node_id); + } + + // inline BaseRootSelector::BaseRootSelector(eSLIMCirMan& es_man, const eSLIMConfig& cfg) : es_man(es_man), cfg(cfg) { + inline BaseRootSelector::BaseRootSelector(eSLIMCirMan& es_man, const eSLIMConfig& cfg) : es_man(es_man) { + + } + + inline TabooRootSelector::TabooRootSelector(eSLIMCirMan& es_man, const eSLIMConfig& cfg) : es_man(es_man), cfg(cfg), taboo(es_man) { + es_man.registerTabooList(&taboo); + } + + inline TabooRootSelector::~TabooRootSelector() { + es_man.unregisterTabooList(); + } + + + inline unsigned int BaseRootSelector::getUniformRandomNumber(std::mt19937& rng, unsigned int lower, unsigned int upper) { + udistr.param(std::uniform_int_distribution::param_type(lower, upper)); + return udistr(rng); + } + + inline int BaseRootSelector::getRoot(std::mt19937& rng) { + int min_id = es_man.getNofPis() + 1; + int max_id = es_man.getNofObjs() - es_man.getNofPos() - 1; + return getUniformRandomNumber(rng, min_id, max_id); + } + + + inline int TabooRootSelector::getRoot(std::mt19937& rng) { + return taboo.getRandomNonTaboo(rng, cfg.taboo_ratio, cfg.taboo_time); + } + + inline void TabooRootSelector::reset() { + taboo.reset(); + es_man.registerTabooList(&taboo); + } + + inline int CriticalPathSelector::getRoot(std::mt19937& rng) { + return taboo.getBiasedRandomNonTaboo(rng, cfg.bias, cfg.taboo_ratio, cfg.taboo_time); + } + + inline bool BaseCandidateNodeConstraint::check(int to_check, const eSLIMCirMan& cir, const std::set& subcircuit) { + return subcircuit.find(to_check) == subcircuit.end(); + } + + inline bool SingleOutputCandidateNodeConstraint::check(int to_check, const eSLIMCirMan& cir, const std::set& subcircuit) { + if (subcircuit.find(to_check) != subcircuit.end()) { + return false; + } + const auto& obj = cir.getObj(to_check); + for (const auto& fo : obj.fanouts) { + // The node has a fanout that is not yet in the subcircuit + if (subcircuit.find(fo->node_id) == subcircuit.end()) { + return false; + } + } + return true; + } + + template + RandomizedBFSSelection::RandomizedBFSSelection(eSLIMCirMan& es_man, const eSLIMConfig& cfg, eSLIMLog& log) + : es_man(es_man), cfg(cfg), log(log), validator(cfg), root_selector(es_man, cfg), search_direction(es_man), + bdist(cfg.expansion_probability), biased_bdist(std::min(1., cfg.expansion_probability*1.5)) { + } + + template + void RandomizedBFSSelection::setSeed(int seed) { + rng.seed(seed); + } + + template + bool RandomizedBFSSelection::getRandomBool() { + return bdist(rng); + } + + template + bool RandomizedBFSSelection::getBiasedRandomBool() { + return biased_bdist(rng); + } + + template + int RandomizedBFSSelection::selectRoot() { + return root_selector.getRoot(rng); + } + + template + void RandomizedBFSSelection::reset() { + root_selector.reset(); + } + + template + std::set RandomizedBFSSelection::computeSubcircuitCandidate() { + std::set scir; + int root_id = selectRoot(); + std::queue candidates; + processCandidateNode(root_id, scir, candidates); + std::queue rejected_nodes; + while (!candidates.empty() && scir.size() < cfg.subcircuit_max_size) { + int c = candidates.front(); + candidates.pop(); + if (CandidateNodeConstraint::check(c, es_man, scir)) { + bool add_node; + if (BiasSelector::useBias(es_man, es_man.getObj(c))) { + add_node = getBiasedRandomBool(); + } else { + add_node = getRandomBool(); + } + if (add_node) { + processCandidateNode(c, scir, candidates); + } else { + rejected_nodes.push(c); + } + } + } + if (this->cfg.fill_subcircuits) { + while (!rejected_nodes.empty() && scir.size() < cfg.subcircuit_max_size) { + int c = rejected_nodes.front(); + rejected_nodes.pop(); + if (CandidateNodeConstraint::check(c, es_man, scir)) { + processCandidateNode(c, scir, rejected_nodes); + } + } + } + return scir; + } + + template + void RandomizedBFSSelection::processCandidateNode(int node, std::set& subcircuit, std::queue& candidates) { + subcircuit.insert(node); + auto& obj = es_man.getObj(node); + for (auto it = search_direction.getNextBegin(obj); it != search_direction.getNextEnd(obj); it++) { + const auto& f = *it; + if (search_direction.check(*f)) { + candidates.push(f->node_id); + } + } + } + + template + Subcircuit RandomizedBFSSelection::getSubcircuit(bool& status) { + std::set scir; + status = false; + for (int i = 1; i < cfg.nselection_trials; i++) { + scir = computeSubcircuitCandidate(); + if (scir.size() < min_size) { + scir.clear(); + continue; + } + es_man.incrementTraversalId(); + Subcircuit candidate = Subcircuit::getSubcircuit(es_man, scir); + if (validator.check(candidate)) { + status = true; + return candidate; + } + } + return Subcircuit::getEmptySubcircuit(); + } + + +} +ABC_NAMESPACE_CXX_HEADER_END \ No newline at end of file diff --git a/src/opt/eslim/selectionStrategy.hpp b/src/opt/eslim/selectionStrategy.hpp deleted file mode 100644 index 29650a5d78..0000000000 --- a/src/opt/eslim/selectionStrategy.hpp +++ /dev/null @@ -1,154 +0,0 @@ -/**CFile**************************************************************** - - FileName [selectionStrategy.hpp] - - SystemName [ABC: Logic synthesis and verification system.] - - PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - - Synopsis [Procedures for selecting subcircuits.] - - Author [Franz-Xaver Reichl] - - Affiliation [University of Freiburg] - - Date [Ver. 1.0. Started - March 2025.] - - Revision [$Id: selectionStrategy.hpp,v 1.00 2025/03/17 00:00:00 Exp $] - -***********************************************************************/ - -#ifndef ABC__OPT__ESLIM__SELECTIONSTRATEGY_h -#define ABC__OPT__ESLIM__SELECTIONSTRATEGY_h - -#include -#include - -#include "misc/util/abc_namespaces.h" -#include "misc/vec/vec.h" -#include "aig/gia/gia.h" - -#include "utils.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace eSLIM { - - template - class SelectionStrategy { - public: - Subcircuit getSubcircuit(); - bool getStatus() {return status;}; - void setSeed(int seed); - - protected: - SelectionStrategy(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log); - unsigned int getUniformRandomNumber(unsigned int lower, unsigned int upper); // requires lower < upper - bool getRandomBool(); - int getSubcircuitIO(Vec_Int_t* subcircuit, Vec_Int_t* io); - // The resulting map assigns inputs of the subcircuit to outputs of the subcircuit s.t. each associated input is reachable from the output - std::unordered_map> computeForbiddenPairs(const Subcircuit& subcir); - - Gia_Man_t*& gia_man; - const eSLIMConfig& cfg; - eSLIMLog& log; - - private: - - bool filterSubcircuit(const Subcircuit& subcir); - void forbiddenPairsRec(Gia_Obj_t * pObj, int input, int min_level, std::unordered_map>& pairs, const std::unordered_map& out_ids ); - - bool status = true; //set to false if selection fails too often - static constexpr unsigned int min_size = 2; - - std::mt19937 rng; - std::bernoulli_distribution bdist; - std::uniform_int_distribution udistr; - - }; - - template - class randomizedBFS : public SelectionStrategy { - private: - randomizedBFS(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log); - Subcircuit findSubcircuit(); - int selectRoot(); - void checkCandidate(Subcircuit& subcircuit, std::queue& candidate, std::queue& rejected, bool add); - const int nPis; - const int nPos; - friend SelectionStrategy; - friend T; - }; - - class randomizedBFSFP : public randomizedBFS { - public : - randomizedBFSFP(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log); - private: - Subcircuit getSubcircuitImpl(); - bool filterSubcircuitImpl(const Subcircuit& subcir); - - friend SelectionStrategy; - }; - - class randomizedBFSnoFP : public randomizedBFS { - public: - randomizedBFSnoFP(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log); - private: - Subcircuit getSubcircuitImpl() {return findSubcircuit();}; - bool filterSubcircuitImpl(const Subcircuit& subcir); - bool filterSubcircuitRec(Gia_Obj_t * pObj, unsigned int min_level); - - friend SelectionStrategy; - }; - - - template - SelectionStrategy::SelectionStrategy(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log) - : gia_man(gia_man), cfg(cfg), log(log), rng(std::random_device()()), bdist(cfg.expansion_probability) { - } - - template - void SelectionStrategy::setSeed(int seed) { - rng.seed(seed); - } - - template - unsigned int SelectionStrategy::getUniformRandomNumber(unsigned int lower, unsigned int upper) { - udistr.param(std::uniform_int_distribution::param_type(lower, upper)); - return udistr(rng); - } - - template - bool SelectionStrategy::getRandomBool() { - return bdist(rng); - } - - template - randomizedBFS::randomizedBFS(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log) - : SelectionStrategy(gia_man, cfg, log), nPis(Gia_ManPiNum(gia_man)), nPos(Gia_ManPoNum(gia_man)) { - } - - inline randomizedBFSFP::randomizedBFSFP(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log) - : randomizedBFS(gia_man, cfg, log){ - } - - inline Subcircuit randomizedBFSFP::getSubcircuitImpl() { - Subcircuit subcir = findSubcircuit(); - subcir.forbidden_pairs = computeForbiddenPairs(subcir); - return subcir; - } - - inline randomizedBFSnoFP::randomizedBFSnoFP(Gia_Man_t*& gia_man, const eSLIMConfig& cfg, eSLIMLog& log) - : randomizedBFS(gia_man, cfg, log) { - } - - inline bool randomizedBFSFP::filterSubcircuitImpl(const Subcircuit& subcir) { - return true; - } -} - -ABC_NAMESPACE_CXX_HEADER_END - -#include "selectionStrategy.tpp" - -#endif \ No newline at end of file diff --git a/src/opt/eslim/selectionStrategy.tpp b/src/opt/eslim/selectionStrategy.tpp deleted file mode 100644 index 94f656a5b3..0000000000 --- a/src/opt/eslim/selectionStrategy.tpp +++ /dev/null @@ -1,233 +0,0 @@ -/**CFile**************************************************************** - - FileName [selectionStrategy.tpp] - - SystemName [ABC: Logic synthesis and verification system.] - - PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - - Synopsis [Procedures for selecting subcircuits.] - - Author [Franz-Xaver Reichl] - - Affiliation [University of Freiburg] - - Date [Ver. 1.0. Started - March 2025.] - - Revision [$Id: selectionStrategy.tpp,v 1.00 2025/03/17 00:00:00 Exp $] - -***********************************************************************/ - -#include - -#include "misc/util/abc_namespaces.h" - -#include "selectionStrategy.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -namespace eSLIM { - - template - Subcircuit SelectionStrategy::getSubcircuit() { - for (int i = 0; i < cfg.nselection_trials; i++) { - Subcircuit subcir = static_cast(this)->getSubcircuitImpl(); - if (filterSubcircuit(subcir)) { - return subcir; - } else { - subcir.free(); - } - } - status = false; - Subcircuit empty_subcir; - return empty_subcir; - } - - template - bool SelectionStrategy::filterSubcircuit(const Subcircuit& subcir) { - // if (subcir.nof_inputs > 8) { - if (subcir.nof_inputs > 10) { - return false; - } - // ABC internally requires that the subcircuit has not more than 6 outputs (e.g. generateMinterm) - if (Vec_IntSize(subcir.io)-subcir.nof_inputs > 6) { - return false; - } - if (Vec_IntSize(subcir.nodes) < min_size) { - return false; - } - return static_cast(this)->filterSubcircuitImpl(subcir); - } - - template - int SelectionStrategy::getSubcircuitIO(Vec_Int_t* subcircuit, Vec_Int_t* io) { - assert(Vec_IntSize(io) == 0); - Gia_ManIncrementTravId(gia_man); - Gia_Obj_t * pObj; - int i; - Gia_ManForEachObjVec( subcircuit, gia_man, pObj, i ) { - Gia_ObjSetTravIdCurrent(gia_man, pObj); - } - Gia_ManIncrementTravId(gia_man); - - // inputs - Gia_ManForEachObjVec( subcircuit, gia_man, pObj, i ) { - // fanin0 is not in the subcircuit and was not considered yet - if (!Gia_ObjIsTravIdPrevious(gia_man, Gia_ObjFanin0(pObj)) && !Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin0(pObj))) { - Gia_ObjSetTravIdCurrent(gia_man, Gia_ObjFanin0(pObj)); - Vec_IntPush(io, Gia_ObjId(gia_man, Gia_ObjFanin0(pObj))); - } - // fanin1 is not in the subcircuit and was not considered yet - if (!Gia_ObjIsTravIdPrevious(gia_man, Gia_ObjFanin1(pObj)) && !Gia_ObjIsTravIdCurrent(gia_man, Gia_ObjFanin1(pObj))) { - Gia_ObjSetTravIdCurrent(gia_man, Gia_ObjFanin1(pObj)); - Vec_IntPush(io, Gia_ObjId(gia_man, Gia_ObjFanin1(pObj))); - } - } - int nof_inputs = Vec_IntSize(io); - - // outputs - Gia_ManForEachAnd( gia_man, pObj, i ) { - // If there is an object that is not contained in the subcircuit that has a fanin in the subcircuit then this fanin is an output - if (!Gia_ObjIsTravIdPrevious(gia_man, pObj)) { - if (Gia_ObjIsTravIdPrevious(gia_man, Gia_ObjFanin0(pObj))) { - Gia_ObjSetTravIdCurrent(gia_man, Gia_ObjFanin0(pObj)); - Vec_IntPush(io, Gia_ObjId(gia_man, Gia_ObjFanin0(pObj))); - } - if (Gia_ObjIsTravIdPrevious(gia_man, Gia_ObjFanin1(pObj))) { - Gia_ObjSetTravIdCurrent(gia_man, Gia_ObjFanin1(pObj)); - Vec_IntPush(io, Gia_ObjId(gia_man, Gia_ObjFanin1(pObj))); - } - } - } - - Gia_ManForEachPo( gia_man, pObj, i ) { - if (Gia_ObjIsTravIdPrevious(gia_man, Gia_ObjFanin0(pObj))) { - Gia_ObjSetTravIdCurrent(gia_man, Gia_ObjFanin0(pObj)); - Vec_IntPush(io, Gia_ObjId(gia_man, Gia_ObjFanin0(pObj))); - } - } - return nof_inputs; - } - - template - std::unordered_map> SelectionStrategy::computeForbiddenPairs(const Subcircuit& subcir) { - Gia_Obj_t * pObj; - int i; - unsigned int min_output_level = Gia_ManLevelNum(gia_man); - Gia_ManIncrementTravId(gia_man); - std::unordered_map out_id; - Gia_ManForEachObjVecStart(subcir.io, gia_man, pObj, i, subcir.nof_inputs) { - min_output_level = Gia_ObjLevel(gia_man, pObj) < min_output_level ? Gia_ObjLevel(gia_man, pObj) : min_output_level; - Gia_ObjSetTravIdCurrent(gia_man, pObj); - auto id = Gia_ObjId(gia_man, pObj); - out_id[id] = i - subcir.nof_inputs; - } - std::unordered_map> forbidden_pairs; - Gia_ManForEachObjVecStop(subcir.io, gia_man, pObj, i, subcir.nof_inputs) { - forbiddenPairsRec(pObj, i, min_output_level, forbidden_pairs, out_id); - } - return forbidden_pairs; - } - - template - void SelectionStrategy::forbiddenPairsRec(Gia_Obj_t * pObj, int input, int min_level, std::unordered_map>& pairs, const std::unordered_map& out_id) { - if (Gia_ObjIsTravIdCurrent(gia_man, pObj)) { - auto id = Gia_ObjId(gia_man, pObj); - pairs[out_id.at(id)].insert(input); - return; - } - if (Gia_ObjLevel(gia_man, pObj) < min_level) { - return; - } - forbiddenPairsRec(Gia_ObjFanin0(pObj), input, min_level, pairs, out_id); - forbiddenPairsRec(Gia_ObjFanin1(pObj), input, min_level, pairs, out_id); - } - - template - int randomizedBFS::selectRoot() { - int nof_objs = Gia_ManObjNum(this->gia_man); - int root_id = this->getUniformRandomNumber(this->nPis + 1, nof_objs - this->nPos - 1); - return root_id; - } - - template - void randomizedBFS::checkCandidate(Subcircuit& subcircuit, std::queue& candidates, std::queue& rejected, bool add) { - int obj_id = candidates.front(); - candidates.pop(); - Gia_Obj_t * gia_obj = Gia_ManObj(this->gia_man, obj_id); - if (Gia_ObjIsAnd(gia_obj) && !Gia_ObjIsTravIdCurrent(this->gia_man, gia_obj)) { - if (add) { - Gia_ObjSetTravIdCurrent(this->gia_man, gia_obj); - Vec_IntPush(subcircuit.nodes, obj_id); - candidates.push(Gia_ObjFaninId0(gia_obj, obj_id)); - candidates.push(Gia_ObjFaninId1(gia_obj, obj_id)); - } else { - rejected.push(obj_id); - } - } - } - - template - Subcircuit randomizedBFS::findSubcircuit() { - Subcircuit result; - assert (Gia_ManIsNormalized(this->gia_man)); - int root_id = selectRoot(); - Gia_Obj_t * gia_obj = Gia_ManObj(this->gia_man, root_id); - assert (Gia_ObjIsAnd(gia_obj)); - result.nodes = Vec_IntAlloc(this->cfg.subcircuit_size_bound); - result.io = Vec_IntAlloc(this->cfg.subcircuit_size_bound); - - Vec_IntPush(result.nodes, root_id); - - std::queue candidates; - candidates.push(Gia_ObjFaninId0(gia_obj, root_id)); - candidates.push(Gia_ObjFaninId1(gia_obj, root_id)); - std::queue rejected_nodes; - - Gia_ManIncrementTravId(this->gia_man); - - while (!candidates.empty() && Vec_IntSize(result.nodes) < this->cfg.subcircuit_size_bound) { - checkCandidate(result, candidates, rejected_nodes, this->getRandomBool()); - } - - if (this->cfg.fill_subcircuits) { - while (!rejected_nodes.empty() && Vec_IntSize(result.nodes) < this->cfg.subcircuit_size_bound) { - checkCandidate(result, rejected_nodes, rejected_nodes, true); - } - } - result.nof_inputs = this->getSubcircuitIO(result.nodes, result.io); - return result; - } - - inline bool randomizedBFSnoFP::filterSubcircuitImpl(const Subcircuit& subcir) { - Gia_Obj_t * pObj; - int i; - unsigned int min_output_level = Gia_ManLevelNum(gia_man); - Gia_ManIncrementTravId(gia_man); - Gia_ManForEachObjVecStart(subcir.io, gia_man, pObj, i, subcir.nof_inputs) { - min_output_level = Gia_ObjLevel(gia_man, pObj) < min_output_level ? Gia_ObjLevel(gia_man, pObj) : min_output_level; - Gia_ObjSetTravIdCurrent(gia_man, pObj); - } - Gia_ManIncrementTravId(gia_man); - Gia_ManForEachObjVecStop(subcir.io, gia_man, pObj, i, subcir.nof_inputs) { - if (!filterSubcircuitRec(pObj, min_output_level)) { - return false; - } - } - return true; - } - - inline bool randomizedBFSnoFP::filterSubcircuitRec(Gia_Obj_t * pObj, unsigned int min_level) { - if (Gia_ObjIsTravIdPrevious(gia_man, pObj)) { - return false; - } else if (Gia_ObjIsTravIdCurrent(gia_man, pObj)) { - return true; - } else if (Gia_ObjLevel(gia_man, pObj) < min_level) { - return true; - } - Gia_ObjSetTravIdCurrent(gia_man, pObj); - return filterSubcircuitRec(Gia_ObjFanin0(pObj), min_level) && filterSubcircuitRec(Gia_ObjFanin1(pObj), min_level); - } -} - -ABC_NAMESPACE_CXX_HEADER_END \ No newline at end of file diff --git a/src/opt/eslim/subcircuit.cpp b/src/opt/eslim/subcircuit.cpp new file mode 100644 index 0000000000..83f639681c --- /dev/null +++ b/src/opt/eslim/subcircuit.cpp @@ -0,0 +1,132 @@ +/**CFile**************************************************************** + + FileName [subcircuit.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Subcircuit representation] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#include +#include +#include + +#include "subcircuit.hpp" + + +ABC_NAMESPACE_IMPL_START +namespace eSLIM { + + Subcircuit Subcircuit::getSubcircuit(eSLIMCirMan& es_man, const std::set& nodes) { + Subcircuit scir = getSubcircuitBare(es_man, nodes.begin(), nodes.end()); + es_man.markCones(scir.nodes); + scir.setForbiddenPairs(es_man); + return scir; + } + + Subcircuit Subcircuit::getEmptySubcircuit() { + Subcircuit scir; + return scir; + } + + + + void Subcircuit::setIO(eSLIMCirMan& es_man) { + std::set input_set; + std::set output_set; + for (int n : nodes) { + const auto& obj = es_man.getObj(n); + for (const auto& f : obj.fanins) { + if (!es_man.inSubcircuit(*f) && !es_man.isConst(*f)) { + input_set.insert(f->node_id); + } + } + for (const auto& f : obj.fanouts) { + if (!es_man.inSubcircuit(*f)) { + output_set.insert(obj.node_id); + } + } + } + inputs.insert(inputs.end(), input_set.begin(), input_set.end()); + outputs.insert(outputs.end(), output_set.begin(), output_set.end()); + } + + void Subcircuit::setForbiddenPairs(const eSLIMCirMan& es_man) { + std::unordered_set seen; + for (int inp : inputs) { + getForbiddenPairsRec(inp, seen, es_man.getObj(inp), es_man); + seen.clear(); + } + } + + void Subcircuit::getForbiddenPairsRec(int inp, std::unordered_set& seen, const eSLIMCirObj& obj, const eSLIMCirMan& es_man) { + if (seen.find(obj.node_id) != seen.end()) { + return; + } + seen.insert(obj.node_id); + for (const auto& f : obj.fanins) { + if (es_man.inSubcircuit(*f)) { + forbidden_pairs[f->node_id].emplace(inp); + } else if (es_man.inTFI(*f) && es_man.inTFO(*f)) { + getForbiddenPairsRec(inp, seen, *f, es_man); + } + } + } + + int Subcircuit::setupTimings(eSLIMCirMan& es_man) { + // es_man.setupTimings(); + for (int in : inputs) { + arrival_times.push_back(es_man.getObj(in).depth); + } + int max_crossing_path = 0; + for (int out : outputs) { + remaining_times.push_back(es_man.getObj(out).remaining_time); + max_crossing_path = std::max(max_crossing_path, es_man.getObj(out).remaining_time + es_man.getObj(out).depth); + } + return max_crossing_path; + } + + void Subcircuit::print() const { + std::cout << "Inputs:"; + for (int i : inputs) { + std::cout << " " << i; + } + std::cout << "\nOutputs:"; + for (int o : outputs) { + std::cout << " " << o; + } + std::cout << "\nArrival times:"; + for (int ar : arrival_times) { + std::cout << " " << ar; + } + std::cout << "\nRemaining times:"; + for (int re : remaining_times) { + std::cout << " " << re; + } + std::cout << "\nNodes:"; + for (int n : nodes) { + std::cout << " " << n; + } + std::cout << "\nForbidden pairs:"; + for (const auto & [out,inps] : forbidden_pairs) { + std::cout << "\n" << out << " :"; + for (int inp : inps) { + std::cout << " " << inp; + } + } + std::cout << "\n"; + } + +} +ABC_NAMESPACE_IMPL_END \ No newline at end of file diff --git a/src/opt/eslim/subcircuit.hpp b/src/opt/eslim/subcircuit.hpp new file mode 100644 index 0000000000..e22640c2a8 --- /dev/null +++ b/src/opt/eslim/subcircuit.hpp @@ -0,0 +1,80 @@ +/**CFile**************************************************************** + + FileName [subcircuit.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Subcircuit representation] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__SUBCIRCUIT_hpp +#define ABC__OPT__ESLIM__SUBCIRCUIT_hpp + +#include +#include +#include +#include + +#include "misc/util/abc_namespaces.h" + +#include "eslimCirMan.hpp" + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class Subcircuit { + + public: + template + static Subcircuit getSubcircuitBare(eSLIMCirMan& es_man, It begin, It end); + static Subcircuit getSubcircuit(eSLIMCirMan& es_man, const std::set& nodes); + static Subcircuit getEmptySubcircuit(); + + int setupTimings(eSLIMCirMan& es_man); + + std::vector nodes; + std::vector inputs; + std::vector outputs; + // Assigns the inputs of the subcircuit that can be reached from an output x to x. + std::unordered_map> forbidden_pairs; + + std::vector arrival_times; + std::vector remaining_times; + + void print() const; + + private: + template + Subcircuit(eSLIMCirMan& es_man, It begin, It end); + Subcircuit() = default; + void setIO(eSLIMCirMan& es_man); + void setForbiddenPairs(const eSLIMCirMan& es_man); + void getForbiddenPairsRec(int inp, std::unordered_set& seen, const eSLIMCirObj& obj, const eSLIMCirMan& es_man); + }; + + template + Subcircuit::Subcircuit(eSLIMCirMan& es_man, It begin, It end) : nodes(begin, end) { + es_man.markSubcircuit(this->nodes); + setIO(es_man); + } + + template + Subcircuit Subcircuit::getSubcircuitBare(eSLIMCirMan& es_man, It begin, It end) { + Subcircuit scir(es_man, begin, end); + return scir; + } + +} +ABC_NAMESPACE_CXX_HEADER_END +#endif \ No newline at end of file diff --git a/src/opt/eslim/synthesisEngine.hpp b/src/opt/eslim/synthesisEngine.hpp deleted file mode 100644 index d061e761ba..0000000000 --- a/src/opt/eslim/synthesisEngine.hpp +++ /dev/null @@ -1,240 +0,0 @@ -/**CFile**************************************************************** - - FileName [synthesisEngine.hpp] - - SystemName [ABC: Logic synthesis and verification system.] - - PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - - Synopsis [Procedures for synthesising Boolean relations.] - - Author [Franz-Xaver Reichl] - - Affiliation [University of Freiburg] - - Date [Ver. 1.0. Started - March 2025.] - - Revision [$Id: synthesisEngine.hpp,v 1.00 2025/03/17 00:00:00 Exp $] - -***********************************************************************/ - -#ifndef ABC__OPT__ESLIM__SYNTHESISENGINE_h -#define ABC__OPT__ESLIM__SYNTHESISENGINE_h - -#include -#include -#include -#include - -#include "misc/util/abc_namespaces.h" -#include "aig/miniaig/miniaig.h" -#include "misc/vec/vec.h" - -#include "satInterfaces.hpp" - -ABC_NAMESPACE_CXX_HEADER_START - -#define MAJ_NOBJS 64 // Const0 + Const1 + nVars + nNodes - -namespace eSLIM { - - // Based on Exa6_ManGenStart - template - class exactSynthesisEngine { - - private: - Vec_Wrd_t * vSimsIn; // input signatures (nWords = 1, nIns <= 64) - Vec_Wrd_t * vSimsOut; // output signatures (nWords = 1, nOuts <= 6) - int nIns; - int nDivs; // divisors (const + inputs + tfi + sidedivs) - int nNodes; - int nOuts; - int nObjs; - int VarMarks[MAJ_NOBJS][2][MAJ_NOBJS] = {}; // default init the array - int nCnfVars; - int nCnfVars2; - int nCnfClauses; - - // Assign outputs to their connectivity variables - std::unordered_map> connection_variables; - const std::unordered_map>& forbidden_pairs; - eSLIMLog& log; - const eSLIMConfig& cfg; - - exactSynthesisEngine( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg); - int addClause(int lit1, int lit2, int lit3, int lit4); - static bool isNormalized( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut ); - int markup(); - int findFanin( Vec_Int_t * vValues, int i, int k, int nof_objs); - Mini_Aig_t * getAig(Vec_Int_t * assignment, int size); - Mini_Aig_t * getAig(Vec_Int_t * assignment); - void generateCNF(int fOrderNodes, int fUniqFans); - int startEncoding(int fOrderNodes, int fUniqFans ); - void generateMinterm( int iMint); - - int getGateEnablingLiteral(int index, bool negated); - int getAuxilaryVariableCount(); - - void introduceConnectionVariables(); - void setupConnectionVariables(); - void addConnectivityConstraints(); - void addCombinedCycleConstraints(); - std::unordered_map> computeNotInPair(const std::unordered_map>& pairs); - - int prepareLits(int * pLits, int& nLits); - - friend Derived; - }; - - // applies Cadical incrementally - class CadicalEngine : public exactSynthesisEngine { - - public: - CadicalEngine(Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg); - Mini_Aig_t* getCircuit(int size, double timeout); - - private: - using exactSynthesisEngine::addClause; - std::vector gate_enabling_assumptions; - CadicalSolver solver; - - - int addClause( int * pLits, int nLits ); - Vec_Int_t * solve(int size, double timeout); - int getAuxilaryVariableCountDerived(); - int getGateEnablingLiteralImpl(int index, bool negated); - void addAssumptions(int size); - void addGateDeactivatedConstraint(int out); - - std::vector getAssumptions(int size); - - friend exactSynthesisEngine; - }; - - class OneshotEngine { - protected: - int getAuxilaryVariableCountDerived() {return 0;}; - int getGateEnablingLiteralImpl(int index, bool negated) {return 0;}; - // void addAssumptions(int size) {}; - void addGateDeactivatedConstraint(int idx) {}; - }; - - template - class OneshotManager { - public: - OneshotManager(Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int maxnNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg); - Mini_Aig_t* getCircuit(int size, double timeout); - - private: - T getOneshotEngine(int size); - - Vec_Wrd_t * vSimsIn; // input signatures (nWords = 1, nIns <= 64) - Vec_Wrd_t * vSimsOut; // output signatures (nWords = 1, nOuts <= 6) - int nIns; - int nDivs; // divisors (const + inputs + tfi + sidedivs) - int maxnNodes; - int nOuts; - - const std::unordered_map>& forbidden_pairs; - eSLIMLog& log; - const eSLIMConfig& cfg; - }; - - class CadicalEngineOneShot : public exactSynthesisEngine, public OneshotEngine { - - public: - CadicalEngineOneShot( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg); - Mini_Aig_t* getCircuit(int size, double timeout); - - private: - using exactSynthesisEngine::addClause; - - int addClause( int * pLits, int nLits ); - Vec_Int_t * solve(int size, double timeout); - - CadicalSolver solver; - - friend exactSynthesisEngine; - }; - - class KissatEngineOneShot : public exactSynthesisEngine, public OneshotEngine { - - public: - KissatEngineOneShot( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg); - Mini_Aig_t* getCircuit(int size, double timeout); - - private: - using exactSynthesisEngine::addClause; - - int addClause( int * pLits, int nLits ); - Vec_Int_t * solve(int size, double timeout); - - KissatSolver solver; - - friend exactSynthesisEngine; - }; - - - class KissatCmdEngine : public exactSynthesisEngine, public OneshotEngine { - - public: - KissatCmdEngine( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg); - Mini_Aig_t* getCircuit(int size, double timeout); - - private: - using exactSynthesisEngine::addClause; - - - int addClause( int * pLits, int nLits ); - Vec_Int_t * solve(int size, double timeout); - - FILE * encoding_file; - - // Ensure that there is no file with the same name in the diretory - constexpr static char * pFileNameIn = "_temp_.cnf"; - constexpr static char * pFileNameOut = "_temp_out.cnf"; - - #ifdef WIN32 - constexpr static char * pKissat = "kissat.exe"; - #else - constexpr static char * pKissat = "kissat"; - #endif - - friend exactSynthesisEngine; - }; - - - template - inline int exactSynthesisEngine::addClause(int lit1, int lit2, int lit3, int lit4) { - int clause[4] = {lit1, lit2, lit3, lit4}; - return static_cast(this)->addClause(clause, 4); - } - - template - inline int exactSynthesisEngine::getGateEnablingLiteral(int index, bool negated) { - return static_cast(this)->getGateEnablingLiteralImpl(index, negated); - } - - template - inline int exactSynthesisEngine::getAuxilaryVariableCount() { - int ncvars = connection_variables.empty() ? 0 : connection_variables.begin()->second.size() * connection_variables.size(); - return ncvars + static_cast(this)->getAuxilaryVariableCountDerived(); - } - - typedef OneshotManager CadicalOneShot; - typedef OneshotManager KissatOneShot; - typedef OneshotManager KissatCmdOneShot; - -} - -ABC_NAMESPACE_CXX_HEADER_END - -#include "synthesisEngine.tpp" - -#endif \ No newline at end of file diff --git a/src/opt/eslim/synthesisEngine.tpp b/src/opt/eslim/synthesisEngine.tpp deleted file mode 100644 index ef94d92268..0000000000 --- a/src/opt/eslim/synthesisEngine.tpp +++ /dev/null @@ -1,632 +0,0 @@ -/**CFile**************************************************************** - - FileName [synthesisEngine.tpp] - - SystemName [ABC: Logic synthesis and verification system.] - - PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - - Synopsis [Procedures for synthesising Boolean relations.] - - Author [Franz-Xaver Reichl] - - Affiliation [University of Freiburg] - - Date [Ver. 1.0. Started - March 2025.] - - Revision [$Id: synthesisEngine.tpp,v 1.00 2025/03/17 00:00:00 Exp $] - -***********************************************************************/ - -#include -#ifdef WIN32 - #include - #define unlink _unlink -#else - #include -#endif - -#include "misc/util/utilTruth.h" - -#include "synthesisEngine.hpp" -#include "utils.hpp" - -ABC_NAMESPACE_HEADER_START - Vec_Int_t * Exa4_ManParse( char * pFileName ); -ABC_NAMESPACE_HEADER_END - -ABC_NAMESPACE_CXX_HEADER_START - -namespace eSLIM { - - template - exactSynthesisEngine::exactSynthesisEngine( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg) - : vSimsIn(vSimsIn), vSimsOut(vSimsOut), nIns(nIns), nDivs(nDivs), nNodes(nNodes), - nOuts(nOuts), forbidden_pairs(forbidden_pairs), log(log), cfg(cfg) { - assert( Vec_WrdSize(vSimsIn) == Vec_WrdSize(vSimsOut) ); - nObjs = nDivs + nNodes + nOuts; - nCnfVars = markup(); - introduceConnectionVariables(); - nCnfVars2 = 0; - nCnfClauses = 0; - assert( nObjs < 64 ); - } - - template - bool exactSynthesisEngine::isNormalized( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut ) { - int i, Count = 0; word Temp; - Vec_WrdForEachEntry( vSimsIn, Temp, i ) { - if ( Temp & 1 ) { - Count++; - } - } - if ( Count ) { - return false; - } - if ( !(Vec_WrdEntry(vSimsOut, 0) & 1) ) { - return false; - } - return true; - } - - template - int exactSynthesisEngine::markup() { - int i, k, j, nVars[3] = {1 + 3*nNodes, 0, 3*nNodes*Vec_WrdSize(vSimsIn)}; - assert( nObjs <= MAJ_NOBJS ); - for ( i = nDivs; i < nDivs + nNodes; i++ ) { - for ( k = 0; k < 2; k++ ) { - for ( j = 1+!k; j < i-k; j++ ) { - VarMarks[i][k][j] = nVars[0] + nVars[1]++; - } - } - } - for ( i = nDivs + nNodes; i < nObjs; i++ ) { - for ( j = 0; j < nDivs + nNodes; j++ ) { - VarMarks[i][0][j] = nVars[0] + nVars[1]++; - } - } - return nVars[0] + nVars[1] + nVars[2]; - } - - template - int exactSynthesisEngine::findFanin( Vec_Int_t * vValues, int i, int k, int nof_objs ) { - int j, Count = 0, iVar = -1; - for ( j = 0; j < nof_objs; j++ ) { - if ( VarMarks[i][k][j] && Vec_IntEntry(vValues, VarMarks[i][k][j]) ){ - iVar = j; - Count++; - } - } - assert( Count == 1 ); - return iVar; - } - - template - Mini_Aig_t * exactSynthesisEngine::getAig(Vec_Int_t * vValues) { - return getAig(vValues, nNodes); - } - - template - Mini_Aig_t * exactSynthesisEngine::getAig(Vec_Int_t * vValues, int size) { - assert (size >= 0 && size <= nNodes && "Invalid size for AIG construction given."); - int i, k, Compl[MAJ_NOBJS] = {0}; - int nof_Objs = nDivs + size + nOuts; - Mini_Aig_t * pMini = Mini_AigStartSupport( nDivs-1, nof_Objs ); - for ( i = nDivs; i < nDivs + size; i++ ) { - int iNodes[2] = {0}; - int iVarStart = 1 + 3*(i - nDivs);// - int Val1 = Vec_IntEntry(vValues, iVarStart); - int Val2 = Vec_IntEntry(vValues, iVarStart+1); - int Val3 = Vec_IntEntry(vValues, iVarStart+2); - Compl[i] = Val1 && Val2 && Val3; - for ( k = 0; k < 2; k++ ) { - int iNode = findFanin(vValues, i, !k, nof_Objs); - int fComp = k ? !Val1 && Val2 && !Val3 : Val1 && !Val2 && !Val3; - iNodes[k] = Abc_Var2Lit(iNode, fComp ^ Compl[iNode]); - } - if ( Val1 && Val2 ) { - if ( Val3 ) { - Mini_AigOr( pMini, iNodes[0], iNodes[1] ); - } else { - Mini_AigXorSpecial( pMini, iNodes[0], iNodes[1] ); - } - } else { - Mini_AigAnd( pMini, iNodes[0], iNodes[1] ); - } - } - for ( i = nDivs + nNodes; i < nObjs; i++ ) { - int iVar = findFanin( vValues, i, 0, nof_Objs); - Mini_AigCreatePo( pMini, Abc_Var2Lit(iVar, Compl[iVar]) ); - } - assert( nof_Objs == Mini_AigNodeNum(pMini) ); - return pMini; - } - - template - void exactSynthesisEngine::generateCNF( int fOrderNodes, int fUniqFans ) { - int m; - startEncoding( fOrderNodes, fUniqFans ); - // Ignore pattern for all false input pattern (as we start with m=1) - for ( m = 1; m < Vec_WrdSize(vSimsIn); m++ ) { - generateMinterm( m ); - } - - if (forbidden_pairs.size() > 0) { - addConnectivityConstraints(); - } - } - - template - void exactSynthesisEngine::introduceConnectionVariables() { - std::unordered_set inputs_in_pairs; - // for (const auto& [out, inputs] : forbidden_pairs) { - for (const auto& it: forbidden_pairs) { - const auto& inputs = it.second; - inputs_in_pairs.insert(inputs.begin(), inputs.end()); - } - int nCVars = nObjs - 1; // The constant node does not depend on any node - for (int in : inputs_in_pairs) { - connection_variables.emplace(in, std::vector (nCVars, 0)); - std::iota(connection_variables[in].begin(), connection_variables[in].end(), nCnfVars); - nCnfVars += nCVars; - } - } - - template - void exactSynthesisEngine::setupConnectionVariables() { - // for (const auto& [in, cvars] : connection_variables) { - for (const auto& it: connection_variables) { - const auto& in = it.first; - const auto& cvars = it.second; - addClause(Abc_Var2Lit(cvars[in],0),0,0,0); - for (int i = nDivs; i < nDivs + nNodes; i++ ) { - int gate_idx = i - nDivs; - for (int j = 1; j < i - 1; j++) { - addClause(Abc_Var2Lit(VarMarks[i][0][j + 1], 1), Abc_Var2Lit(cvars[j], 1), Abc_Var2Lit(cvars[i - 1], 0), getGateEnablingLiteral(gate_idx, 1)); - addClause(Abc_Var2Lit(VarMarks[i][1][j], 1), Abc_Var2Lit(cvars[j - 1], 1), Abc_Var2Lit(cvars[i - 1], 0), getGateEnablingLiteral(gate_idx, 1)); - } - } - for (int i = nDivs + nNodes; i < nObjs; i++) { - for (int j = 1; j < nDivs + nNodes; j++) { - addClause(Abc_Var2Lit(VarMarks[i][0][j], 1), Abc_Var2Lit(cvars[j - 1], 1), Abc_Var2Lit(cvars[i - 1], 0),0); - } - } - } - } - - template - void exactSynthesisEngine::addConnectivityConstraints() { - assert (forbidden_pairs.size() > 0 && "Connectivity constraints are only needed if there are forbidden pairs."); - setupConnectionVariables(); - // If there are forbidden pairs for more than one input it is possible that a loop is obtained via two pairs - if (forbidden_pairs.size() > 1) { - addCombinedCycleConstraints(); - } - - // for (const auto& [out, inputs] : forbidden_pairs) { - for (const auto& it: forbidden_pairs) { - const auto& out = it.first; - const auto& inputs = it.second; - for (int in : inputs) { - const auto& cvars = connection_variables.at(in); - int out_obj_idx = nDivs + nNodes + out; - addClause(Abc_Var2Lit(cvars[out_obj_idx - 1], 1), 0, 0, 0); - } - } - } - - template - void exactSynthesisEngine::addCombinedCycleConstraints() { - assert (forbidden_pairs.size() > 1 && "Combined connectivity constraints are only needed if there are forbidden pairs for multiple inputs."); - std::unordered_map> not_in_pair = computeNotInPair(forbidden_pairs); - - // for (const auto& [out, inputs] : not_in_pair) { - for (const auto& it: not_in_pair) { - const auto& out = it.first; - const auto& inputs = it.second; - for (int in : inputs) { - const auto& cvars = connection_variables.at(in); - int out_obj_idx = nDivs + nNodes + out; - for (int in2 : forbidden_pairs.at(out)) { - addClause(Abc_Var2Lit(cvars[out_obj_idx - 1], 1), Abc_Var2Lit(cvars[in2], 0),0,0); - } - } - } - } - - template - std::unordered_map> exactSynthesisEngine::computeNotInPair(const std::unordered_map>& pairs) { - std::unordered_set all_inputs_in_pairs; - // for (const auto& [out, in] : pairs) { - for (const auto& it: pairs) { - const auto& in = it.second; - all_inputs_in_pairs.insert(in.begin(), in.end()); - } - std::unordered_map> not_in_pair; - // for (const auto& [out, in] : pairs) { - for (const auto& it: pairs) { - const auto& out = it.first; - const auto& in = it.second; - if (in.size() == all_inputs_in_pairs.size()) { - continue; - } - for (int i : all_inputs_in_pairs) { - if (in.find(i) == in.end()) { - not_in_pair[out].insert(i); - } - } - } - return not_in_pair; - } - - template - int exactSynthesisEngine::startEncoding(int fOrderNodes, int fUniqFans ) - { - int pLits[2*MAJ_NOBJS], i, j, k, n, m, nLits; - for ( i = nDivs; i < nDivs + nNodes; i++ ) - { - int gate_idx = i - nDivs; - int iVarStart = 1 + 3*(i - nDivs);// - for ( k = 0; k < 2; k++ ) - { - nLits = 0; - for ( j = 0; j < nObjs; j++ ) - if ( VarMarks[i][k][j] ) - pLits[nLits++] = Abc_Var2Lit( VarMarks[i][k][j], 0 ); - assert( nLits > 0 ); - static_cast(this)->addClause( pLits, nLits ); - for ( n = 0; n < nLits; n++ ) - for ( m = n+1; m < nLits; m++ ) - addClause( Abc_LitNot(pLits[n]), Abc_LitNot(pLits[m]), getGateEnablingLiteral(gate_idx, 1), 0 ); - if ( k == 1 ) - break; - for ( j = 0; j < nObjs; j++ ) if ( VarMarks[i][0][j] ) - for ( n = j; n < nObjs; n++ ) if ( VarMarks[i][1][n] ) - addClause( Abc_Var2Lit(VarMarks[i][0][j], 1), Abc_Var2Lit(VarMarks[i][1][n], 1), getGateEnablingLiteral(gate_idx, 1), 0 ); - } - if ( fOrderNodes ) - for ( j = nDivs; j < i; j++ ) - for ( n = 0; n < nObjs; n++ ) if ( VarMarks[i][0][n] ) - for ( m = n+1; m < nObjs; m++ ) if ( VarMarks[j][0][m] ) - addClause( Abc_Var2Lit(VarMarks[i][0][n], 1), Abc_Var2Lit(VarMarks[j][0][m], 1), getGateEnablingLiteral(gate_idx, 1), 0 ); - for ( j = nDivs; j < i; j++ ) - for ( k = 0; k < 2; k++ ) if ( VarMarks[i][k][j] ) - for ( n = 0; n < nObjs; n++ ) if ( VarMarks[i][!k][n] ) - for ( m = 0; m < 2; m++ ) if ( VarMarks[j][m][n] ) - addClause( Abc_Var2Lit(VarMarks[i][k][j], 1), Abc_Var2Lit(VarMarks[i][!k][n], 1), Abc_Var2Lit(VarMarks[j][m][n], 1), getGateEnablingLiteral(gate_idx, 1) ); - for ( k = 0; k < 3; k++ ) - addClause( Abc_Var2Lit(iVarStart, k==1), Abc_Var2Lit(iVarStart+1, k==2), Abc_Var2Lit(iVarStart+2, k!=0), getGateEnablingLiteral(gate_idx, 1)); - if ( !cfg.allow_xors ) - addClause( Abc_Var2Lit(iVarStart, 1), Abc_Var2Lit(iVarStart+1, 1), Abc_Var2Lit(iVarStart+2, 0), getGateEnablingLiteral(gate_idx, 1)); - - } - for ( i = nDivs; i < nDivs + nNodes; i++ ) - { - int gate_idx = i - nDivs; - nLits = 0; - for ( k = 0; k < 2; k++ ) - for ( j = i+1; j < nObjs; j++ ) - if ( VarMarks[j][k][i] ) - pLits[nLits++] = Abc_Var2Lit( VarMarks[j][k][i], 0 ); - if ( fUniqFans ) - for ( n = 0; n < nLits; n++ ) - for ( m = n+1; m < nLits; m++ ) - addClause( Abc_LitNot(pLits[n]), Abc_LitNot(pLits[m]), getGateEnablingLiteral(gate_idx, 1), 0 ); - } - for ( i = nDivs + nNodes; i < nObjs; i++ ) - { - static_cast(this)->addGateDeactivatedConstraint(i); - nLits = 0; - for ( j = 0; j < nDivs + nNodes; j++ ) if ( VarMarks[i][0][j] ) { - pLits[nLits++] = Abc_Var2Lit( VarMarks[i][0][j], 0 ); - } - static_cast(this)->addClause( pLits, nLits ); - for ( n = 0; n < nLits; n++ ) - for ( m = n+1; m < nLits; m++ ) - addClause( Abc_LitNot(pLits[n]), Abc_LitNot(pLits[m]), 0, 0 ); - } - return 1; - } - - template - void exactSynthesisEngine::generateMinterm( int iMint) { - int internalCnfVars = nCnfVars - getAuxilaryVariableCount(); // for each gate we add an assumption - int iNodeVar = internalCnfVars + 3*nNodes*(iMint - Vec_WrdSize(vSimsIn)); - int iOutMint = Abc_Tt6FirstBit( Vec_WrdEntry(vSimsOut, iMint) ); - int fOnlyOne = Abc_TtSuppOnlyOne( (int)Vec_WrdEntry(vSimsOut, iMint) ); - int i, k, n, j, VarVals[MAJ_NOBJS]; - int fAllOnes = Abc_TtCountOnes( Vec_WrdEntry(vSimsOut, iMint) ) == (1 << nOuts); - if ( fAllOnes ) - return; - assert( nObjs <= MAJ_NOBJS ); - assert( iMint < Vec_WrdSize(vSimsIn) ); - assert( nOuts <= 6 ); - for ( i = 0; i < nDivs; i++ ) { - VarVals[i] = (Vec_WrdEntry(vSimsIn, iMint) >> i) & 1; - } - for ( i = 0; i < nNodes; i++ ) { - VarVals[nDivs + i] = Abc_Var2Lit(iNodeVar + 3*i + 2, 0); - } - if ( fOnlyOne ) { - for ( i = 0; i < nOuts; i++ ) - VarVals[nDivs + nNodes + i] = (iOutMint >> i) & 1; - } else { - word t = Abc_Tt6Stretch( Vec_WrdEntry(vSimsOut, iMint), nOuts ); - int i, c, nCubes = 0, pCover[100], pLits[10]; - int iOutVar = nCnfVars + nCnfVars2; nCnfVars2 += nOuts; - for ( i = 0; i < nOuts; i++ ) { - VarVals[nDivs + nNodes + i] = Abc_Var2Lit(iOutVar + i, 0); - } - assert( t ); - if ( ~t ) { - Abc_Tt6IsopCover( ~t, ~t, nOuts, pCover, &nCubes ); - for ( c = 0; c < nCubes; c++ ) { - int nLits = 0; - for ( i = 0; i < nOuts; i++ ) { - int iLit = (pCover[c] >> (2*i)) & 3; - if ( iLit == 1 || iLit == 2 ) - pLits[nLits++] = Abc_Var2Lit(iOutVar + i, iLit != 1); - } - static_cast(this)->addClause( pLits, nLits ); - } - } - } - - for ( i = nDivs; i < nDivs + nNodes; i++ ) { - int iVarStart = 1 + 3*(i - nDivs);// - int iBaseVarI = iNodeVar + 3*(i - nDivs); - for ( k = 0; k < 2; k++ ) { - for ( j = 0; j < i; j++ ) { - if ( VarMarks[i][k][j] ) { - for ( n = 0; n < 2; n++ ) { - addClause( Abc_Var2Lit(VarMarks[i][k][j], 1), Abc_Var2Lit(iBaseVarI + k, n), Abc_LitNotCond(VarVals[j], !n), 0); - } - } - } - } - for ( k = 0; k < 4; k++ ) { - for ( n = 0; n < 2; n++ ) { - addClause( Abc_Var2Lit(iBaseVarI + 0, k&1), Abc_Var2Lit(iBaseVarI + 1, k>>1), Abc_Var2Lit(iBaseVarI + 2, !n), Abc_Var2Lit(k ? iVarStart + k-1 : 0, n)); - } - } - } - for ( i = nDivs + nNodes; i < nObjs; i++ ) { - for ( j = 0; j < nDivs + nNodes; j++ ) { - if ( VarMarks[i][0][j] ) { - for ( n = 0; n < 2; n++ ) { - addClause( Abc_Var2Lit(VarMarks[i][0][j], 1), Abc_LitNotCond(VarVals[j], n), Abc_LitNotCond(VarVals[i], !n), 0); - } - } - } - } - } - - template - int exactSynthesisEngine::prepareLits(int * pLits, int& nLits) { - int k = 0; - for ( int i = 0; i < nLits; i++ ) { - if ( pLits[i] == 1 ) - return 0; - else if ( pLits[i] == 0 ) - continue; - else if ( pLits[i] <= 2*(nCnfVars + nCnfVars2) ) - pLits[k++] = pLits[i]; - else assert( 0 ); - } - nLits = k; - return 1; - } - - inline CadicalEngine::CadicalEngine(Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg) - : exactSynthesisEngine(vSimsIn, vSimsOut, nIns, nDivs, nOuts, nNodes, forbidden_pairs, log, cfg) { - gate_enabling_assumptions.reserve(nNodes); - for (int i = 0; i < nNodes; i++) { - gate_enabling_assumptions.push_back(nCnfVars + i); - } - nCnfVars += nNodes; - generateCNF(0, 0); - } - - inline int CadicalEngine::addClause( int * pLits, int nLits ) { - if (prepareLits(pLits, nLits) == 0) { - return 0; - } - assert( nLits > 0 ); - solver.addClause(pLits, nLits); - return 1; - } - - inline Vec_Int_t * CadicalEngine::solve( int size, double timeout) { - std::vector assumptions = getAssumptions(size); - solver.assume(assumptions); - int status = timeout > 0 ? solver.solve(timeout) : solver.solve(); - Vec_Int_t * vRes = status == 10 ? solver.getModelVec() : nullptr; - if (status == 10) { - log.cummulative_sat_runtimes_per_size[size] += solver.getRunTime(); - log.nof_sat_calls_per_size[size] ++; - } else if (status == 20) { - log.cummulative_unsat_runtimes_per_size[size] += solver.getRunTime(); - log.nof_unsat_calls_per_size[size] ++; - } - return vRes; - } - - Mini_Aig_t* CadicalEngine::getCircuit(int size, double timeout) { - addAssumptions(size); - Vec_Int_t * vValues = solve(size, timeout); - Mini_Aig_t * pMini = vValues ? getAig(vValues, size) : nullptr; - Vec_IntFreeP( &vValues ); - return pMini; - } - - inline std::vector CadicalEngine::getAssumptions(int size) { - std::vector assumptions(nNodes); - for (int i = 0; i < size; i++) { - assumptions[i] = gate_enabling_assumptions[i]; - } - for (int i = size; i < nNodes; i++) { - assumptions[i] = -gate_enabling_assumptions[i]; - } - return assumptions; - } - - inline void CadicalEngine::addGateDeactivatedConstraint(int out_idx) { - for (int j = nDivs; j < nDivs + nNodes; j++ ) { - int gate_idx = j - nDivs; - // if a gate is disabled then it shall not be an output - addClause( Abc_Var2Lit(VarMarks[out_idx][0][j],1), Abc_Var2Lit( gate_enabling_assumptions[gate_idx], 0 ), 0, 0 ); - } - } - - inline int CadicalEngine::getGateEnablingLiteralImpl(int index, bool negated) { - return Abc_Var2Lit( gate_enabling_assumptions[index], negated ); - } - - inline int CadicalEngine::getAuxilaryVariableCountDerived() { - return nNodes; // We add an assumption for each gate - } - - inline void CadicalEngine::addAssumptions(int size) { - solver.assume(getAssumptions(size)); - } - - template - OneshotManager::OneshotManager(Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int maxnNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg) - : vSimsIn(vSimsIn), vSimsOut(vSimsOut), nIns(nIns), nDivs(nDivs), maxnNodes(maxnNodes), nOuts(nOuts), forbidden_pairs(forbidden_pairs), log(log), cfg(cfg) { - } - - template - T OneshotManager::getOneshotEngine(int size) { - assert(size <= maxnNodes); - T oneshotEngine(vSimsIn, vSimsOut, nIns, nDivs, nOuts, size, forbidden_pairs, log, cfg); - return oneshotEngine; - } - - template - Mini_Aig_t* OneshotManager::getCircuit(int size, double timeout) { - T synth = getOneshotEngine(size); - return synth.getCircuit(size, timeout); - } - - inline CadicalEngineOneShot::CadicalEngineOneShot( Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg) - : exactSynthesisEngine(vSimsIn, vSimsOut, nIns, nDivs, nOuts, nNodes, forbidden_pairs, log, cfg) { - } - - inline Vec_Int_t* CadicalEngineOneShot::solve( int size, double timeout) { - int status = timeout > 0 ? solver.solve(timeout) : solver.solve(); - Vec_Int_t * vRes = status != 10 ? nullptr : solver.getModelVec(); - return vRes; - } - - Mini_Aig_t* CadicalEngineOneShot::getCircuit(int size, double timeout) { - generateCNF(0, 0); - Vec_Int_t * vValues = solve(size, timeout); - Mini_Aig_t * pMini = vValues ? getAig(vValues, size) : nullptr; - Vec_IntFreeP( &vValues ); - return pMini; - } - - inline int CadicalEngineOneShot::addClause( int * pLits, int nLits ) { - if (prepareLits(pLits, nLits) == 0) { - return 0; - } - assert( nLits > 0 ); - solver.addClause(pLits, nLits); - return 1; - } - - inline KissatEngineOneShot::KissatEngineOneShot(Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg) - : exactSynthesisEngine(vSimsIn, vSimsOut, nIns, nDivs, nOuts, nNodes, forbidden_pairs, log, cfg) { - } - - Mini_Aig_t* KissatEngineOneShot::getCircuit(int size, double timeout) { - solver.init(nCnfVars); - generateCNF(0, 0); - Vec_Int_t * vValues = solve(size, timeout); - Mini_Aig_t * pMini = vValues ? getAig(vValues, size) : nullptr; - Vec_IntFreeP( &vValues ); - return pMini; - } - - inline Vec_Int_t *KissatEngineOneShot::solve( int size, double timeout) { - // TODO: The used interface does not yet allow timeouts - int status = solver.solve(); - Vec_Int_t * vRes = status != 10 ? nullptr : solver.getModelVec(); - return vRes; - } - - inline int KissatEngineOneShot::addClause( int * pLits, int nLits ) { - if (prepareLits(pLits, nLits) == 0) { - return 0; - } - assert( nLits > 0 ); - solver.addClause(pLits, nLits); - return 1; - } - - inline KissatCmdEngine::KissatCmdEngine(Vec_Wrd_t * vSimsIn, Vec_Wrd_t * vSimsOut, int nIns, int nDivs, int nOuts, int nNodes, - const std::unordered_map>& forbidden_pairs, eSLIMLog& log, const eSLIMConfig& cfg) - : exactSynthesisEngine(vSimsIn, vSimsOut, nIns, nDivs, nOuts, nNodes, forbidden_pairs, log, cfg) { - } - - Mini_Aig_t* KissatCmdEngine::getCircuit(int size, double timeout) { - encoding_file = fopen( pFileNameIn, "wb" ); - fputs( "p cnf \n", encoding_file ); - generateCNF(0, 0); - Vec_Int_t * vValues = solve(size, timeout); - Mini_Aig_t * pMini = vValues ? getAig(vValues, size) : nullptr; - Vec_IntFreeP( &vValues ); - return pMini; - } - - inline int KissatCmdEngine::addClause( int * pLits, int nLits ) { - if (prepareLits(pLits, nLits) == 0) { - return 0; - } - assert( nLits > 0 ); - if ( encoding_file ) { - nCnfClauses++; - for ( int i = 0; i < nLits; i++ ) { - fprintf( encoding_file, "%s%d ", Abc_LitIsCompl(pLits[i]) ? "-" : "", Abc_Lit2Var(pLits[i]) ); - } - fprintf( encoding_file, "0\n" ); - } - return 1; - } - - inline Vec_Int_t * KissatCmdEngine::solve(int size, double timeout) { - rewind( encoding_file ); - fprintf( encoding_file, "p cnf %d %d", nCnfVars + nCnfVars2, nCnfClauses ); - fclose( encoding_file ); - encoding_file = nullptr; - - char Command[1000], * pCommand = (char *)&Command; - - // TODO: - // if ( TimeOut ) - // sprintf( pCommand, "%s --time=%d -q %s > %s", pKissat, TimeOut, pFileNameIn, pFileNameOut ); - // else - // sprintf( pCommand, "%s -q %s > %s", pKissat, pFileNameIn, pFileNameOut ); - - sprintf( pCommand, "%s -q %s > %s", pKissat, pFileNameIn, pFileNameOut ); -#ifdef __wasm - if ( 1 ) { -#else - if ( system( pCommand ) == -1 ) { -#endif - std::cerr << "Command " << pCommand << " failed\n"; - return nullptr; - } - Vec_Int_t * vRes = Exa4_ManParse( pFileNameOut ); - unlink( pFileNameIn ); - return vRes; - } - -} - -ABC_NAMESPACE_CXX_HEADER_END \ No newline at end of file diff --git a/src/opt/eslim/synthesisEngines.hpp b/src/opt/eslim/synthesisEngines.hpp new file mode 100644 index 0000000000..5f65139986 --- /dev/null +++ b/src/opt/eslim/synthesisEngines.hpp @@ -0,0 +1,115 @@ +/**CFile**************************************************************** + + FileName [synthesisEngines.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Synthesis engines] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__SYNTHESISENGINES_hpp +#define ABC__OPT__ESLIM__SYNTHESISENGINES_hpp + + +#include "misc/util/abc_global.h" + +#include "eslimCirMan.hpp" +#include "areaEngine.hpp" +#include "delayEngine.hpp" + + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class Relation; + class Subcircuit; + class eSLIMConfig; + class eSLIMLog; + + class AreaMinimizer { + public: + static eSLIMCirMan optimize(const eSLIMCirMan& encompassing, const Relation& relation, const Subcircuit& subcir, + const eSLIMConfig& cfg, eSLIMLog& log) { + return AreaEngine::synthesiseMinimumSizeCircuit(relation, subcir, cfg, log); + } + private: + AreaMinimizer() = default; + }; + + class AreaDelayConstraintMinimizer { + public: + static eSLIMCirMan optimize(eSLIMCirMan& encompassing, const Relation& relation, Subcircuit& subcir, + const eSLIMConfig& cfg, eSLIMLog& log) { + subcir.setupTimings(encompassing); + return DelayEngine::synthesizeMinimumAreaDelayConstraintCircuit(relation, subcir, encompassing.getDepth(), cfg, log); + } + private: + AreaDelayConstraintMinimizer() = default; + }; + + class AreaDelayMinimizer { + public: + static eSLIMCirMan optimize(eSLIMCirMan& encompassing, const Relation& relation, Subcircuit& subcir, + const eSLIMConfig& cfg, eSLIMLog& log) { + subcir.setupTimings(encompassing); + return DelayEngine::synthesizeMinimumAreaDelayCircuit(relation, subcir, cfg, log); + } + private: + AreaDelayMinimizer() = default; + }; + + class DelayMinimizer { + public: + static eSLIMCirMan optimize(eSLIMCirMan& encompassing, const Relation& relation, Subcircuit& subcir, + const eSLIMConfig& cfg, eSLIMLog& log) { + int max_crossing_path = subcir.setupTimings(encompassing); + unsigned int max_size = std::min(cfg.subcircuit_max_size, static_cast(subcir.nodes.size() + cfg.additional_gates)); + return DelayEngine::synthesizeMinimumDelayCircuit(relation, subcir, max_crossing_path, max_size, cfg, log); + } + private: + DelayMinimizer() = default; + }; + + class DelayAreaMinimizer { + public: + static eSLIMCirMan optimize(eSLIMCirMan& encompassing, const Relation& relation, Subcircuit& subcir, + const eSLIMConfig& cfg, eSLIMLog& log) { + int max_crossing_path = subcir.setupTimings(encompassing); + unsigned int max_size = std::min(cfg.subcircuit_max_size, static_cast(subcir.nodes.size() + cfg.additional_gates)); + return DelayEngine::synthesizeMinimumDelayAreaCircuit(relation, subcir, max_crossing_path, max_size, cfg, log); + } + private: + DelayAreaMinimizer() = default; + }; + + + template + struct isDelayEngine : std::integral_constant< bool, + std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + > {}; + + static_assert(!isDelayEngine::value); + static_assert(isDelayEngine::value); + static_assert(isDelayEngine::value); + static_assert(isDelayEngine::value); + static_assert(isDelayEngine::value); + + +} + +ABC_NAMESPACE_CXX_HEADER_END +#endif \ No newline at end of file diff --git a/src/opt/eslim/tabooList.hpp b/src/opt/eslim/tabooList.hpp new file mode 100644 index 0000000000..02f562e863 --- /dev/null +++ b/src/opt/eslim/tabooList.hpp @@ -0,0 +1,178 @@ +/**CFile**************************************************************** + + FileName [tabooList.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Taboo list for subcircuit selection.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__TABOOLIST_hpp +#define ABC__OPT__ESLIM__TABOOLIST_hpp + +#include +#include +#include + +#include "misc/util/abc_namespaces.h" + +#include "eslimCirMan.hpp" +#include "subcircuit.hpp" + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + class TabooList { + + public: + TabooList(const eSLIMCirMan& es_man); + int getRandomNonTaboo(std::mt19937& rng, double taboo_ratio, unsigned int taboo_time); + template + int getBiasedRandomNonTaboo(std::mt19937& rng, unsigned int bias, double taboo_ratio, unsigned int taboo_time); + void reset(); + + private: + struct eSLIMCirObjCompBase { + bool operator()(eSLIMCirObj* a, eSLIMCirObj* b) const { + return a->getId() < b->getId(); + } + }; + + struct eSLIMCirObjComp { + bool operator()(eSLIMCirObj* a, eSLIMCirObj* b) const { + return a->isTaboo < b->isTaboo || (a->isTaboo == b->isTaboo && eSLIMCirObjCompBase()(a,b)); + } + }; + + void updateTaboo(double taboo_ratio, unsigned int taboo_time); + void registerReplacement(eSLIMCirMan& replacement, const Subcircuit& subcir); + void discardSubcircuit(const Subcircuit& subcir); + void removeRedundantNode(eSLIMCirObj* nptr); + void fill(); + + void addNode(eSLIMCirObj* pobj); + void discardNode(eSLIMCirObj* pobj); + void addNonTaboo(eSLIMCirObj* pobj); + void removeNonTaboo(eSLIMCirObj* pobj); + + + const eSLIMCirMan& es_man; + std::set taboo_nodes; + std::set non_taboo_nodes; + + friend eSLIMCirMan; + }; + + inline TabooList::TabooList(const eSLIMCirMan& es_man) + : es_man(es_man) { + fill(); + } + + inline void TabooList::fill() { + for (int i = es_man.getNofPis() + 1; i < es_man.getNofObjs()-es_man.getNofPos(); i++) { + auto pObj = es_man.nodes[i].get(); + addNonTaboo(pObj); + } + } + + inline void TabooList::reset() { + taboo_nodes.clear(); + non_taboo_nodes.clear(); + fill(); + } + + inline void TabooList::updateTaboo(double taboo_ratio, unsigned int taboo_time) { + assert (taboo_ratio > 0 && taboo_ratio < 1); + auto it = taboo_nodes.begin(); + while (it != taboo_nodes.end()) { + if (!es_man.isTaboo(*(*it), taboo_time)) { + addNonTaboo(*it); + it = taboo_nodes.erase(it); + } else if (taboo_nodes.size() > static_cast(es_man.getNofGates()) * taboo_ratio) { + addNonTaboo(*it); + it = taboo_nodes.erase(it); + } else { + return; + } + } + } + + inline void TabooList::registerReplacement(eSLIMCirMan& replacement, const Subcircuit& subcir) { + discardSubcircuit(subcir); + for (int i = replacement.getNofPis() + 1; i < replacement.getNofObjs() - replacement.getNofPos(); i++) { + auto pObj = replacement.nodes[i].get(); + taboo_nodes.insert(pObj); + } + } + + inline void TabooList::discardSubcircuit(const Subcircuit& subcir) { + for (int nd : subcir.nodes) { + discardNode(es_man.nodes[nd].get()); + } + } + + inline void TabooList::removeRedundantNode(eSLIMCirObj* nptr) { + discardNode(nptr); + } + + + inline int TabooList::getRandomNonTaboo(std::mt19937& rng, double taboo_ratio, unsigned int taboo_time) { + updateTaboo(taboo_ratio, taboo_time); + assert(non_taboo_nodes.size() > 0); + std::uniform_int_distribution udistr(0, non_taboo_nodes.size() - 1); + int rv = udistr(rng); + auto it = non_taboo_nodes.begin(); + std::advance(it, rv); + return (*it)->node_id; + } + + template + int TabooList::getBiasedRandomNonTaboo(std::mt19937& rng, unsigned int bias, double taboo_ratio, unsigned int taboo_time) { + updateTaboo(taboo_ratio, taboo_time); + std::vector weights; + weights.reserve(non_taboo_nodes.size()); + for (const auto& pObj : non_taboo_nodes) { + if (BiasCondition::useBias(es_man, *pObj)) { + weights.push_back(bias); + } else { + weights.push_back(1); + } + } + std::discrete_distribution ddistr(weights.begin(), weights.end()); + int rv = ddistr(rng); + auto it = non_taboo_nodes.begin(); + std::advance(it, rv); + return (*it)->node_id; + } + + inline void TabooList::addNode(eSLIMCirObj* pobj) { + taboo_nodes.insert(pobj); + } + + inline void TabooList::discardNode(eSLIMCirObj* pobj) { + removeNonTaboo(pobj); + taboo_nodes.erase(pobj); + } + + inline void TabooList::addNonTaboo(eSLIMCirObj* pobj) { + non_taboo_nodes.insert(pobj); + } + + inline void TabooList::removeNonTaboo(eSLIMCirObj* pobj) { + non_taboo_nodes.erase(pobj); + } + +} +ABC_NAMESPACE_CXX_HEADER_END +#endif \ No newline at end of file diff --git a/src/opt/eslim/utils.hpp b/src/opt/eslim/utils.hpp index fb942c417b..b4beb33f07 100644 --- a/src/opt/eslim/utils.hpp +++ b/src/opt/eslim/utils.hpp @@ -6,48 +6,118 @@ PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] - Synopsis [Utilities for the eSLIM package.] + Synopsis [Utilities] Author [Franz-Xaver Reichl] Affiliation [University of Freiburg] - Date [Ver. 1.0. Started - March 2025.] + Date [Ver. 1.0. Started - April 2026.] - Revision [$Id: utils.hpp,v 1.00 2025/03/17 00:00:00 Exp $] + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] ***********************************************************************/ -#ifndef ABC__OPT__ESLIM__UTILS_h -#define ABC__OPT__ESLIM__UTILS_h +#ifndef ABC__OPT__ESLIM__UTILS_hpp +#define ABC__OPT__ESLIM__UTILS_hpp #include #include #include -#include "misc/util/abc_namespaces.h" -#include "misc/vec/vec.h" -#include "aig/gia/gia.h" +#include "misc/util/abc_global.h" + +#if __cplusplus >= 202002L + // C++20 (and later) code + #include +#endif + +#if __cplusplus >= 202002L + // // C++20 (and later) code + // #include + #define popcount(x) std::popcount(x) +#elif defined(__GNUC__) + #define popcount(x) __builtin_popcountll(x) +// #elif defined(_MSC_VER) +#elif defined(_WIN64) + #define popcount(x) __popcnt64(x) +#else + + inline int popcount64Impl(ABC_UINT64_T x) { + // Constants are 64-bit masks + const ABC_UINT64_T m1 = 0x5555555555555555; + const ABC_UINT64_T m2 = 0x3333333333333333; + const ABC_UINT64_T m3 = 0x0F0F0F0F0F0F0F0F; + const ABC_UINT64_T m4 = 0x0101010101010101; + x = x - ((x >> 1) & m1); + x = (x & m2) + ((x >> 2) & m2); + x = (x + (x >> 4)) & m3; + return (x * m4) >> 56; + } + #define popcount(x) popcount64Impl(x) -ABC_NAMESPACE_CXX_HEADER_START +#endif +ABC_NAMESPACE_CXX_HEADER_START namespace eSLIM { - struct eSLIMConfig { - bool extended_normality_processing = false; + inline int lsb(ABC_UINT64_T tt) { + #if __cplusplus >= 202002L + if (tt == 0) { + return -1; + } + return std::countr_zero(tt); + #elif defined(__GNUC__) + return __builtin_ffsl(tt) - 1; + // #elif defined(_MSC_VER) + #elif defined(_WIN64) + if (tt == 0) { + return -1; + } + int x = 0; + bool status = _BitScanForward64(x, tt); + return x; + #else + if (tt == 0) { + return -1; + } + ABC_UINT64_T lsb = tt & -tt; + const ABC_UINT64_T debruijn = 0x03f79d71b4cb0a89ULL; + ABC_UINT64_T scrambled = (lsb * debruijn) >> 58; + const int index64[64] = { + 0, 1, 48, 2, 57, 49, 28, 3, 61, 58, 50, 42, 29, 17, 4, 62, + 55, 59, 36, 53, 51, 43, 22, 30, 18, 12, 5, 63, 35, 56, 60, 54, + 37, 52, 44, 27, 23, 31, 19, 8, 13, 6, 64, 32, 47, 38, 45, 21, + 26, 24, 39, 16, 7, 64, 33, 46, 20, 25, 41, 15, 64, 34, 40, 64 + }; + return index64[scrambled]; + #endif + } + + struct eSLIMConfig { + bool aig = true; + int gate_size = 2; + bool apply_strash = true; bool fix_seed = false; bool fill_subcircuits = false; bool trial_limit_active = true; - bool allow_xors = false; + bool allow_xors = false; unsigned int timeout = 3600; unsigned int iterations = 0; - unsigned int subcircuit_size_bound = 6; + unsigned int subcircuit_max_size = 6; + unsigned int additional_gates = 0; unsigned int strash_intervall = 100; int seed = 0; - unsigned int nselection_trials = 100; - double expansion_probability = 0.6; + unsigned int nselection_trials = 120; + double expansion_probability = 0.5; + unsigned int bias = 2; + unsigned int nwindows = 1; + unsigned int window_size = 500; + + unsigned int taboo_time = 100; + double taboo_ratio = 0.5; // times given in sec int minimum_sat_timeout = 1; @@ -55,49 +125,73 @@ namespace eSLIM { int minimum_dynamic_timeout_sample_size = 50; double dynamic_timeout_buffer_factor = 1.4; + int relation_generation_timeout = 180; + bool approximate_relation = false; // Compute an overapproximation of the relation that may not use all don't cares by limiting the tfo of the subcircuit + bool generate_relation_with_tfi_limit = false; // If approximate_relation is true, also limit the tfi of the subcircuit + unsigned int relation_tfi_bound = 0; + unsigned int relation_tfo_bound = 0; + int verbosity_level = 0; }; - struct eSLIMLog { - unsigned int iteration_count = 0; - double relation_generation_time = 0; - double synthesis_time = 0; - unsigned int subcircuits_with_forbidden_pairs = 0; + class eSLIMLog { - std::vector nof_analyzed_circuits_per_size; - std::vector nof_replaced_circuits_per_size; - std::vector nof_reduced_circuits_per_size; + public: + unsigned int iteration_count = 0; + double relation_generation_time = 0; + double synthesis_time = 0; + unsigned int subcircuits_with_forbidden_pairs = 0; - std::vector cummulative_sat_runtimes_per_size; - std::vector nof_sat_calls_per_size; - std::vector cummulative_unsat_runtimes_per_size; - std::vector nof_unsat_calls_per_size; + std::vector nof_analyzed_circuits_per_size; + std::vector nof_replaced_circuits_per_size; + std::vector nof_reduced_circuits_per_size; - eSLIMLog(int size); - }; + std::vector cummulative_sat_runtimes_per_size; + std::vector nof_sat_calls_per_size; + std::vector cummulative_unsat_runtimes_per_size; + std::vector nof_unsat_calls_per_size; + std::vector cummulative_relation_generation_times_per_size; + std::vector nof_relation_generations_per_size; + + double cummulative_sat_runtime_synthesis = 0; // msec + double max_sat_runtime_synthesis = 0; // msec + ABC_UINT64_T nof_sat_calls_synthesis = 0; - struct Subcircuit { - Vec_Int_t* nodes; - Vec_Int_t* io; - unsigned int nof_inputs; - std::unordered_map> forbidden_pairs; - void free(); + double cummulative_sat_runtime_relation_generation = 0; // sec + double max_sat_runtime_relation_generation = 0; // sec + ABC_UINT64_T nof_sat_calls_relation_generation = 0; + + eSLIMLog(int size); + int getSize() const; + void resize(int size); }; - inline void Subcircuit::free() { - Vec_IntFree(nodes); - Vec_IntFree(io); - } inline eSLIMLog::eSLIMLog(int size) : nof_analyzed_circuits_per_size(size + 1, 0), nof_replaced_circuits_per_size(size + 1, 0), - nof_reduced_circuits_per_size(size + 1, 0), cummulative_sat_runtimes_per_size(size + 1, 0), - nof_sat_calls_per_size(size + 1, 0), cummulative_unsat_runtimes_per_size(size + 1, 0), - nof_unsat_calls_per_size(size + 1, 0) { + nof_reduced_circuits_per_size(size + 1, 0), + cummulative_sat_runtimes_per_size(size + 1, 0), nof_sat_calls_per_size(size + 1, 0), + cummulative_unsat_runtimes_per_size(size + 1, 0), nof_unsat_calls_per_size(size + 1, 0), + cummulative_relation_generation_times_per_size(size + 1, 0), nof_relation_generations_per_size(size + 1, 0) { } -} + inline int eSLIMLog::getSize() const { + return nof_analyzed_circuits_per_size.size() - 1; + } -ABC_NAMESPACE_CXX_HEADER_END + inline void eSLIMLog::resize(int size) { + int sz = size + 1; + nof_analyzed_circuits_per_size.resize(sz); + nof_replaced_circuits_per_size.resize(sz); + nof_reduced_circuits_per_size.resize(size); + cummulative_sat_runtimes_per_size.resize(sz); + nof_sat_calls_per_size.resize(sz); + cummulative_unsat_runtimes_per_size.resize(sz); + nof_unsat_calls_per_size.resize(sz); + cummulative_relation_generation_times_per_size.resize(sz); + nof_relation_generations_per_size.resize(sz); + } +} +ABC_NAMESPACE_CXX_HEADER_END #endif \ No newline at end of file diff --git a/src/opt/eslim/windowMan.hpp b/src/opt/eslim/windowMan.hpp new file mode 100644 index 0000000000..0ab59fb04e --- /dev/null +++ b/src/opt/eslim/windowMan.hpp @@ -0,0 +1,79 @@ +/**CFile**************************************************************** + + FileName [windowMan.hpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Basic implementation of a window manager.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#ifndef ABC__OPT__ESLIM__WINDOWMAN_hpp +#define ABC__OPT__ESLIM__WINDOWMAN_hpp + +#include + +#include "misc/util/abc_global.h" + +#include "eslimCirMan.hpp" +#include "utils.hpp" + + +ABC_NAMESPACE_CXX_HEADER_START +namespace eSLIM { + + // The parallelisation strategy implemented in this class is rather easy and mainly intended to demonstrate + // how the eslim package could make use of parallel computing + template + class WindowMan { + + public: + void static applyeSLIM(eSLIMCirMan& es_man, eSLIMConfig& cfg, eSLIMLog& log); + + private: + + WindowMan(eSLIMCirMan& es_man, const eSLIMConfig& cfg, eSLIMLog& log); + eSLIMCirMan getCircuit(); + + void setupWindows(); + void computeWindow(const std::vector& gate_ids, int node_begin, int node_end); + void reorderCircuit(); + + void runParallel(); + eSLIMCirMan recombineWindows(); + void addEncompassing(eSLIMCirMan& es_man, int node_begin, int node_end); + void addWindow(eSLIMCirMan& es_man, int wid); + + void combineLogs(); + + + int nwindows = 0; + eSLIMCirMan& es_man; + const eSLIMConfig& cfg; + eSLIMLog& log; + std::vector wlogs; + std::mt19937 rng; + + std::vector windows; + std::vector scirs; + std::vector node_ranges_begin; + std::vector node_ranges_end; + + }; + +} +ABC_NAMESPACE_CXX_HEADER_END + +#include "windowMan.tpp" + +#endif \ No newline at end of file diff --git a/src/opt/eslim/windowMan.tpp b/src/opt/eslim/windowMan.tpp new file mode 100644 index 0000000000..d09b264abe --- /dev/null +++ b/src/opt/eslim/windowMan.tpp @@ -0,0 +1,221 @@ +/**CFile**************************************************************** + + FileName [windowMan.tpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).] + + Synopsis [Basic implementation of a window manager.] + + Author [Franz-Xaver Reichl] + + Affiliation [University of Freiburg] + + Date [Ver. 1.0. Started - April 2026.] + + Revision [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $] + +***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ABC_USE_PTHREADS + #include +#endif + +#include "windowMan.hpp" +#include "subcircuit.hpp" + + +ABC_NAMESPACE_IMPL_START +namespace eSLIM { + + template + void WindowMan::applyeSLIM(eSLIMCirMan& es_man, eSLIMConfig& cfg, eSLIMLog& log) { + int v = cfg.verbosity_level; + cfg.verbosity_level = 0; + WindowMan win_man(es_man, cfg, log); + win_man.setupWindows(); + win_man.runParallel(); + cfg.verbosity_level = v; + // eSLIMCirMan combined = win_man.recombineWindows(); + // std::swap(es_man, combined); + es_man = win_man.recombineWindows(); + } + + template + WindowMan::WindowMan(eSLIMCirMan& es_man, const eSLIMConfig& cfg, eSLIMLog& log) + : es_man(es_man), cfg(cfg), log(log) { + if (cfg.fix_seed) { + rng.seed(cfg.seed); + } + } + + template + void WindowMan::setupWindows() { + reorderCircuit(); + unsigned int nmax_windows = std::ceil(static_cast(es_man.getNofGates()) / cfg.window_size); + nwindows = std::min(nmax_windows, cfg.nwindows); + std::vector gate_ids(es_man.getNofGates()); + std::iota(gate_ids.begin(), gate_ids.end(), es_man.getNofPis() + 1); + std::vector window_ids (nmax_windows); + std::iota(window_ids.begin(), window_ids.end(), 0); + std::vector selected_window_ids; + selected_window_ids.reserve(nwindows); + std::sample(window_ids.begin(), window_ids.end(), std::back_inserter(selected_window_ids), nwindows, rng); + for (int i = 0; i < nwindows; i++) { + int begin_idx = selected_window_ids[i]*cfg.window_size; + // int end_idx = selected_window_ids[i] == nmax_windows ? gate_ids.size() : selected_window_ids[i+1]*cfg.window_size; + // int end_idx = selected_window_ids[i] == nmax_windows - 1 ? gate_ids.size() : begin_idx + cfg.window_size; + int end_idx = begin_idx + cfg.window_size; + computeWindow(gate_ids, begin_idx, end_idx); + wlogs.emplace_back(log.getSize()); + } + } + + template + void WindowMan::reorderCircuit() { + std::vector pos; + pos.reserve(es_man.getNofPos()); + for (int i = es_man.getNofObjs() - es_man.getNofPos(); i < es_man.getNofObjs(); i++) { + pos.push_back(i); + } + std::shuffle(pos.begin(), pos.end(), rng); + es_man.applyDFSOrdering(pos); + } + + template + void WindowMan::computeWindow(const std::vector& gate_ids, int node_begin, int node_end) { + es_man.incrementTraversalId(); // needed for the insubcircuit marks + int end_node; + if (node_end >= gate_ids.size()) { + node_end = gate_ids.size(); + end_node = gate_ids.back() + 1; + } else { + end_node = gate_ids[node_end]; + } + Subcircuit scir = Subcircuit::getSubcircuitBare(es_man, gate_ids.begin() + node_begin, gate_ids.begin() + node_end); + scirs.push_back(scir); + windows.emplace_back(es_man, scir); + node_ranges_begin.push_back(gate_ids[node_begin]); + node_ranges_end.push_back(end_node); + } + + + template + void WindowMan::runParallel() { + #ifdef ABC_USE_PTHREADS + std::vector eslim_threads; + eslim_threads.reserve(nwindows); + for (int i = 0; i < nwindows; i++) { + std::thread esman_thread(eSLIM_Man::applyeSLIM, std::ref(windows[i]), std::ref(cfg), std::ref(wlogs[i])); + eslim_threads.push_back(std::move(esman_thread)); + + } + for(auto& t: eslim_threads) { + t.join(); + } + #else + std::cout << "PThreads not available, minimize random window.\n"; + std::uniform_int_distribution<> udist(0, windows.size() - 1); + int wid = udist(rng); + eSLIM_Man::applyeSLIM(windows[wid], cfg, wlogs[wid]) + #endif + } + + template + eSLIMCirMan WindowMan::recombineWindows() { + combineLogs(); + eSLIMCirMan combined(es_man.getNofObjs()); + for (int i = 1; i <= es_man.getNofPis(); i++) { + es_man.nodes[i]->value1 = combined.addPi(); //node 0 is the constant node + } + int block_begin = es_man.getNofPis() + 1; + for (int i = 0; i < nwindows; i++) { + // windows[i].print(); + int block_end = node_ranges_begin[i]; + addEncompassing(combined, block_begin, block_end); + addWindow(combined, i); + block_begin = node_ranges_end[i]; + } + if (node_ranges_end.back() < es_man.getNofObjs() - es_man.getNofPos()) { + addEncompassing(combined, node_ranges_end.back(), es_man.getNofObjs() - es_man.getNofPos()); + } + for (int i = 0; i < es_man.getNofPos(); i++) { + int nd_id = es_man.getNofObjs() - es_man.getNofPos() + i; + auto& nd = es_man.nodes[nd_id]; + bool is_negated = nd->tt == 1; + combined.addPo(nd->fanins[0]->value1, is_negated); + } + return combined; + } + + template + void WindowMan::addEncompassing(eSLIMCirMan& combined, int node_begin, int node_end) { + for (int i = node_begin; i < node_end; i++) { + auto& nd = es_man.nodes[i]; + std::vector fanins; + fanins.reserve(nd->fanins.size()); + for (int i = 0; i < nd->fanins.size(); i++) { + fanins.push_back(nd->fanins[i]->value1); + } + nd->value1 = combined.addNode(fanins, nd->tt); + } + } + + template + void WindowMan::addWindow(eSLIMCirMan& combined, int wid) { + for (int i = 1; i <= windows[wid].getNofPis(); i++) { + windows[wid].nodes[i]->value1 = es_man.nodes[scirs[wid].inputs[i-1]]->value1; + } + for (int i = windows[wid].getNofPis() + 1; i < windows[wid].getNofObjs() - windows[wid].getNofPos(); i++) { + auto& nd = windows[wid].nodes[i]; + std::vector fanins; + fanins.reserve(nd->fanins.size()); + for (int i = 0; i < nd->fanins.size(); i++) { + fanins.push_back(nd->fanins[i]->value1); + } + nd->value1 = combined.addNode(fanins, nd->tt); + } + for (int i = 0; i < windows[wid].getNofPos(); i++) { + auto& out_nd = windows[wid].nodes[windows[wid].getNofObjs() - windows[wid].getNofPos() + i]; + es_man.nodes[scirs[wid].outputs[i]]->value1 = out_nd->fanins[0]->value1; + } + } + + + template + void WindowMan::combineLogs() { + for (const auto& wl : wlogs) { + int wl_size = wl.getSize(); + if (log.getSize() < wl_size) { + log.resize(wl_size); + } + log.iteration_count += wl.iteration_count; + log.relation_generation_time += wl.relation_generation_time; + log.synthesis_time += wl.synthesis_time; + log.subcircuits_with_forbidden_pairs += wl.subcircuits_with_forbidden_pairs; + + for (int i = 0; i <= wl_size; i++) { + log.nof_analyzed_circuits_per_size[i] += wl.nof_analyzed_circuits_per_size[i]; + log.nof_replaced_circuits_per_size[i] += wl.nof_replaced_circuits_per_size[i]; + log.nof_reduced_circuits_per_size[i] += wl.nof_reduced_circuits_per_size[i]; + log.cummulative_sat_runtimes_per_size[i] += wl.cummulative_sat_runtimes_per_size[i]; + log.nof_sat_calls_per_size[i] += wl.nof_sat_calls_per_size[i]; + log.cummulative_unsat_runtimes_per_size[i] += wl.cummulative_unsat_runtimes_per_size[i]; + log.nof_unsat_calls_per_size[i] += wl.nof_unsat_calls_per_size[i]; + log.cummulative_relation_generation_times_per_size[i] += wl.cummulative_relation_generation_times_per_size[i]; + log.nof_relation_generations_per_size[i] += wl.nof_relation_generations_per_size[i]; + } + } + } + +} +ABC_NAMESPACE_IMPL_END \ No newline at end of file