def contract_path(path: Tuple[List[Tuple[int, int]]], nodes: Iterable[AbstractNode], output_edge_order: Sequence[Edge]) -> AbstractNode: """Contract `nodes` using `path`. Args: path: The contraction path as returned from `path_solver`. nodes: A collection of connected nodes. output_edge_order: A list of edges. Edges of the final node in `nodes` are reordered into `output_edge_order`; Returns: Final node after full contraction. """ if len(path) == 0: return nodes for a, b in path: new_node = contract_between(nodes[a], nodes[b], allow_outer_product=True) nodes.append(new_node) nodes = utils.multi_remove(nodes, [a, b]) # if the final node has more than one edge, # output_edge_order has to be specified final_node = nodes[0] # nodes were connected, we checked this final_node.reorder_edges(output_edge_order) return final_node
def base(net: network.TensorNetwork, algorithm: Callable[[List[Set[int]], Set[int], Dict[int, int]], List] ) -> network.TensorNetwork: """Base method for all `opt_einsum` contractors. Args: net: a TensorNetwork object. Should be connected. algorithm: `opt_einsum` contraction method to use. Returns: The network after full contraction. """ net.check_connected() # First contract all trace edges edges = net.get_all_nondangling() for edge in edges: if edge in net and edge.is_trace(): net.contract_parallel(edge) if not net.get_all_nondangling(): # There's nothing to contract. return net # Then apply `opt_einsum`'s algorithm nodes = sorted(net.nodes_set) input_sets = utils.get_input_sets(net) output_set = utils.get_output_set(net) size_dict = utils.get_size_dict(net) path = algorithm(input_sets, output_set, size_dict) for a, b in path: new_node = nodes[a] @ nodes[b] nodes.append(new_node) nodes = utils.multi_remove(nodes, [a, b]) return net
def _base_nodes( nodes: Iterable[BaseNode], algorithm: utils.Algorithm, output_edge_order: Optional[Sequence[Edge]] = None) -> BaseNode: """Base method for all `opt_einsum` contractors. Args: nodes: A collection of connected nodes. algorithm: `opt_einsum` contraction method to use. output_edge_order: An optional list of edges. Edges of the final node in `nodes_set` are reordered into `output_edge_order`; if final node has more than one edge, `output_edge_order` must be pronvided. Returns: Final node after full contraction. """ nodes_set = set(nodes) check_connected(nodes_set) edges = get_all_edges(nodes_set) #output edge order has to be determinded before any contraction #(edges are refreshed after contractions) if output_edge_order is None: output_edge_order = list(get_subgraph_dangling(nodes)) if len(output_edge_order) > 1: raise ValueError( "The final node after contraction has more than " "one remaining edge. In this case `output_edge_order` " "has to be provided.") if set(output_edge_order) != get_subgraph_dangling(nodes): raise ValueError("output edges are not equal to the remaining " "non-contracted edges of the final node.") for edge in edges: if not edge.is_disabled: #if its disabled we already contracted it if edge.is_trace(): nodes_set.remove(edge.node1) nodes_set.add(contract_parallel(edge)) if len(nodes_set) == 1: # There's nothing to contract. return list(nodes_set)[0].reorder_edges(output_edge_order) # Then apply `opt_einsum`'s algorithm path, nodes = utils.get_path(nodes_set, algorithm) for a, b in path: new_node = nodes[a] @ nodes[b] nodes.append(new_node) nodes = utils.multi_remove(nodes, [a, b]) # if the final node has more than one edge, # output_edge_order has to be specified final_node = nodes[0] # nodes were connected, we checked this final_node.reorder_edges(output_edge_order) return final_node
def base( net: network.TensorNetwork, algorithm: Callable[[List[Set[int]], Set[int], Dict[int, int]], List], output_edge_order: Optional[Sequence[network_components.Edge]] = None ) -> network.TensorNetwork: """Base method for all `opt_einsum` contractors. Args: net: a TensorNetwork object. Should be connected. algorithm: `opt_einsum` contraction method to use. output_edge_order: An optional list of edges. Edges of the final node in `nodes_set` are reordered into `output_edge_order`; if final node has more than one edge, `output_edge_order` must be provided. Returns: The network after full contraction. """ net.check_connected() # First contract all trace edges edges = net.get_all_nondangling() for edge in edges: if edge in net and edge.is_trace(): net.contract_parallel(edge) if not net.get_all_nondangling(): # There's nothing to contract. return net # Then apply `opt_einsum`'s algorithm nodes = sorted(net.nodes_set) input_sets = utils.get_input_sets(net) output_set = utils.get_output_set(net) size_dict = utils.get_size_dict(net) path = algorithm(input_sets, output_set, size_dict) for a, b in path: new_node = nodes[a] @ nodes[b] nodes.append(new_node) nodes = utils.multi_remove(nodes, [a, b]) # if the final node has more than one edge, # output_edge_order has to be specified final_node = net.get_final_node() if (len(final_node.edges) <= 1) and (output_edge_order is None): output_edge_order = list( (net.get_all_edges() - net.get_all_nondangling())) elif (len(final_node.edges) > 1) and (output_edge_order is None): raise ValueError("if the final node has more than one dangling edge" " `output_edge_order` has to be provided") if set(output_edge_order) != (net.get_all_edges() - net.get_all_nondangling()): raise ValueError("output edges are not all dangling.") final_node.reorder_edges(output_edge_order) return net
def _base_network(net: TensorNetwork, algorithm: utils.Algorithm, output_edge_order: Optional[Sequence[Edge]] = None, ignore_edge_order: bool = False) -> TensorNetwork: """Base method for all `opt_einsum` contractors. Args: net: a TensorNetwork object. Should be connected. algorithm: `opt_einsum` contraction method to use. output_edge_order: An optional list of edges. Edges of the final node in `nodes_set` are reordered into `output_edge_order`; if final node has more than one edge, `output_edge_order` must be provided. ignore_edge_order: An option to ignore the output edge order. Returns: The network after full contraction. """ net.check_connected() # First contract all trace edges edges = net.get_all_nondangling() for edge in edges: if edge in net and edge.is_trace(): net.contract_parallel(edge) if not net.get_all_nondangling(): # There's nothing to contract. return net # Then apply `opt_einsum`'s algorithm path, nodes = utils.get_path(net, algorithm) for a, b in path: new_node = nodes[a] @ nodes[b] nodes.append(new_node) nodes = utils.multi_remove(nodes, [a, b]) # if the final node has more than one edge, # output_edge_order has to be specified final_node = net.get_final_node() if not ignore_edge_order: if (len(final_node.edges) <= 1) and (output_edge_order is None): output_edge_order = list( (net.get_all_edges() - net.get_all_nondangling())) elif (len(final_node.edges) > 1) and (output_edge_order is None): raise ValueError( "The final node after contraction has more than " "one dangling edge. In this case `output_edge_order` " "has to be provided.") if set(output_edge_order) != (net.get_all_edges() - net.get_all_nondangling()): raise ValueError("output edges are not all dangling.") final_node.reorder_edges(output_edge_order) return net
def contract_path(path: Tuple[List[Tuple[int, int]]], nodes: Iterable[AbstractNode], output_edge_order: Sequence[Edge]) -> AbstractNode: """Contract `nodes` using `path`. Args: path: The contraction path as returned from `path_solver`. nodes: A collection of connected nodes. output_edge_order: A list of edges. Edges of the final node in `nodes` are reordered into `output_edge_order`; Returns: Final node after full contraction. """ edges = get_all_edges(nodes) for edge in edges: if not edge.is_disabled: #if its disabled we already contracted it if edge.is_trace(): contract_parallel(edge) if len(nodes) == 1: newnode = nodes[0].copy() for edge in nodes[0].edges: redirect_edge(edge, newnode, nodes[0]) return newnode.reorder_edges(output_edge_order) if len(path) == 0: return nodes for p in path: if len(p) > 1: a, b = p new_node = contract_between(nodes[a], nodes[b], allow_outer_product=True) nodes.append(new_node) nodes = utils.multi_remove(nodes, [a, b]) elif len(p) == 1: a = p[0] node = nodes.pop(a) new_node = contract_trace_edges(node) nodes.append(new_node) # if the final node has more than one edge, # output_edge_order has to be specified final_node = nodes[0] # nodes were connected, we checked this #some contractors miss trace edges final_node = contract_trace_edges(final_node) final_node.reorder_edges(output_edge_order) return final_node