mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	adding lns
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									64954cc551
								
							
						
					
					
						commit
						4375f54c45
					
				
					 7 changed files with 257 additions and 21 deletions
				
			
		|  | @ -5,6 +5,7 @@ z3_add_component(opt | ||||||
|     mss.cpp |     mss.cpp | ||||||
|     opt_cmds.cpp |     opt_cmds.cpp | ||||||
|     opt_context.cpp |     opt_context.cpp | ||||||
|  |     opt_lns.cpp | ||||||
|     opt_pareto.cpp |     opt_pareto.cpp | ||||||
|     opt_parse.cpp |     opt_parse.cpp | ||||||
|     optsmt.cpp |     optsmt.cpp | ||||||
|  |  | ||||||
|  | @ -145,9 +145,8 @@ namespace opt { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void context::reset_maxsmts() { |     void context::reset_maxsmts() { | ||||||
|         map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); |         for (auto& kv : m_maxsmts) { | ||||||
|         for (; it != end; ++it) { |             dealloc(kv.m_value); | ||||||
|             dealloc(it->m_value); |  | ||||||
|         } |         } | ||||||
|         m_maxsmts.reset(); |         m_maxsmts.reset(); | ||||||
|     } |     } | ||||||
|  | @ -255,6 +254,9 @@ namespace opt { | ||||||
|         if (m_pareto) { |         if (m_pareto) { | ||||||
|             return execute_pareto(); |             return execute_pareto(); | ||||||
|         } |         } | ||||||
|  |         if (m_lns) { | ||||||
|  |             return execute_lns(); | ||||||
|  |         } | ||||||
|         if (m_box_index != UINT_MAX) { |         if (m_box_index != UINT_MAX) { | ||||||
|             return execute_box(); |             return execute_box(); | ||||||
|         } |         } | ||||||
|  | @ -271,10 +273,16 @@ namespace opt { | ||||||
| #endif | #endif | ||||||
|         solver& s = get_solver(); |         solver& s = get_solver(); | ||||||
|         s.assert_expr(m_hard_constraints); |         s.assert_expr(m_hard_constraints); | ||||||
|         IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n";); |          | ||||||
|  |         opt_params optp(m_params); | ||||||
|  |         symbol pri = optp.priority(); | ||||||
|  |         if (pri == symbol("lns")) { | ||||||
|  |             return execute_lns(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n"); | ||||||
|         lbool is_sat = s.check_sat(0,0); |         lbool is_sat = s.check_sat(0,0); | ||||||
|         TRACE("opt", tout << "initial search result: " << is_sat << "\n"; |         TRACE("opt", s.display(tout << "initial search result: " << is_sat << "\n"););  | ||||||
|               s.display(tout);); |  | ||||||
|         if (is_sat != l_false) { |         if (is_sat != l_false) { | ||||||
|             s.get_model(m_model); |             s.get_model(m_model); | ||||||
|             s.get_labels(m_labels); |             s.get_labels(m_labels); | ||||||
|  | @ -286,7 +294,7 @@ namespace opt { | ||||||
|             TRACE("opt", tout << m_hard_constraints << "\n";);             |             TRACE("opt", tout << m_hard_constraints << "\n";);             | ||||||
|             return is_sat; |             return is_sat; | ||||||
|         } |         } | ||||||
|         IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); |         IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n"); | ||||||
|         TRACE("opt", model_smt2_pp(tout, m, *m_model, 0);); |         TRACE("opt", model_smt2_pp(tout, m, *m_model, 0);); | ||||||
|         m_optsmt.setup(*m_opt_solver.get()); |         m_optsmt.setup(*m_opt_solver.get()); | ||||||
|         update_lower(); |         update_lower(); | ||||||
|  | @ -303,6 +311,9 @@ namespace opt { | ||||||
|             if (pri == symbol("pareto")) { |             if (pri == symbol("pareto")) { | ||||||
|                 is_sat = execute_pareto(); |                 is_sat = execute_pareto(); | ||||||
|             } |             } | ||||||
|  |             else if (pri == symbol("lns")) { | ||||||
|  |                 is_sat = execute_lns(); | ||||||
|  |             } | ||||||
|             else if (pri == symbol("box")) { |             else if (pri == symbol("box")) { | ||||||
|                 is_sat = execute_box(); |                 is_sat = execute_box(); | ||||||
|             } |             } | ||||||
|  | @ -525,7 +536,12 @@ namespace opt { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void context::yield() { |     void context::yield() { | ||||||
|         m_pareto->get_model(m_model, m_labels); |         if (m_pareto) { | ||||||
|  |             m_pareto->get_model(m_model, m_labels); | ||||||
|  |         } | ||||||
|  |         else if (m_lns) { | ||||||
|  |             m_lns->get_model(m_model, m_labels); | ||||||
|  |         } | ||||||
|         update_bound(true); |         update_bound(true); | ||||||
|         update_bound(false); |         update_bound(false); | ||||||
|     } |     } | ||||||
|  | @ -536,7 +552,7 @@ namespace opt { | ||||||
|         } |         } | ||||||
|         lbool is_sat = (*(m_pareto.get()))(); |         lbool is_sat = (*(m_pareto.get()))(); | ||||||
|         if (is_sat != l_true) { |         if (is_sat != l_true) { | ||||||
|             set_pareto(0); |             set_pareto(nullptr); | ||||||
|         } |         } | ||||||
|         if (is_sat == l_true) { |         if (is_sat == l_true) { | ||||||
|             yield(); |             yield(); | ||||||
|  | @ -544,6 +560,20 @@ namespace opt { | ||||||
|         return is_sat; |         return is_sat; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     lbool context::execute_lns() { | ||||||
|  |         if (!m_lns) { | ||||||
|  |             m_lns = alloc(lns, *this, m_solver.get()); | ||||||
|  |         } | ||||||
|  |         lbool is_sat = (*(m_lns.get()))(); | ||||||
|  |         if (is_sat != l_true) { | ||||||
|  |             m_lns = nullptr; | ||||||
|  |         } | ||||||
|  |         if (is_sat == l_true) { | ||||||
|  |             yield(); | ||||||
|  |         } | ||||||
|  |         return l_undef; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     std::string context::reason_unknown() const {  |     std::string context::reason_unknown() const {  | ||||||
|         if (m.canceled()) { |         if (m.canceled()) { | ||||||
|             return Z3_CANCELED_MSG; |             return Z3_CANCELED_MSG; | ||||||
|  | @ -990,6 +1020,24 @@ namespace opt { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |        \brief retrieve literals used by the neighborhood search feature. | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     void context::get_lns_literals(expr_ref_vector& lits) { | ||||||
|  |         for (objective & obj : m_objectives) { | ||||||
|  |             switch(obj.m_type) { | ||||||
|  |             case O_MAXSMT:  | ||||||
|  |                 for (expr* f : obj.m_terms) { | ||||||
|  |                     lits.push_back(f); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     bool context::verify_model(unsigned index, model* md, rational const& _v) { |     bool context::verify_model(unsigned index, model* md, rational const& _v) { | ||||||
|         rational r; |         rational r; | ||||||
|         app_ref term = m_objectives[index].m_term; |         app_ref term = m_objectives[index].m_term; | ||||||
|  | @ -1352,7 +1400,8 @@ namespace opt { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void context::clear_state() { |     void context::clear_state() { | ||||||
|         set_pareto(0); |         m_pareto = nullptr; | ||||||
|  |         m_lns = nullptr; | ||||||
|         m_box_index = UINT_MAX; |         m_box_index = UINT_MAX; | ||||||
|         m_model.reset(); |         m_model.reset(); | ||||||
|     } |     } | ||||||
|  | @ -1388,9 +1437,8 @@ namespace opt { | ||||||
|             m_solver->updt_params(m_params); |             m_solver->updt_params(m_params); | ||||||
|         } |         } | ||||||
|         m_optsmt.updt_params(m_params); |         m_optsmt.updt_params(m_params); | ||||||
|         map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); |         for (auto & kv : m_maxsmts) { | ||||||
|         for (; it != end; ++it) { |             kv.m_value->updt_params(m_params); | ||||||
|             it->m_value->updt_params(m_params); |  | ||||||
|         } |         } | ||||||
|         opt_params _p(p); |         opt_params _p(p); | ||||||
|         m_enable_sat = _p.enable_sat(); |         m_enable_sat = _p.enable_sat(); | ||||||
|  |  | ||||||
|  | @ -19,16 +19,18 @@ Notes: | ||||||
| #define OPT_CONTEXT_H_ | #define OPT_CONTEXT_H_ | ||||||
| 
 | 
 | ||||||
| #include "ast/ast.h" | #include "ast/ast.h" | ||||||
|  | #include "ast/arith_decl_plugin.h" | ||||||
|  | #include "ast/bv_decl_plugin.h" | ||||||
|  | #include "tactic/model_converter.h" | ||||||
|  | #include "tactic/tactic.h" | ||||||
|  | #include "qe/qsat.h" | ||||||
| #include "opt/opt_solver.h" | #include "opt/opt_solver.h" | ||||||
| #include "opt/opt_pareto.h" | #include "opt/opt_pareto.h" | ||||||
| #include "opt/optsmt.h" | #include "opt/optsmt.h" | ||||||
|  | #include "opt/opt_lns.h" | ||||||
| #include "opt/maxsmt.h" | #include "opt/maxsmt.h" | ||||||
| #include "tactic/model_converter.h" |  | ||||||
| #include "tactic/tactic.h" |  | ||||||
| #include "ast/arith_decl_plugin.h" |  | ||||||
| #include "ast/bv_decl_plugin.h" |  | ||||||
| #include "cmd_context/cmd_context.h" | #include "cmd_context/cmd_context.h" | ||||||
| #include "qe/qsat.h" | 
 | ||||||
| 
 | 
 | ||||||
| namespace opt { | namespace opt { | ||||||
| 
 | 
 | ||||||
|  | @ -145,6 +147,7 @@ namespace opt { | ||||||
|         ref<solver>         m_solver; |         ref<solver>         m_solver; | ||||||
|         ref<solver>         m_sat_solver; |         ref<solver>         m_sat_solver; | ||||||
|         scoped_ptr<pareto_base>          m_pareto; |         scoped_ptr<pareto_base>          m_pareto; | ||||||
|  |         scoped_ptr<lns>      m_lns; | ||||||
|         scoped_ptr<qe::qmax> m_qmax; |         scoped_ptr<qe::qmax> m_qmax; | ||||||
|         sref_vector<model>  m_box_models; |         sref_vector<model>  m_box_models; | ||||||
|         unsigned            m_box_index; |         unsigned            m_box_index; | ||||||
|  | @ -231,6 +234,8 @@ namespace opt { | ||||||
| 
 | 
 | ||||||
|         virtual bool verify_model(unsigned id, model* mdl, rational const& v); |         virtual bool verify_model(unsigned id, model* mdl, rational const& v); | ||||||
| 
 | 
 | ||||||
|  |         void get_lns_literals(expr_ref_vector& lits); | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
|         lbool execute(objective const& obj, bool committed, bool scoped); |         lbool execute(objective const& obj, bool committed, bool scoped); | ||||||
|         lbool execute_min_max(unsigned index, bool committed, bool scoped, bool is_max); |         lbool execute_min_max(unsigned index, bool committed, bool scoped, bool is_max); | ||||||
|  | @ -238,6 +243,7 @@ namespace opt { | ||||||
|         lbool execute_lex(); |         lbool execute_lex(); | ||||||
|         lbool execute_box(); |         lbool execute_box(); | ||||||
|         lbool execute_pareto(); |         lbool execute_pareto(); | ||||||
|  |         lbool execute_lns(); | ||||||
|         lbool adjust_unknown(lbool r); |         lbool adjust_unknown(lbool r); | ||||||
|         bool scoped_lex(); |         bool scoped_lex(); | ||||||
|         expr_ref to_expr(inf_eps const& n); |         expr_ref to_expr(inf_eps const& n); | ||||||
|  |  | ||||||
							
								
								
									
										115
									
								
								src/opt/opt_lns.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/opt/opt_lns.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2018 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     opt_lns.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Large neighborhood search default implementation  | ||||||
|  |     based on phase saving and assumptions | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2018-3-13 | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  |     | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "opt/opt_lns.h" | ||||||
|  | #include "opt/opt_context.h" | ||||||
|  | 
 | ||||||
|  | namespace opt { | ||||||
|  |     | ||||||
|  |     lns::lns(context& ctx, solver* s): | ||||||
|  |         m(ctx.get_manager()), | ||||||
|  |         m_ctx(ctx), | ||||||
|  |         m_solver(s), | ||||||
|  |         m_models_trail(m) | ||||||
|  |     {} | ||||||
|  | 
 | ||||||
|  |     lns::~lns() {} | ||||||
|  | 
 | ||||||
|  |     void lns::display(std::ostream & out) const { | ||||||
|  |         for (auto const& q : m_queue) { | ||||||
|  |             out << q.m_index << ": " << q.m_assignment << "\n"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     lbool lns::operator()() { | ||||||
|  | 
 | ||||||
|  |         if (m_queue.empty()) { | ||||||
|  |             expr_ref_vector lits(m); | ||||||
|  |             m_ctx.get_lns_literals(lits); | ||||||
|  |             m_queue.push_back(queue_elem(lits)); | ||||||
|  |             m_qhead = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         params_ref p; | ||||||
|  |         p.set_uint("sat.inprocess.max", 3); | ||||||
|  |         p.set_uint("smt.max_conflicts", 10000); | ||||||
|  |         m_solver->updt_params(p); | ||||||
|  | 
 | ||||||
|  |         while (m_qhead < m_queue.size()) { | ||||||
|  |             unsigned index = m_queue[m_qhead].m_index;             | ||||||
|  |             if (index > m_queue[m_qhead].m_assignment.size()) { | ||||||
|  |                 ++m_qhead; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             IF_VERBOSE(2, verbose_stream() << "(opt.lns :queue " << m_qhead << " :index " << index << ")\n"); | ||||||
|  |              | ||||||
|  |             // recalibrate state to an initial satisfying assignment
 | ||||||
|  |             lbool is_sat = m_solver->check_sat(m_queue[m_qhead].m_assignment); | ||||||
|  |             IF_VERBOSE(2, verbose_stream() << "(opt.lns :calibrate-status " << is_sat << ")\n"); | ||||||
|  | 
 | ||||||
|  |             expr_ref lit(m_queue[m_qhead].m_assignment[index].get(), m); | ||||||
|  |             lit = mk_not(m, lit); | ||||||
|  |             expr* lits[1] = { lit }; | ||||||
|  |             ++m_queue[m_qhead].m_index; | ||||||
|  |             if (!m.limit().inc()) { | ||||||
|  |                 return l_undef; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Update configuration for local search:
 | ||||||
|  |             // p.set_uint("sat.local_search_threads", 2);
 | ||||||
|  |             // p.set_uint("sat.unit_walk_threads", 1);
 | ||||||
|  |              | ||||||
|  |             is_sat = m_solver->check_sat(1, lits); | ||||||
|  |             IF_VERBOSE(2, verbose_stream() << "(opt.lns :status " << is_sat << ")\n"); | ||||||
|  |             if (is_sat == l_true && add_assignment()) { | ||||||
|  |                 return l_true; | ||||||
|  |             } | ||||||
|  |         }         | ||||||
|  |         return l_false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool lns::add_assignment() { | ||||||
|  |         model_ref mdl; | ||||||
|  |         m_solver->get_model(mdl); | ||||||
|  |         m_ctx.fix_model(mdl); | ||||||
|  |         expr_ref tmp(m); | ||||||
|  |         expr_ref_vector fmls(m); | ||||||
|  |         for (expr* f : m_queue[0].m_assignment) { | ||||||
|  |             mdl->eval(f, tmp); | ||||||
|  |             if (m.is_false(tmp)) { | ||||||
|  |                 fmls.push_back(mk_not(m, tmp)); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 fmls.push_back(tmp); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         tmp = mk_and(fmls); | ||||||
|  |         if (m_models.contains(tmp)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             m_models.insert(tmp); | ||||||
|  |             m_models_trail.push_back(tmp); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										66
									
								
								src/opt/opt_lns.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/opt/opt_lns.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2018 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     opt_lns.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Large neighborhood seearch | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2018-3-13 | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  |     | ||||||
|  | --*/ | ||||||
|  | #ifndef OPT_LNS_H_ | ||||||
|  | #define OPT_LNS_H_ | ||||||
|  | 
 | ||||||
|  | #include "solver/solver.h" | ||||||
|  | #include "model/model.h" | ||||||
|  | 
 | ||||||
|  | namespace opt { | ||||||
|  | 
 | ||||||
|  |     class context; | ||||||
|  | 
 | ||||||
|  |     class lns { | ||||||
|  |         struct queue_elem { | ||||||
|  |             expr_ref_vector m_assignment; | ||||||
|  |             unsigned        m_index; | ||||||
|  |             queue_elem(expr_ref_vector& assign): | ||||||
|  |                 m_assignment(assign), | ||||||
|  |                 m_index(0) | ||||||
|  |             {} | ||||||
|  |         }; | ||||||
|  |         ast_manager&     m; | ||||||
|  |         context&         m_ctx; | ||||||
|  |         ref<solver>      m_solver; | ||||||
|  |         model_ref        m_model; | ||||||
|  |         svector<symbol>  m_labels; | ||||||
|  |         vector<queue_elem> m_queue; | ||||||
|  |         unsigned         m_qhead; | ||||||
|  |         expr_ref_vector  m_models_trail; | ||||||
|  |         obj_hashtable<expr> m_models; | ||||||
|  | 
 | ||||||
|  |         bool add_assignment(); | ||||||
|  |     public: | ||||||
|  |         lns(context& ctx, solver* s); | ||||||
|  | 
 | ||||||
|  |         ~lns(); | ||||||
|  | 
 | ||||||
|  |         void display(std::ostream & out) const; | ||||||
|  | 
 | ||||||
|  |         lbool operator()(); | ||||||
|  | 
 | ||||||
|  |         void get_model(model_ref& mdl, svector<symbol>& labels) { | ||||||
|  |             mdl = m_model; | ||||||
|  |             labels = m_labels; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -3,7 +3,7 @@ def_module_params('opt', | ||||||
|                   export=True, |                   export=True, | ||||||
|                   params=(('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), |                   params=(('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), | ||||||
|                           ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres'"), |                           ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres'"), | ||||||
|                           ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', or 'box'"), |                           ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', 'box', or 'lns' (large neighborhood search)"), | ||||||
|                           ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), |                           ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), | ||||||
|                           ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), |                           ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), | ||||||
|                           ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), |                           ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), | ||||||
|  |  | ||||||
|  | @ -222,7 +222,7 @@ namespace sat { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         TRACE("scc", for (unsigned i = 0; i < roots.size(); i++) { tout << i << " -> " << roots[i] << "\n"; } |         TRACE("scc", for (unsigned i = 0; i < roots.size(); i++) { tout << i << " -> " << roots[i] << "\n"; } | ||||||
|               tout << "to_elim: "; for (literal l : to_elim) tout << l << " "; tout << "\n";); |               tout << "to_elim: "; for (unsigned v : to_elim) tout << v << " "; tout << "\n";); | ||||||
|         m_num_elim += to_elim.size(); |         m_num_elim += to_elim.size(); | ||||||
|         elim_eqs eliminator(m_solver); |         elim_eqs eliminator(m_solver); | ||||||
|         eliminator(roots, to_elim); |         eliminator(roots, to_elim); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue