def iter_specs(self, inp_spec: EngineSpec, depth: int, programs: List[Set[FunctionCall]] = None): """ The basic version evaluates all argument combinations, and maintains a cache of results which clubs together combinations that produce the same result. This cache is then iterated over at the end to produce the new specs for the next depth level. If this is the last function in the overall sequence, then the values are returned along with programs as final results. The checking of the result against the output is NOT done here. This will be the responsibility of the function-sequence engine. This is to afford the capability of pausing/restarting this argument engine. """ func: BaseGenerator = self.func_sequence[depth - 1] if programs is None: programs = [None] * len(self.func_sequence) result_cache: ResultCache = ResultCache() for arg_vals, arg_annotations in self.iter_args_wrapper( inp_spec, depth, programs): result = self.execute(func, arg_vals, arg_annotations) if self.stats is not None: self.stats.num_cands_generated[depth] += 1 if result is None: if self.stats is not None: self.stats.num_cands_error[depth] += 1 continue call: FunctionCall = FunctionCall(func, arg_vals, arg_annotations) result_cache.insert(result, call) if depth == len(self.func_sequence): for result, calls in result_cache.iter_results(): programs[depth - 1] = calls if self.stats is not None: self.stats.num_cands_propagated[depth] += 1 yield result, programs programs[depth - 1] = None else: for result, calls in result_cache.iter_results(): programs[depth - 1] = calls inp_spec.intermediates[depth - 1] = result inp_spec.depth = depth + 1 if self.stats is not None: self.stats.num_cands_propagated[depth] += 1 yield from self.iter_specs(inp_spec, depth + 1, programs) programs[depth - 1] = None inp_spec.depth = depth
def iter_specs(self, inp_spec: EngineSpec, depth: int, programs: List[Set[FunctionCall]] = None): """ The checking of the result against the output is NOT done here. This will be the responsibility of the function-sequence engine. This is to afford the capability of pausing/restarting this argument engine. """ func: BaseGenerator = self.func_sequence[depth - 1] if programs is None: programs = [None] * len(self.func_sequence) for arg_vals, arg_annotations, prob in self.iter_args_wrapper( inp_spec, depth, programs): result = self.execute(func, arg_vals, arg_annotations) if self.stats is not None: self.stats.num_cands_generated[depth] += 1 if result is None: if self.stats is not None: self.stats.num_cands_error[depth] += 1 continue call: FunctionCall = FunctionCall(func, arg_vals, arg_annotations) if depth == len(self.func_sequence): programs[depth - 1] = [call] if self.stats is not None: self.stats.num_cands_propagated[depth] += 1 if self.get_probs: yield result, programs, prob else: yield result, programs programs[depth - 1] = None else: programs[depth - 1] = [call] inp_spec.intermediates[depth - 1] = result inp_spec.depth = depth + 1 if self.stats is not None: self.stats.num_cands_propagated[depth] += 1 yield from self.iter_specs(inp_spec, depth + 1, programs) programs[depth - 1] = None inp_spec.intermediates[depth - 1] = None inp_spec.depth = depth