def connect_tuple_wrappers(node, output_type): if not isinstance(output_type, Tuple): return for index in range(0, len(output_type.inner)): internal_input = output_type.inner[index] for other_clss in registry: annotations = _get_annotations(other_clss) other_input = annotations.input if not (conforms(internal_input, other_input) and other_clss != node): continue # `other_class` has input compatible with one element in the Tuple # build the output `Tuple[..., internal_output, ...]` of the wrapper class internal_output = annotations.output output_tuple = list(output_type.inner) output_tuple[index] = internal_output output_tuple_type = Tuple(*output_tuple) # dynamic class representing the wrapper algorithm if (index, output_type, output_tuple_type) in list_tuples: continue other_wrapper = build_composite_tuple( index, output_type, output_tuple_type ) list_tuples.add((index, output_type, output_tuple_type)) registry.append(other_wrapper) open_nodes.append(other_wrapper) G.add_edge(node, other_wrapper)
def build(internal_output, depth): if internal_output in types_seen: return for other_clss in registry: annotations = _get_annotations(other_clss) if annotations in list_pairs: continue other_input = annotations.input other_output = annotations.output if other_input == other_output: continue if not conforms(internal_output, other_input): continue other_wrapper = build_composite_list(other_input, other_output, depth) list_pairs.add(annotations) registry.append(other_wrapper) types_queue.append(_get_annotations(other_wrapper).output)
def _has_output(clss, output): return conforms(_get_annotations(clss).output, output)
def _has_input(clss, input): return conforms(input, _get_annotations(clss).input)
def build_pipelines(input, output, registry) -> "PipelineBuilder": """ Creates a `PipelineBuilder` instance that generates all pipelines from `input` to `output` types. ##### Parameters - `input`: type descriptor for the desired input. - `output`: type descriptor for the desired output. - `registry`: list of available classes to build the pipelines. """ # warnings.warn( # "This method is deprecated and not under use by AutoGOAL's" # " internal API anymore, use `build_pipeline_graph` instead.", # category=DeprecationWarning, # stacklevel=2, # ) list_pairs = set() types_queue = [] if isinstance(input, Tuple): types_queue.extend(input.inner) else: types_queue.append(input) if isinstance(output, Tuple): types_queue.extend(output.inner) else: types_queue.append(output) types_seen = set() while types_queue: output_type = types_queue.pop(0) def build(internal_output, depth): if internal_output in types_seen: return for other_clss in registry: annotations = _get_annotations(other_clss) if annotations in list_pairs: continue other_input = annotations.input other_output = annotations.output if other_input == other_output: continue if not conforms(internal_output, other_input): continue other_wrapper = build_composite_list(other_input, other_output, depth) list_pairs.add(annotations) registry.append(other_wrapper) types_queue.append(_get_annotations(other_wrapper).output) depth = 0 while isinstance(output_type, List): if output_type.depth() >= MAX_LIST_DEPTH: break depth += 1 output_type = output_type.inner build(output_type, depth) types_seen.add(output_type) logger.debug("Output type", output_type) list_tuples = set() def connect_tuple_wrappers(node, output_type): if not isinstance(output_type, Tuple): return for index in range(0, len(output_type.inner)): internal_input = output_type.inner[index] for other_clss in registry: annotations = _get_annotations(other_clss) other_input = annotations.input if not (conforms(internal_input, other_input) and other_clss != node): continue # `other_class` has input compatible with one element in the Tuple # build the output `Tuple[..., internal_output, ...]` of the wrapper class internal_output = annotations.output output_tuple = list(output_type.inner) output_tuple[index] = internal_output output_tuple_type = Tuple(*output_tuple) # dynamic class representing the wrapper algorithm if (index, output_type, output_tuple_type) in list_tuples: continue other_wrapper = build_composite_tuple( index, output_type, output_tuple_type ) list_tuples.add((index, output_type, output_tuple_type)) registry.append(other_wrapper) open_nodes.append(other_wrapper) G.add_edge(node, other_wrapper) G = Graph() open_nodes = [] closed_nodes = set() # Enqueue open nodes for clss in registry: if conforms(input, _get_annotations(clss).input): open_nodes.append(clss) G.add_edge(GraphSpace.Start, clss) connect_tuple_wrappers(GraphSpace.Start, input) if GraphSpace.Start not in G: raise TypeError("There are no classes compatible with input type:%r." % input) while open_nodes: clss = open_nodes.pop(0) if clss in closed_nodes: continue closed_nodes.add(clss) output_type = _get_annotations(clss).output for other_clss in registry: other_input = _get_annotations(other_clss).input if conforms(output_type, other_input) and other_clss != clss: open_nodes.append(other_clss) G.add_edge(clss, other_clss) connect_tuple_wrappers(clss, output_type) if conforms(output_type, output): G.add_edge(clss, GraphSpace.End) if GraphSpace.End not in G: raise TypeError( "No pipelines can be constructed from input:%r to output:%r." % (input, output) ) reachable_from_end = set(nx.dfs_preorder_nodes(G.reverse(False), GraphSpace.End)) unreachable_nodes = set(G.nodes) - reachable_from_end G.remove_nodes_from(unreachable_nodes) if not GraphSpace.Start in G: raise TypeError( "No pipelines can be constructed from input:%r to output:%r." % (input, output) ) return PipelineBuilder(G, registry)
def type_is_guaranteed(input_type): for other_type in types: if conforms(other_type, input_type): return True return False