def transform_fg_universe(finite_model, lambdafunc, annctx): """ Applies the given function lambdafunc to all elements of the foreground universe present in finite_model. All vocabulary entries must be tracked by annctx. :param finite_model: dict {string -> dict {tuple of any -> any}} :param lambdafunc: function :param annctx: naturalproofs.AnnotatedContext.AnnotatedContext :return: dict {string -> dict {tuple of any -> any}} """ vocab_keys = finite_model.keys() for vocab_key in vocab_keys: vocab_decl = get_decl_from_name(vocab_key, annctx) arity = vocab_decl.arity() uct_signature = get_uct_signature(vocab_decl, annctx) if all(sig not in {fgsort, fgsetsort} for sig in uct_signature): # No foreground sort elements anywhere. No transformation required continue new_vocab_key_dict = dict() args = finite_model[vocab_key].keys() for arg in args: new_arg = tuple( _transform_value(arg[i], uct_signature[i], lambdafunc) for i in range(arity)) new_out = _transform_value(finite_model[vocab_key][arg], uct_signature[-1], lambdafunc) new_vocab_key_dict[new_arg] = new_out finite_model[vocab_key] = new_vocab_key_dict return finite_model
def collect_fg_universe(finite_model, annctx): """ Returns all elements of the foreground universe present in finite_model. All vocabulary entries must be tracked by annctx. :param finite_model: dict {string -> dict {tuple of any -> any}} :param annctx: naturalproofs.AnnotatedContext.AnnotatedContext :return: set of any """ fg_elem_set = set() vocab_keys = finite_model.keys() for vocab_key in vocab_keys: vocab_decl = get_decl_from_name(vocab_key, annctx) arity = vocab_decl.arity() uct_signature = get_uct_signature(vocab_decl, annctx) if all(sig not in {fgsort, fgsetsort} for sig in uct_signature): # No foreground sort elements anywhere. No collection required continue args = finite_model[vocab_key].keys() for arg in args: for i in range(arity): fg_elem_set = fg_elem_set | _collect_value( arg[i], uct_signature[i]) fg_elem_set = fg_elem_set | _collect_value( finite_model[vocab_key][arg], uct_signature[-1]) return fg_elem_set
def modelToSolver(model, vocab, sol, annctx): for fct in vocab: arity = fct.arity() if arity == 0: # TODO: handle constant symbols # constant symbol continue else: fct_name = fct.name() uct_signature = get_uct_signature(fct, annctx) for input_arg in model[fct_name].keys(): output_value = model[fct_name][input_arg] if isinstance(output_value, set): output_value_converted = recover_value(output_value, uct_signature[-1]) else: output_value_converted = output_value if isinstance(input_arg, tuple): # arg must be unpacked as *arg before constructing the Z3 term sol.add(fct(*input_arg) == output_value_converted) else: sol.add(fct(input_arg) == output_value_converted)
def __init__(self, smtmodel, terms, vocabulary=None, annctx=default_annctx): """ Finite model creation. Foreground universe of extracted model corresponds to terms with subterm-closure. If vocabulary is not specified, the entire vocabulary tracked in annctx is used. :param smtmodel: z3.ModelRef :param terms: set of z3.ExprRef :param vocabulary: set of z3.ExprRef :param annctx: naturalproofs.AnnotatedContext.AnnotatedContext This function also defines the format of finite models. Given a vocabulary of functions f1, f2, ..fm, of arity a1, a2, ...am, the model is as follows: model :: dict {'fk' : dict_fk} where 'fk' is some representation of the function fk, and dict_fk :: dict {(e1, e2,... ek) : fk(e1, e2, ...ek)} where (e1, e2, ...ek) is a tuple such that e1, e2,.. etc are concrete values in python that are dependent on the domain and range sorts of fk. In particular if the arity k is 0, then dict_fk will be of the following form: dict_fk :: dict {() : fk()} These models are meant to be partial models, and in general it will not be possible to evaluate an arbitrary formula on such a model, just those that are quantifier-free with foreground terms in the set of terms used to extract the finite model. """ model = dict() # TODO: VERY IMPORTANT: hack to include those terms that are directly given as integers or integer expressions elems = {smtmodel.eval(term, model_completion=True) for term in terms} # TODO: the assumption is that uninterpreted functions have arguments only from the foreground sort. Must handle # cases where uninterpreted functions have arguments in other domains, primarily integers. # Subterm-close the given terms assuming one-way functions # get_foreground_terms already performs subterm closure subterm_closure = get_foreground_terms(terms, annctx=annctx) elems = elems | { smtmodel.eval(term, model_completion=True) for term in subterm_closure } if vocabulary is None: vocabulary = get_vocabulary(annctx) for func in vocabulary: arity = func.arity() *input_signature, output_sort = get_uct_signature(func, annctx) # Only supporting uninterpreted functions with input arguments all from the foreground sort if not all(sig == fgsort for sig in input_signature): raise ValueError( 'Function with input(s) not from the foreground sort. Unsupported.' ) func_key_repr = model_key_repr(func) # Distinguish common cases for faster execution if arity == 0: model[func_key_repr] = { (): _extract_value( smtmodel.eval(func(), model_completion=True), output_sort) } elif arity == 1: model[func_key_repr] = { (_extract_value(elem, fgsort), ): _extract_value( smtmodel.eval(func(elem), model_completion=True), output_sort) for elem in elems } else: func_dict = dict() args = itertools.product(elems, repeat=arity) for arg in args: arg_value = tuple( _extract_value(component, fgsort) for component in arg) func_dict[arg_value] = _extract_value( smtmodel.eval(func(*arg), model_completion=True), output_sort) model[func_key_repr] = func_dict # Object attributes self.finitemodel = model self.smtmodel = smtmodel self.vocabulary = vocabulary self.annctx = annctx self.offset = 0 self.fg_universe = collect_fg_universe(self.finitemodel, self.annctx) # Logging attributes self.extraction_terms = subterm_closure # Caching attributes self.recompute_offset = True
def gen_lfp_model(size, annctx, invalid_formula=None): """ Generate a finite model of the theory given by annctx of specified size where the valuation of recursive definitions respects LFP semantics. Returns None if model with specified conditions does not exist. Optional argument invalid_formula is a bound formula that must be falsified by the returned model. :param size: int :param annctx: naturalproofs.AnnotatedContext.AnnotatedContext :param invalid_formula: pair (tuple of z3.ExprRef, z3.BoolRef) :return: pair (z3.ModelRef, set) """ # Establish underlying universe universe = {z3.IntVal(i) for i in range(size)} constraints = [] vocabulary = get_vocabulary(annctx=annctx) # Closure for vocabluary that is over the foreground sort # If signature guidelines follow naturalproofs.uct then range can only be fgsort if the domain args are all fgsort foreground_vocabluary = { funcdecl for funcdecl in vocabulary if all(srt == fgsort for srt in get_uct_signature(funcdecl, annctx=annctx)) } for funcdecl in foreground_vocabluary: argslist = itertools.product(universe, repeat=funcdecl.arity()) constraints.append( And([ Or([funcdecl(*args) == elem for elem in universe]) for args in argslist ])) # Recursive definitions and ranks recdefs = get_recursive_definition(None, alldefs=True, annctx=annctx) recdef_unfoldings = make_recdef_unfoldings(recdefs) untagged_unfoldings = set(recdef_unfoldings.values()) rank_formulas = { recdef.name(): rank_defs_dict.get(recdef.name(), None) for recdef, _, _ in recdefs } if all(value is None for value in rank_formulas.values()): raise Exception( 'Rank definitions must be given at least for the underlying datastructure definition. ' 'This should be one among: {}'.format(', '.join( key for key, value in rank_formulas.items() if value is None))) # Axioms axioms = get_all_axioms(annctx=annctx) structural_constraints = untagged_unfoldings | set( rankdef for rankdef in rank_formulas.values() if rankdef is not None) | axioms constraints.extend(instantiate(structural_constraints, universe)) # Bound formula to negate if invalid_formula is not None: formal_vars, body = invalid_formula constraints.append( Or([ Not(apply_bound_formula(invalid_formula, args)) for args in itertools.product(universe, repeat=len(formal_vars)) ])) z3.set_param('smt.random_seed', 0) z3.set_param('sat.random_seed', 0) sol = z3.Solver() sol.add(constraints) if sol.check() == z3.sat: lfp_model = sol.model() # Project model onto the numbers corresponding to the foreground universe finite_lfp_model = FiniteModel(lfp_model, universe, annctx=annctx) return finite_lfp_model, universe else: return None, None