def _unfold(self, op: Operator, n: int) -> Optional[Operator]: """ Unroll all possible operators from the grammar `g` starting from non-terminal `op` after `n` derivations. Parameters ---------- op : Operator starting rule (e.g., `g.start`) n : int number of derivations Returns ------- Optional[Operator] """ if isinstance(op, BasePipeline): steps = op.steps() new_steps = [self._unfold(sop, n) for sop in op.steps()] step_map = {steps[i]: new_steps[i] for i in range(len(steps))} new_edges = [(step_map[s], step_map[d]) for s, d in op.edges()] if not None in new_steps: return get_pipeline_of_applicable_type(new_steps, new_edges, True) return None if isinstance(op, OperatorChoice): steps = [ s for s in (self._unfold(sop, n) for sop in op.steps()) if s ] return make_choice(*steps) if steps else None if isinstance(op, NonTerminal): return self._unfold(self._variables[op.name()], n - 1) if n > 0 else None if isinstance(op, IndividualOp): return op assert False, f"Unknown operator {op}"
def _sample(self, op: Operator, n: int) -> Optional[Operator]: """ Sample the grammar `g` starting from `g.start`, that is, choose one element at random for each possible choices. Parameters ---------- op : Operator starting rule (e.g., `g.start`) n : int number of derivations Returns ------- Optional[Operator] """ if isinstance(op, BasePipeline): steps = op.steps() new_steps = [self._sample(sop, n) for sop in op.steps()] step_map = {steps[i]: new_steps[i] for i in range(len(steps))} new_edges = [(step_map[s], step_map[d]) for s, d in op.edges()] if not None in new_steps: return get_pipeline_of_applicable_type(new_steps, new_edges, True) return None if isinstance(op, OperatorChoice): return self._sample(random.choice(op.steps()), n) if isinstance(op, NonTerminal): return self._sample(getattr(self, op.name()), n - 1) if n > 0 else None if isinstance(op, IndividualOp): return op assert False, f"Unknown operator {op}"