def _standardize_quantified_variables(node: syntax.Node, state: syntax.WalkState) -> syntax.Node: """Standardizes quantified variables by giving them unique names. :param node: The node to rewrite. :param state: Rewriting state. :returns: Rewritten node. Rewrites expressions of type: - `*x: A(x)` into `*var_1: A(var_1)`, - `?x: A(x)` into `?var_1: A(var_1)`. """ seen: List[syntax.Node] = state.context.setdefault('seen', []) replaced: syntax.T_Substitution = state.context.setdefault('replaced', {}) # todo: replaced would not work with nested quantified formulas that # reuse the same symbol, but actually never mind - it's a corner case # I don't want to fiddle with now if node in seen: return node if node.is_quantified(): old = node.get_quantified_variable().value if old.startswith('_'): return node # already renamed new = _new_variable_name() qtype = node.get_quantifier_type() quant = syntax.make_quantifier(qtype, new) rv = syntax.make_formula(quant, node.children) replaced[new] = node.get_quantified_variable() state.stack.append((old, new)) seen.append(rv) return rv elif node.is_variable(): # reversed, because we want to rename symbol to the last seen value. # Example: We want to rewrite `?x, ?x: x` into `?a: ?b: b`. for old, new in reversed(state.stack): if old == node.value: rv = syntax.make_variable(new) seen.append(rv) return rv return node else: return node
def _skolemize(node: syntax.Node, state: syntax.WalkState) -> syntax.Node: """Skolemizes expressions and drops quantifiers. :param node: The node to rewrite. :param state: Rewriting state. :returns: Rewritten node. Rewrites expressions of type: - `?x: x` into `C1`, - `*a: a & ?x: x` into `a & F1(a)`, - `*a: a & ?x: x & *b: b & ?y: y` into `a & F1(a) & b & F2(a, b)`, """ if not state.stack: state.stack.extend([[], []]) # enclosing universally quantified variables universal: List[str] = state.stack[0] replacements: List[Tuple[str, syntax.Node]] = state.stack[1] replaced: syntax.T_Substitution = state.context.setdefault('replaced', {}) if node.is_quantified(): qtype = node.get_quantifier_type() if qtype == syntax.UNIVERSAL_QUANTIFIER: qv = node.get_quantified_variable() universal.append(qv.value) else: assert qtype == syntax.EXISTENTIAL_QUANTIFIER # store what to replace (actual replacement happens in the # elif branch below) qv = node.get_quantified_variable() old = qv.value if universal: name = _new_function_name() new = syntax.make_function(name, *universal) else: name = _new_constant_name() new = syntax.make_constant(name) replaced[new.value] = qv replacements.append((old, new)) # drop quantifiers children = node.children assert len(children) == 1 return children[0] elif node.is_variable(): for old, new in replacements: if old == node.value: return copy.deepcopy(new) return node else: return node