/*++ Copyright (c) 2006 Microsoft Corporation Module Name: der.h Abstract: Destructive equality resolution. Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution --*/ #ifndef _DER_H_ #define _DER_H_ #include"ast.h" #include"var_subst.h" #include"assertion_set_strategy.h" /* New DER: the class DER (above) eliminates variables one by one. This is inefficient, and we should implement a new version that can handle efficiently examples with hundreds of variables. Suppose the target of the simplification is the quantifier (FORALL (x1 T1) (x2 T2) ... (xn Tn) (or ....)) So, the variables x1 ... xn are the candidates for elimination. First, we build a mapping of candidate substitutions Since variables x1 ... xn have ids 0 ... n-1, we can use an array m_map to implement this mapping. The idea is to traverse the children of the (or ...) looking for diseqs. The method is_var_diseq can be used for doing that. Given a child c, if is_var_diseq(c, num_decls, v, t) returns true, and m_map[v] is null, then we store t at m_map[v]. For performance reasons, we also store a mapping from child position (in the OR) to variable ID. Thus, m_pos2var[pos] contains the variable that is defined by the diseq at position pos. m_pos2var[pos] = -1, if this position does not contain a diseq. After doing that, m_map contains the variables that can be potentially eliminated using DER. We say m_map[v] is the definition of variable v. The next step is to perform a topological sort on these variables. The result is an array (m_order) of integers (variable ids) such that i < j implies that m_map[m_order[i]] does not depend on variable m_order[j]. For example, consider the case where m_map contains the following values m_map[0] = (+ (VAR 2) 0) m_map[1] = null m_map[2] = (+ (VAR 3) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null In this example, variable 0 depends on the definition of variable 2, which depends on the definition of variable 3, which depends on the definition of variable 0 (cycle). On the other hand, no cycle is found when starting at variable 4. Cycles can be broken by erasing entries from m_map. For example, the cycle above can be removed by setting m_map[0] = null. m_map[0] = null m_map[1] = null m_map[2] = (+ (VAR 3) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null The file asserted_formulas.cpp has a class top_sort for performing topological sort. This class cannot be used here, since it is meant for eliminating constants (instead of variables). We need to implement a new top_sort here, we do not need a separate class for doing that. Moreover, it is much simpler, since m_map is just an array. In the example above (after setting m_map[0] to null), top_sort will produce the following order m_order = [3, 2, 4] The next step is to use var_subst to update the definitions in var_subst. The idea is to process the variables in the order specified by m_order. When processing m_map[m_order[i]] we use the definitions of all variables in m_order[0 ... i-1]. For example: The first variable is 3, since it is at m_order[0], nothing needs to be done. Next we have variable 2, we use m_map[3] since 3 is before 2 in m_order. So, the new definition for 2 is (+ (+ (VAR 1) 1) 0). That is, we update m_map[2] with (+ (+ (VAR 1) 1) 0) Next we have variable 4, we use m_map[3] and m_map[2] since 3 and 2 are before 4 in m_order. In this case, var_subst will not do anything since m_map[4] does not contain variables 3 or 2. So, the new m_map is: m_map[0] = null m_map[1] = null m_map[2] = (+ (+ (VAR 1) 1) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null Now, we update the body of the quantifier using var_subst and the mapping above. The idea is to create a new set of children for the OR. For each child at position i, we do if m_map[m_pos2var[i]] != -1 skip this child, it is a diseq used during DER else apply var_subst using m_map to this child, and store the result in a new children array Create a new OR (new body of the quantifier) using the new children Then, we create a new quantifier using this new body, and use the function elim_unused_vars to eliminate the ununsed variables. Remark: let us implement the new version inside the class der. Use #if 0 ... #endif to comment the old version. Remark: after you are done, we can eliminate the call to occurs in is_var_diseq, since top_sort is already performing cycle detection. */ /** \brief Functor for applying Destructive Multi-Equality Resolution. (forall (X Y) (or X /= s C[X])) --> (forall (Y) C[Y]) */ class der { ast_manager & m_manager; var_subst m_subst; expr_ref_buffer m_new_exprs; ptr_vector m_map; int_vector m_pos2var; ptr_vector m_inx2var; unsigned_vector m_order; expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; /** \brief Return true if e can be viewed as a variable disequality. Store the variable id in v and the definition in t. For example: if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T. if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T). (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))), and can be viewed as a disequality. */ bool is_var_diseq(expr * e, unsigned num_decls, var *& v, expr_ref & t); void get_elimination_order(); void create_substitution(unsigned sz); void apply_substitution(quantifier * q, expr_ref & r); void reduce1(quantifier * q, expr_ref & r, proof_ref & pr); public: der(ast_manager & m):m_manager(m),m_subst(m),m_new_exprs(m),m_subst_map(m),m_new_args(m) {} ast_manager & m() const { return m_manager; } void operator()(quantifier * q, expr_ref & r, proof_ref & pr); }; /** \brief Functor for applying Destructive Multi-Equality Resolution in all universal quantifiers in an expression. */ class der_rewriter { protected: struct imp; imp * m_imp; public: der_rewriter(ast_manager & m); ~der_rewriter(); ast_manager & m () const; void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void cancel() { set_cancel(true); } void reset_cancel() { set_cancel(false); } void set_cancel(bool f); void cleanup(); void reset(); }; typedef der_rewriter der_star; // TODO: delete obsolete class class der_strategy : public assertion_set_strategy { struct imp; imp * m_imp; public: der_strategy(ast_manager & m); virtual ~der_strategy(); void operator()(assertion_set & s); virtual void operator()(assertion_set & s, model_converter_ref & mc) { operator()(s); mc = 0; } virtual void cleanup(); virtual void set_cancel(bool f); }; inline as_st * mk_der(ast_manager & m) { return alloc(der_strategy, m); } class tactic; tactic * mk_der_tactic(ast_manager & m); #endif /* _DER_H_ */