def __init__(self, spec: TyrellSpec, interpreter: Interpreter, examples: List[Example], equal_output: Callable[[Any, Any], bool] = lambda x, y: x == y): super().__init__(interpreter, examples, equal_output) self._assert_handler = AssertionViolationHandler(spec, interpreter)
def __init__(self, spec: TyrellSpec, interpreter: Interpreter, examples: List[Example], prune: str, equal_output: Callable[[Any, Any], bool] = lambda x, y: x == y): super().__init__(interpreter, examples, equal_output) self._assert_handler = AssertionViolationHandler(spec, interpreter) self.prune = prune # the memory used for storing abstract evaluation result, so that we don't have to run the prune again and again self.abstract_eval_memory = {}
class BidirectionalDecider(ExampleDecider): assert_handler: AssertionViolationHandler def __init__(self, spec: TyrellSpec, interpreter: Interpreter, examples: List[Example], prune: str, equal_output: Callable[[Any, Any], bool] = lambda x, y: x == y): super().__init__(interpreter, examples, equal_output) self._assert_handler = AssertionViolationHandler(spec, interpreter) self.prune = prune # the memory used for storing abstract evaluation result, so that we don't have to run the prune again and again self.abstract_eval_memory = {} def analyze_interpreter_error(self, error: InterpreterError): return self._assert_handler.handle_interpreter_error(error) def analyze(self, prog): blame_finder = BlameFinder(self.interpreter, prog) res = blame_finder.process_examples(self.examples, self.equal_output, self.prune, self.abstract_eval_memory) return res
def test_blame(self): enode0 = builder.make_enum('SmallInt', '-3') enode1 = builder.make_enum('SmallInt', '-2') snode = builder.make_apply('sqrt', [enode0]) inode = builder.make_apply('id', [snode]) interp = FooInterpreter() handler = AssertionViolationHandler(spec, interp) with self.assertRaises(AssertionViolation) as cm: interp.eval(inode, []) type_error = cm.exception blames = handler.handle_interpreter_error(type_error) self.assertIsNotNone(blames) self.assertEqual(len(blames), 2) for blame in blames: self.assertNotIn(Blame(inode, inode.production), blame) self.assertIn(Blame(snode, snode.production), blame) self.assertTrue((Blame(enode0, enode0.production) in blame) or (Blame(enode0, enode1.production) in blame))
def test_blame_with_capture(self): enode0 = builder.make_enum('SmallInt', '-2') cnode = builder.make_apply('const', [enode0]) enode1 = builder.make_enum('SmallInt', '-3') enode2 = builder.make_enum('SmallInt', '3') dnode = builder.make_apply('idiv', [cnode, enode1]) interp = FooInterpreter() handler = AssertionViolationHandler(spec, interp) with self.assertRaises(AssertionViolation) as cm: interp.eval(dnode, []) type_error = cm.exception blames = handler.handle_interpreter_error(type_error) self.assertIsNotNone(blames) self.assertEqual(len(blames), 2) for blame in blames: self.assertIn(Blame(dnode, dnode.production), blame) self.assertTrue((Blame(enode1, enode1.production) in blame) or (Blame(enode1, enode2.production) in blame)) # These nodes are captured in our assertion self.assertIn(Blame(cnode, cnode.production), blame) self.assertIn(Blame(enode0, enode0.production), blame)
class ExampleConstraintPruningDecider(ExampleDecider): assert_handler: AssertionViolationHandler def __init__(self, spec: TyrellSpec, interpreter: Interpreter, examples: List[Example], equal_output: Callable[[Any, Any], bool] = lambda x, y: x == y): super().__init__(interpreter, examples, equal_output) self._assert_handler = AssertionViolationHandler(spec, interpreter) def analyze_interpreter_error(self, error: InterpreterError): return self._assert_handler.handle_interpreter_error(error) def analyze(self, prog): blame_finder = BlameFinder(self.interpreter, prog) return blame_finder.process_examples(self.examples, self.equal_output)
class ExampleConstraintDecider(ExampleDecider): _imply_map: ImplyMap _assert_handler: AssertionViolationHandler def __init__(self, spec: TyrellSpec, interpreter: Interpreter, examples: List[Example], equal_output: Callable[[Any, Any], bool] = lambda x, y: x == y): super().__init__(interpreter, examples, equal_output) self._imply_map = self._build_imply_map(spec) self._assert_handler = AssertionViolationHandler(spec, interpreter) def _check_implies(self, pre, post) -> bool: def encode_property(prop_expr: PropertyExpr): param_expr = cast(ParamExpr, prop_expr.operand) var_name = '{}_p{}'.format(prop_expr.name, param_expr.index) ptype = prop_expr.type if ptype is ExprType.INT: return z3.Int(var_name) elif ptype is ExprType.BOOL: return z3.Bool(var_name) else: raise RuntimeError('Unrecognized ExprType: {}'.format(ptype)) constraint_visitor = ConstraintEncoder(encode_property) z3_solver = z3.Solver() z3_pre = constraint_visitor.visit(pre) z3_post = constraint_visitor.visit(post) z3_solver.add(z3.Not(z3.Implies(z3_pre, z3_post))) return z3_solver.check() == z3.unsat def _build_imply_map(self, spec: TyrellSpec) -> ImplyMap: ret: MutableImplyMap = defaultdict(list) constrained_prods = filter( lambda prod: prod.is_function() and len(prod.constraints) > 0, spec.productions()) for prod0, prod1 in permutations(constrained_prods, r=2): if len(prod0.rhs) != len(prod1.rhs): continue for c0 in prod0.constraints: for c1 in prod1.constraints: if self._check_implies(c1, c0): ret[(prod0, c0)].append(prod1) break return ret def analyze(self, prog): ''' This version of analyze() tries to analyze the reason why a synthesized program fails, if it does not pass all the tests. ''' failed_examples = self.get_failed_examples(prog) if len(failed_examples) == 0: return ok() else: blame_finder = BlameFinder(self.interpreter, self._imply_map, prog) blame_finder.process_examples(failed_examples) blames = blame_finder.get_blames() if len(blames) == 0: return bad() else: return bad(why=blames) def analyze_interpreter_error(self, error: InterpreterError): return self._assert_handler.handle_interpreter_error(error)