We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ncon should take a BaseBackend object
Since we are now supporting passing `BaseBackend` to `Node` constructors, we should also
allow it in `ncon`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tensornetwork/ncon_interface.py`
Content:
```
1 # Copyright 2019 The TensorNetwork Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """NCON interface to TensorNetwork."""
15 
16 import warnings
17 from typing import Any, Sequence, List, Optional, Union, Text, Tuple, Dict
18 from tensornetwork import network_components
19 from tensornetwork.backend_contextmanager import get_default_backend
20 from tensornetwork.backends import backend_factory
21 Tensor = Any
22 
23 
24 def ncon(
25     tensors: Sequence[Union[network_components.BaseNode, Tensor]],
26     network_structure: Sequence[Sequence],
27     con_order: Optional[Sequence] = None,
28     out_order: Optional[Sequence] = None,
29     backend: Optional[Text] = None
30 ) -> Union[network_components.BaseNode, Tensor]:
31   r"""Contracts a list of tensors or nodes according to a tensor network 
32     specification.
33 
34     The network is provided as a list of lists, one for each
35     tensor, specifying labels for the edges connected to that tensor.
36 
37     If a contraction order `con_order` and an output order `out_order`
38     are both provided, the edge labels can be anything.
39     Otherwise (`con_order == None or out_order == None`), the edge labels 
40     must be nonzero integers and edges will be contracted in ascending order.
41     Negative integers denote the (dangling) indices of the output tensor,
42     which will be in descending order, e.g. `[-1,-2,-3,...]`.
43 
44     For example, matrix multiplication:
45 
46     .. code-block:: python
47 
48       A = np.array([[1.0, 2.0], [3.0, 4.0]])
49       B = np.array([[1.0, 1.0], [0.0, 1.0]])
50       ncon([A,B], [(-1, 1), (1, -2)])
51 
52     Matrix trace:
53 
54     .. code-block:: python
55 
56       A = np.array([[1.0, 2.0], [3.0, 4.0]])
57       ncon([A], [(1, 1)]) # 5.0
58 
59     Note: 
60       The reason `0` is not allowed as an edge label without manually
61       specifying the contraction order is to maintain compatibility with the
62       `original NCON implementation`_. However, the use of `0` in `con_order` 
63       to denote outer products is not (currently) 
64       supported in this implementation.
65     
66     .. _original NCON implementation:
67       https://arxiv.org/abs/1402.0939
68 
69     Args:
70       tensors: List of `Tensors` or `BaseNodes`.
71       network_structure: List of lists specifying the tensor network structure.
72       con_order: List of edge labels specifying the contraction order.
73       out_order: List of edge labels specifying the output order.
74       backend: String specifying the backend to use. Defaults to
75         `tensornetwork.backend_contextmanager.get_default_backend`.
76 
77     Returns:
78       The result of the contraction. The result is returned as a `Node`
79       if all elements of `tensors` are `BaseNode` objects, else
80       it is returned as a `Tensor` object.
81     """
82   if backend and (backend not in backend_factory._BACKENDS):
83     raise ValueError("Backend '{}' does not exist".format(backend))
84   if backend is None:
85     backend = get_default_backend()
86 
87   are_nodes = [isinstance(t, network_components.BaseNode) for t in tensors]
88   nodes = {t for t in tensors if isinstance(t, network_components.BaseNode)}
89   if not all([n.backend.name == backend for n in nodes]):
90     raise ValueError(
91         "Some nodes have backends different from '{}'".format(backend))
92 
93   _tensors = []
94   for t in tensors:
95     if isinstance(t, network_components.BaseNode):
96       _tensors.append(t.tensor)
97     else:
98       _tensors.append(t)
99 
100   nodes, con_edges, out_edges = ncon_network(
101       _tensors,
102       network_structure,
103       con_order=con_order,
104       out_order=out_order,
105       backend=backend)
106 
107   nodes = set(nodes)  # we don't need the ordering here
108 
109   # Reverse the list so we can pop from the end: O(1).
110   con_edges = con_edges[::-1]
111   while con_edges:
112     nodes_to_contract = con_edges[-1].get_nodes()
113     edges_to_contract = network_components.get_shared_edges(*nodes_to_contract)
114 
115     # Eat up all parallel edges that are adjacent in the ordering.
116     adjacent_parallel_edges = set()
117     for edge in reversed(con_edges):
118       if edge in edges_to_contract:
119         adjacent_parallel_edges.add(edge)
120       else:
121         break
122     con_edges = con_edges[:-len(adjacent_parallel_edges)]
123 
124     # In an optimal ordering, all edges connecting a given pair of nodes are
125     # adjacent in con_order. If this is not the case, warn the user.
126     leftovers = edges_to_contract - adjacent_parallel_edges
127     if leftovers:
128       warnings.warn(
129           "Suboptimal ordering detected. Edges {} are not adjacent in the "
130           "contraction order to edges {}, connecting nodes {}. Deviating from "
131           "the specified ordering!".format(
132               list(map(str, leftovers)),
133               list(map(str, adjacent_parallel_edges)),
134               list(map(str, nodes_to_contract))))
135       con_edges = [e for e in con_edges if e not in edges_to_contract]
136 
137     if set(nodes_to_contract) == nodes:
138       # This contraction produces the final output, so order the edges
139       # here to avoid transposes in some cases.
140       contraction_output_order = out_edges
141     else:
142       contraction_output_order = None
143 
144     nodes = nodes - set(nodes_to_contract)
145     nodes.add(
146         network_components.contract_between(
147             *nodes_to_contract,
148             name="con({},{})".format(*nodes_to_contract),
149             output_edge_order=contraction_output_order))
150 
151   # TODO: More efficient ordering of products based on out_edges
152   res_node = network_components.outer_product_final_nodes(nodes, out_edges)
153   if all(are_nodes):
154     return res_node
155   return res_node.tensor
156 
157 
158 def ncon_network(
159     tensors: Sequence[Tensor],
160     network_structure: Sequence[Sequence],
161     con_order: Optional[Sequence] = None,
162     out_order: Optional[Sequence] = None,
163     backend: Optional[Text] = None
164 ) -> Tuple[List[network_components.BaseNode], List[network_components.Edge],
165            List[network_components.Edge]]:
166   r"""Creates a network from a list of tensors according to `tensors`.
167 
168     The network is provided as a list of lists, one for each
169     tensor, specifying labels for the edges connected to that tensor.
170 
171     If a contraction order `con_order` and an output order `out_order`
172     are both provided, the edge labels can be anything.
173     Otherwise (`con_order == None or out_order == None`), the edge labels 
174     must be integers and edges will be contracted in ascending order.
175     Negative integers denote the (dangling) indices of the output tensor,
176     which will be in descending order, e.g. `[-1,-2,-3,...]`.
177 
178     This is used internally by `ncon()`.
179 
180     Args:
181       tensors: List of `Tensor`s.
182       network_structure: List of lists specifying the tensor network.
183       con_order: List of edge labels specifying the contraction order.
184       out_order: List of edge labels specifying the output order.
185       backend: String specifying the backend to use. Defaults to the default
186         TensorNetwork backend.
187 
188     Returns:
189       nodes: List of constructed nodes in the same order as given in `tensors`.
190       con_edges: List of internal `Edge` objects in contraction order.
191       out_edges: List of dangling `Edge` objects in output order.
192   """
193   if len(tensors) != len(network_structure):
194     raise ValueError('len(tensors) != len(network_structure)')
195 
196   nodes, edges = _build_network(tensors, network_structure, backend)
197 
198   if con_order is None:
199     try:
200       con_order = sorted((k for k in edges if k >= 0))
201       if con_order and con_order[0] == 0:
202         raise ValueError("'0' is not a valid edge label when the "
203                          "contraction order is not specified separately.")
204     except TypeError:
205       raise ValueError("Non-integer edge label(s): {}".format(
206           list(edges.keys())))
207   else:
208     if len(con_order) != len(set(con_order)):
209       raise ValueError("Duplicate labels in con_order: {}".format(con_order))
210 
211   if out_order is None:
212     try:
213       out_order = sorted((k for k in edges if k < 0), reverse=True)
214     except TypeError:
215       raise ValueError("Non-integer edge label(s): {}".format(
216           list(edges.keys())))
217   else:
218     if len(out_order) != len(set(out_order)):
219       raise ValueError("Duplicate labels in out_order: {}".format(out_order))
220 
221   try:
222     con_edges = [edges[k] for k in con_order]
223     out_edges = [edges[k] for k in out_order]
224   except KeyError as err:
225     raise ValueError("Order contained an unknown edge label: {}".format(
226         err.args[0]))
227 
228   if len(con_edges) + len(out_edges) != len(edges):
229     raise ValueError(
230         "Edges {} were not included in the contraction and output "
231         "ordering.".format(
232             list(set(edges.keys()) - set(con_order) - set(out_order))))
233 
234   for e in con_edges:
235     if e.is_dangling():
236       raise ValueError(
237           "Contraction edge {} appears only once in the network.".format(
238               str(e)))
239 
240   for e in out_edges:
241     if not e.is_dangling():
242       raise ValueError(
243           "Output edge {} appears more than once in the network.".format(
244               str(e)))
245 
246   return nodes, con_edges, out_edges
247 
248 
249 def _build_network(
250     tensors: Sequence[Tensor], network_structure: Sequence[Sequence],
251     backend: Text
252 ) -> Tuple[List[network_components.BaseNode], Dict[Any,
253                                                    network_components.Edge]]:
254   nodes = []
255   edges = {}
256   for i, (tensor, edge_lbls) in enumerate(zip(tensors, network_structure)):
257     if len(tensor.shape) != len(edge_lbls):
258       raise ValueError(
259           "Incorrect number of edge labels specified tensor {}".format(i))
260     if isinstance(tensor, network_components.BaseNode):
261       node = tensor
262     else:
263       node = network_components.Node(
264           tensor, name="tensor_{}".format(i), backend=backend)
265 
266     nodes.append(node)
267 
268     for (axis_num, edge_lbl) in enumerate(edge_lbls):
269       if edge_lbl not in edges:
270         e = node[axis_num]
271         e.set_name(str(edge_lbl))
272         edges[edge_lbl] = e
273       else:
274         # This will raise an error if the edges are not dangling.
275         e = network_components.connect(
276             edges[edge_lbl], node[axis_num], name=str(edge_lbl))
277         edges[edge_lbl] = e
278   return nodes, edges
279
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/tensornetwork/ncon_interface.py b/tensornetwork/ncon_interface.py
--- a/tensornetwork/ncon_interface.py
+++ b/tensornetwork/ncon_interface.py
@@ -18,6 +18,8 @@
 from tensornetwork import network_components
 from tensornetwork.backend_contextmanager import get_default_backend
 from tensornetwork.backends import backend_factory
+from tensornetwork.backends.base_backend import BaseBackend
+
 Tensor = Any
 
 
@@ -26,7 +28,7 @@
     network_structure: Sequence[Sequence],
     con_order: Optional[Sequence] = None,
     out_order: Optional[Sequence] = None,
-    backend: Optional[Text] = None
+    backend: Optional[Union[Text, BaseBackend]] = None
 ) -> Union[network_components.BaseNode, Tensor]:
   r"""Contracts a list of tensors or nodes according to a tensor network 
     specification.
@@ -79,16 +81,18 @@
       if all elements of `tensors` are `BaseNode` objects, else
       it is returned as a `Tensor` object.
     """
-  if backend and (backend not in backend_factory._BACKENDS):
-    raise ValueError("Backend '{}' does not exist".format(backend))
   if backend is None:
     backend = get_default_backend()
+  if isinstance(backend, BaseBackend):
+    backend_obj = backend
+  else:
+    backend_obj = backend_factory.get_backend(backend)
 
   are_nodes = [isinstance(t, network_components.BaseNode) for t in tensors]
   nodes = {t for t in tensors if isinstance(t, network_components.BaseNode)}
-  if not all([n.backend.name == backend for n in nodes]):
-    raise ValueError(
-        "Some nodes have backends different from '{}'".format(backend))
+  if not all([n.backend.name == backend_obj.name for n in nodes]):
+    raise ValueError("Some nodes have backends different from '{}'".format(
+        backend_obj.name))
 
   _tensors = []
   for t in tensors:
@@ -102,7 +106,7 @@
       network_structure,
       con_order=con_order,
       out_order=out_order,
-      backend=backend)
+      backend=backend_obj)
 
   nodes = set(nodes)  # we don't need the ordering here
 
@@ -160,7 +164,7 @@
     network_structure: Sequence[Sequence],
     con_order: Optional[Sequence] = None,
     out_order: Optional[Sequence] = None,
-    backend: Optional[Text] = None
+    backend: Optional[Union[Text, BaseBackend]] = None
 ) -> Tuple[List[network_components.BaseNode], List[network_components.Edge],
            List[network_components.Edge]]:
   r"""Creates a network from a list of tensors according to `tensors`.
@@ -182,9 +186,8 @@
       network_structure: List of lists specifying the tensor network.
       con_order: List of edge labels specifying the contraction order.
       out_order: List of edge labels specifying the output order.
-      backend: String specifying the backend to use. Defaults to the default
-        TensorNetwork backend.
-
+      backend: String or BaseBackend object specifying the backend to use. 
+        Defaults to the default TensorNetwork backend.
     Returns:
       nodes: List of constructed nodes in the same order as given in `tensors`.
       con_edges: List of internal `Edge` objects in contraction order.
@@ -247,8 +250,9 @@
 
 
 def _build_network(
-    tensors: Sequence[Tensor], network_structure: Sequence[Sequence],
-    backend: Text
+    tensors: Sequence[Tensor],
+    network_structure: Sequence[Sequence],
+    backend: Optional[Union[BaseBackend, Text]] = None,
 ) -> Tuple[List[network_components.BaseNode], Dict[Any,
                                                    network_components.Edge]]:
   nodes = []

{"golden_diff": "diff --git a/tensornetwork/ncon_interface.py b/tensornetwork/ncon_interface.py\n--- a/tensornetwork/ncon_interface.py\n+++ b/tensornetwork/ncon_interface.py\n@@ -18,6 +18,8 @@\n from tensornetwork import network_components\n from tensornetwork.backend_contextmanager import get_default_backend\n from tensornetwork.backends import backend_factory\n+from tensornetwork.backends.base_backend import BaseBackend\n+\n Tensor = Any\n \n \n@@ -26,7 +28,7 @@\n     network_structure: Sequence[Sequence],\n     con_order: Optional[Sequence] = None,\n     out_order: Optional[Sequence] = None,\n-    backend: Optional[Text] = None\n+    backend: Optional[Union[Text, BaseBackend]] = None\n ) -> Union[network_components.BaseNode, Tensor]:\n   r\"\"\"Contracts a list of tensors or nodes according to a tensor network \n     specification.\n@@ -79,16 +81,18 @@\n       if all elements of `tensors` are `BaseNode` objects, else\n       it is returned as a `Tensor` object.\n     \"\"\"\n-  if backend and (backend not in backend_factory._BACKENDS):\n-    raise ValueError(\"Backend '{}' does not exist\".format(backend))\n   if backend is None:\n     backend = get_default_backend()\n+  if isinstance(backend, BaseBackend):\n+    backend_obj = backend\n+  else:\n+    backend_obj = backend_factory.get_backend(backend)\n \n   are_nodes = [isinstance(t, network_components.BaseNode) for t in tensors]\n   nodes = {t for t in tensors if isinstance(t, network_components.BaseNode)}\n-  if not all([n.backend.name == backend for n in nodes]):\n-    raise ValueError(\n-        \"Some nodes have backends different from '{}'\".format(backend))\n+  if not all([n.backend.name == backend_obj.name for n in nodes]):\n+    raise ValueError(\"Some nodes have backends different from '{}'\".format(\n+        backend_obj.name))\n \n   _tensors = []\n   for t in tensors:\n@@ -102,7 +106,7 @@\n       network_structure,\n       con_order=con_order,\n       out_order=out_order,\n-      backend=backend)\n+      backend=backend_obj)\n \n   nodes = set(nodes)  # we don't need the ordering here\n \n@@ -160,7 +164,7 @@\n     network_structure: Sequence[Sequence],\n     con_order: Optional[Sequence] = None,\n     out_order: Optional[Sequence] = None,\n-    backend: Optional[Text] = None\n+    backend: Optional[Union[Text, BaseBackend]] = None\n ) -> Tuple[List[network_components.BaseNode], List[network_components.Edge],\n            List[network_components.Edge]]:\n   r\"\"\"Creates a network from a list of tensors according to `tensors`.\n@@ -182,9 +186,8 @@\n       network_structure: List of lists specifying the tensor network.\n       con_order: List of edge labels specifying the contraction order.\n       out_order: List of edge labels specifying the output order.\n-      backend: String specifying the backend to use. Defaults to the default\n-        TensorNetwork backend.\n-\n+      backend: String or BaseBackend object specifying the backend to use. \n+        Defaults to the default TensorNetwork backend.\n     Returns:\n       nodes: List of constructed nodes in the same order as given in `tensors`.\n       con_edges: List of internal `Edge` objects in contraction order.\n@@ -247,8 +250,9 @@\n \n \n def _build_network(\n-    tensors: Sequence[Tensor], network_structure: Sequence[Sequence],\n-    backend: Text\n+    tensors: Sequence[Tensor],\n+    network_structure: Sequence[Sequence],\n+    backend: Optional[Union[BaseBackend, Text]] = None,\n ) -> Tuple[List[network_components.BaseNode], Dict[Any,\n                                                    network_components.Edge]]:\n   nodes = []\n", "issue": "ncon should take a BaseBackend object\nSince we are now supporting passing `BaseBackend` to `Node` constructors, we should also\r\nallow it in `ncon`\n", "before_files": [{"content": "# Copyright 2019 The TensorNetwork Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"NCON interface to TensorNetwork.\"\"\"\n\nimport warnings\nfrom typing import Any, Sequence, List, Optional, Union, Text, Tuple, Dict\nfrom tensornetwork import network_components\nfrom tensornetwork.backend_contextmanager import get_default_backend\nfrom tensornetwork.backends import backend_factory\nTensor = Any\n\n\ndef ncon(\n    tensors: Sequence[Union[network_components.BaseNode, Tensor]],\n    network_structure: Sequence[Sequence],\n    con_order: Optional[Sequence] = None,\n    out_order: Optional[Sequence] = None,\n    backend: Optional[Text] = None\n) -> Union[network_components.BaseNode, Tensor]:\n  r\"\"\"Contracts a list of tensors or nodes according to a tensor network \n    specification.\n\n    The network is provided as a list of lists, one for each\n    tensor, specifying labels for the edges connected to that tensor.\n\n    If a contraction order `con_order` and an output order `out_order`\n    are both provided, the edge labels can be anything.\n    Otherwise (`con_order == None or out_order == None`), the edge labels \n    must be nonzero integers and edges will be contracted in ascending order.\n    Negative integers denote the (dangling) indices of the output tensor,\n    which will be in descending order, e.g. `[-1,-2,-3,...]`.\n\n    For example, matrix multiplication:\n\n    .. code-block:: python\n\n      A = np.array([[1.0, 2.0], [3.0, 4.0]])\n      B = np.array([[1.0, 1.0], [0.0, 1.0]])\n      ncon([A,B], [(-1, 1), (1, -2)])\n\n    Matrix trace:\n\n    .. code-block:: python\n\n      A = np.array([[1.0, 2.0], [3.0, 4.0]])\n      ncon([A], [(1, 1)]) # 5.0\n\n    Note: \n      The reason `0` is not allowed as an edge label without manually\n      specifying the contraction order is to maintain compatibility with the\n      `original NCON implementation`_. However, the use of `0` in `con_order` \n      to denote outer products is not (currently) \n      supported in this implementation.\n    \n    .. _original NCON implementation:\n      https://arxiv.org/abs/1402.0939\n\n    Args:\n      tensors: List of `Tensors` or `BaseNodes`.\n      network_structure: List of lists specifying the tensor network structure.\n      con_order: List of edge labels specifying the contraction order.\n      out_order: List of edge labels specifying the output order.\n      backend: String specifying the backend to use. Defaults to\n        `tensornetwork.backend_contextmanager.get_default_backend`.\n\n    Returns:\n      The result of the contraction. The result is returned as a `Node`\n      if all elements of `tensors` are `BaseNode` objects, else\n      it is returned as a `Tensor` object.\n    \"\"\"\n  if backend and (backend not in backend_factory._BACKENDS):\n    raise ValueError(\"Backend '{}' does not exist\".format(backend))\n  if backend is None:\n    backend = get_default_backend()\n\n  are_nodes = [isinstance(t, network_components.BaseNode) for t in tensors]\n  nodes = {t for t in tensors if isinstance(t, network_components.BaseNode)}\n  if not all([n.backend.name == backend for n in nodes]):\n    raise ValueError(\n        \"Some nodes have backends different from '{}'\".format(backend))\n\n  _tensors = []\n  for t in tensors:\n    if isinstance(t, network_components.BaseNode):\n      _tensors.append(t.tensor)\n    else:\n      _tensors.append(t)\n\n  nodes, con_edges, out_edges = ncon_network(\n      _tensors,\n      network_structure,\n      con_order=con_order,\n      out_order=out_order,\n      backend=backend)\n\n  nodes = set(nodes)  # we don't need the ordering here\n\n  # Reverse the list so we can pop from the end: O(1).\n  con_edges = con_edges[::-1]\n  while con_edges:\n    nodes_to_contract = con_edges[-1].get_nodes()\n    edges_to_contract = network_components.get_shared_edges(*nodes_to_contract)\n\n    # Eat up all parallel edges that are adjacent in the ordering.\n    adjacent_parallel_edges = set()\n    for edge in reversed(con_edges):\n      if edge in edges_to_contract:\n        adjacent_parallel_edges.add(edge)\n      else:\n        break\n    con_edges = con_edges[:-len(adjacent_parallel_edges)]\n\n    # In an optimal ordering, all edges connecting a given pair of nodes are\n    # adjacent in con_order. If this is not the case, warn the user.\n    leftovers = edges_to_contract - adjacent_parallel_edges\n    if leftovers:\n      warnings.warn(\n          \"Suboptimal ordering detected. Edges {} are not adjacent in the \"\n          \"contraction order to edges {}, connecting nodes {}. Deviating from \"\n          \"the specified ordering!\".format(\n              list(map(str, leftovers)),\n              list(map(str, adjacent_parallel_edges)),\n              list(map(str, nodes_to_contract))))\n      con_edges = [e for e in con_edges if e not in edges_to_contract]\n\n    if set(nodes_to_contract) == nodes:\n      # This contraction produces the final output, so order the edges\n      # here to avoid transposes in some cases.\n      contraction_output_order = out_edges\n    else:\n      contraction_output_order = None\n\n    nodes = nodes - set(nodes_to_contract)\n    nodes.add(\n        network_components.contract_between(\n            *nodes_to_contract,\n            name=\"con({},{})\".format(*nodes_to_contract),\n            output_edge_order=contraction_output_order))\n\n  # TODO: More efficient ordering of products based on out_edges\n  res_node = network_components.outer_product_final_nodes(nodes, out_edges)\n  if all(are_nodes):\n    return res_node\n  return res_node.tensor\n\n\ndef ncon_network(\n    tensors: Sequence[Tensor],\n    network_structure: Sequence[Sequence],\n    con_order: Optional[Sequence] = None,\n    out_order: Optional[Sequence] = None,\n    backend: Optional[Text] = None\n) -> Tuple[List[network_components.BaseNode], List[network_components.Edge],\n           List[network_components.Edge]]:\n  r\"\"\"Creates a network from a list of tensors according to `tensors`.\n\n    The network is provided as a list of lists, one for each\n    tensor, specifying labels for the edges connected to that tensor.\n\n    If a contraction order `con_order` and an output order `out_order`\n    are both provided, the edge labels can be anything.\n    Otherwise (`con_order == None or out_order == None`), the edge labels \n    must be integers and edges will be contracted in ascending order.\n    Negative integers denote the (dangling) indices of the output tensor,\n    which will be in descending order, e.g. `[-1,-2,-3,...]`.\n\n    This is used internally by `ncon()`.\n\n    Args:\n      tensors: List of `Tensor`s.\n      network_structure: List of lists specifying the tensor network.\n      con_order: List of edge labels specifying the contraction order.\n      out_order: List of edge labels specifying the output order.\n      backend: String specifying the backend to use. Defaults to the default\n        TensorNetwork backend.\n\n    Returns:\n      nodes: List of constructed nodes in the same order as given in `tensors`.\n      con_edges: List of internal `Edge` objects in contraction order.\n      out_edges: List of dangling `Edge` objects in output order.\n  \"\"\"\n  if len(tensors) != len(network_structure):\n    raise ValueError('len(tensors) != len(network_structure)')\n\n  nodes, edges = _build_network(tensors, network_structure, backend)\n\n  if con_order is None:\n    try:\n      con_order = sorted((k for k in edges if k >= 0))\n      if con_order and con_order[0] == 0:\n        raise ValueError(\"'0' is not a valid edge label when the \"\n                         \"contraction order is not specified separately.\")\n    except TypeError:\n      raise ValueError(\"Non-integer edge label(s): {}\".format(\n          list(edges.keys())))\n  else:\n    if len(con_order) != len(set(con_order)):\n      raise ValueError(\"Duplicate labels in con_order: {}\".format(con_order))\n\n  if out_order is None:\n    try:\n      out_order = sorted((k for k in edges if k < 0), reverse=True)\n    except TypeError:\n      raise ValueError(\"Non-integer edge label(s): {}\".format(\n          list(edges.keys())))\n  else:\n    if len(out_order) != len(set(out_order)):\n      raise ValueError(\"Duplicate labels in out_order: {}\".format(out_order))\n\n  try:\n    con_edges = [edges[k] for k in con_order]\n    out_edges = [edges[k] for k in out_order]\n  except KeyError as err:\n    raise ValueError(\"Order contained an unknown edge label: {}\".format(\n        err.args[0]))\n\n  if len(con_edges) + len(out_edges) != len(edges):\n    raise ValueError(\n        \"Edges {} were not included in the contraction and output \"\n        \"ordering.\".format(\n            list(set(edges.keys()) - set(con_order) - set(out_order))))\n\n  for e in con_edges:\n    if e.is_dangling():\n      raise ValueError(\n          \"Contraction edge {} appears only once in the network.\".format(\n              str(e)))\n\n  for e in out_edges:\n    if not e.is_dangling():\n      raise ValueError(\n          \"Output edge {} appears more than once in the network.\".format(\n              str(e)))\n\n  return nodes, con_edges, out_edges\n\n\ndef _build_network(\n    tensors: Sequence[Tensor], network_structure: Sequence[Sequence],\n    backend: Text\n) -> Tuple[List[network_components.BaseNode], Dict[Any,\n                                                   network_components.Edge]]:\n  nodes = []\n  edges = {}\n  for i, (tensor, edge_lbls) in enumerate(zip(tensors, network_structure)):\n    if len(tensor.shape) != len(edge_lbls):\n      raise ValueError(\n          \"Incorrect number of edge labels specified tensor {}\".format(i))\n    if isinstance(tensor, network_components.BaseNode):\n      node = tensor\n    else:\n      node = network_components.Node(\n          tensor, name=\"tensor_{}\".format(i), backend=backend)\n\n    nodes.append(node)\n\n    for (axis_num, edge_lbl) in enumerate(edge_lbls):\n      if edge_lbl not in edges:\n        e = node[axis_num]\n        e.set_name(str(edge_lbl))\n        edges[edge_lbl] = e\n      else:\n        # This will raise an error if the edges are not dangling.\n        e = network_components.connect(\n            edges[edge_lbl], node[axis_num], name=str(edge_lbl))\n        edges[edge_lbl] = e\n  return nodes, edges\n", "path": "tensornetwork/ncon_interface.py"}], "after_files": [{"content": "# Copyright 2019 The TensorNetwork Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"NCON interface to TensorNetwork.\"\"\"\n\nimport warnings\nfrom typing import Any, Sequence, List, Optional, Union, Text, Tuple, Dict\nfrom tensornetwork import network_components\nfrom tensornetwork.backend_contextmanager import get_default_backend\nfrom tensornetwork.backends import backend_factory\nfrom tensornetwork.backends.base_backend import BaseBackend\n\nTensor = Any\n\n\ndef ncon(\n    tensors: Sequence[Union[network_components.BaseNode, Tensor]],\n    network_structure: Sequence[Sequence],\n    con_order: Optional[Sequence] = None,\n    out_order: Optional[Sequence] = None,\n    backend: Optional[Union[Text, BaseBackend]] = None\n) -> Union[network_components.BaseNode, Tensor]:\n  r\"\"\"Contracts a list of tensors or nodes according to a tensor network \n    specification.\n\n    The network is provided as a list of lists, one for each\n    tensor, specifying labels for the edges connected to that tensor.\n\n    If a contraction order `con_order` and an output order `out_order`\n    are both provided, the edge labels can be anything.\n    Otherwise (`con_order == None or out_order == None`), the edge labels \n    must be nonzero integers and edges will be contracted in ascending order.\n    Negative integers denote the (dangling) indices of the output tensor,\n    which will be in descending order, e.g. `[-1,-2,-3,...]`.\n\n    For example, matrix multiplication:\n\n    .. code-block:: python\n\n      A = np.array([[1.0, 2.0], [3.0, 4.0]])\n      B = np.array([[1.0, 1.0], [0.0, 1.0]])\n      ncon([A,B], [(-1, 1), (1, -2)])\n\n    Matrix trace:\n\n    .. code-block:: python\n\n      A = np.array([[1.0, 2.0], [3.0, 4.0]])\n      ncon([A], [(1, 1)]) # 5.0\n\n    Note: \n      The reason `0` is not allowed as an edge label without manually\n      specifying the contraction order is to maintain compatibility with the\n      `original NCON implementation`_. However, the use of `0` in `con_order` \n      to denote outer products is not (currently) \n      supported in this implementation.\n    \n    .. _original NCON implementation:\n      https://arxiv.org/abs/1402.0939\n\n    Args:\n      tensors: List of `Tensors` or `BaseNodes`.\n      network_structure: List of lists specifying the tensor network structure.\n      con_order: List of edge labels specifying the contraction order.\n      out_order: List of edge labels specifying the output order.\n      backend: String specifying the backend to use. Defaults to\n        `tensornetwork.backend_contextmanager.get_default_backend`.\n\n    Returns:\n      The result of the contraction. The result is returned as a `Node`\n      if all elements of `tensors` are `BaseNode` objects, else\n      it is returned as a `Tensor` object.\n    \"\"\"\n  if backend is None:\n    backend = get_default_backend()\n  if isinstance(backend, BaseBackend):\n    backend_obj = backend\n  else:\n    backend_obj = backend_factory.get_backend(backend)\n\n  are_nodes = [isinstance(t, network_components.BaseNode) for t in tensors]\n  nodes = {t for t in tensors if isinstance(t, network_components.BaseNode)}\n  if not all([n.backend.name == backend_obj.name for n in nodes]):\n    raise ValueError(\"Some nodes have backends different from '{}'\".format(\n        backend_obj.name))\n\n  _tensors = []\n  for t in tensors:\n    if isinstance(t, network_components.BaseNode):\n      _tensors.append(t.tensor)\n    else:\n      _tensors.append(t)\n\n  nodes, con_edges, out_edges = ncon_network(\n      _tensors,\n      network_structure,\n      con_order=con_order,\n      out_order=out_order,\n      backend=backend_obj)\n\n  nodes = set(nodes)  # we don't need the ordering here\n\n  # Reverse the list so we can pop from the end: O(1).\n  con_edges = con_edges[::-1]\n  while con_edges:\n    nodes_to_contract = con_edges[-1].get_nodes()\n    edges_to_contract = network_components.get_shared_edges(*nodes_to_contract)\n\n    # Eat up all parallel edges that are adjacent in the ordering.\n    adjacent_parallel_edges = set()\n    for edge in reversed(con_edges):\n      if edge in edges_to_contract:\n        adjacent_parallel_edges.add(edge)\n      else:\n        break\n    con_edges = con_edges[:-len(adjacent_parallel_edges)]\n\n    # In an optimal ordering, all edges connecting a given pair of nodes are\n    # adjacent in con_order. If this is not the case, warn the user.\n    leftovers = edges_to_contract - adjacent_parallel_edges\n    if leftovers:\n      warnings.warn(\n          \"Suboptimal ordering detected. Edges {} are not adjacent in the \"\n          \"contraction order to edges {}, connecting nodes {}. Deviating from \"\n          \"the specified ordering!\".format(\n              list(map(str, leftovers)),\n              list(map(str, adjacent_parallel_edges)),\n              list(map(str, nodes_to_contract))))\n      con_edges = [e for e in con_edges if e not in edges_to_contract]\n\n    if set(nodes_to_contract) == nodes:\n      # This contraction produces the final output, so order the edges\n      # here to avoid transposes in some cases.\n      contraction_output_order = out_edges\n    else:\n      contraction_output_order = None\n\n    nodes = nodes - set(nodes_to_contract)\n    nodes.add(\n        network_components.contract_between(\n            *nodes_to_contract,\n            name=\"con({},{})\".format(*nodes_to_contract),\n            output_edge_order=contraction_output_order))\n\n  # TODO: More efficient ordering of products based on out_edges\n  res_node = network_components.outer_product_final_nodes(nodes, out_edges)\n  if all(are_nodes):\n    return res_node\n  return res_node.tensor\n\n\ndef ncon_network(\n    tensors: Sequence[Tensor],\n    network_structure: Sequence[Sequence],\n    con_order: Optional[Sequence] = None,\n    out_order: Optional[Sequence] = None,\n    backend: Optional[Union[Text, BaseBackend]] = None\n) -> Tuple[List[network_components.BaseNode], List[network_components.Edge],\n           List[network_components.Edge]]:\n  r\"\"\"Creates a network from a list of tensors according to `tensors`.\n\n    The network is provided as a list of lists, one for each\n    tensor, specifying labels for the edges connected to that tensor.\n\n    If a contraction order `con_order` and an output order `out_order`\n    are both provided, the edge labels can be anything.\n    Otherwise (`con_order == None or out_order == None`), the edge labels \n    must be integers and edges will be contracted in ascending order.\n    Negative integers denote the (dangling) indices of the output tensor,\n    which will be in descending order, e.g. `[-1,-2,-3,...]`.\n\n    This is used internally by `ncon()`.\n\n    Args:\n      tensors: List of `Tensor`s.\n      network_structure: List of lists specifying the tensor network.\n      con_order: List of edge labels specifying the contraction order.\n      out_order: List of edge labels specifying the output order.\n      backend: String or BaseBackend object specifying the backend to use. \n        Defaults to the default TensorNetwork backend.\n    Returns:\n      nodes: List of constructed nodes in the same order as given in `tensors`.\n      con_edges: List of internal `Edge` objects in contraction order.\n      out_edges: List of dangling `Edge` objects in output order.\n  \"\"\"\n  if len(tensors) != len(network_structure):\n    raise ValueError('len(tensors) != len(network_structure)')\n\n  nodes, edges = _build_network(tensors, network_structure, backend)\n\n  if con_order is None:\n    try:\n      con_order = sorted((k for k in edges if k >= 0))\n      if con_order and con_order[0] == 0:\n        raise ValueError(\"'0' is not a valid edge label when the \"\n                         \"contraction order is not specified separately.\")\n    except TypeError:\n      raise ValueError(\"Non-integer edge label(s): {}\".format(\n          list(edges.keys())))\n  else:\n    if len(con_order) != len(set(con_order)):\n      raise ValueError(\"Duplicate labels in con_order: {}\".format(con_order))\n\n  if out_order is None:\n    try:\n      out_order = sorted((k for k in edges if k < 0), reverse=True)\n    except TypeError:\n      raise ValueError(\"Non-integer edge label(s): {}\".format(\n          list(edges.keys())))\n  else:\n    if len(out_order) != len(set(out_order)):\n      raise ValueError(\"Duplicate labels in out_order: {}\".format(out_order))\n\n  try:\n    con_edges = [edges[k] for k in con_order]\n    out_edges = [edges[k] for k in out_order]\n  except KeyError as err:\n    raise ValueError(\"Order contained an unknown edge label: {}\".format(\n        err.args[0]))\n\n  if len(con_edges) + len(out_edges) != len(edges):\n    raise ValueError(\n        \"Edges {} were not included in the contraction and output \"\n        \"ordering.\".format(\n            list(set(edges.keys()) - set(con_order) - set(out_order))))\n\n  for e in con_edges:\n    if e.is_dangling():\n      raise ValueError(\n          \"Contraction edge {} appears only once in the network.\".format(\n              str(e)))\n\n  for e in out_edges:\n    if not e.is_dangling():\n      raise ValueError(\n          \"Output edge {} appears more than once in the network.\".format(\n              str(e)))\n\n  return nodes, con_edges, out_edges\n\n\ndef _build_network(\n    tensors: Sequence[Tensor],\n    network_structure: Sequence[Sequence],\n    backend: Optional[Union[BaseBackend, Text]] = None,\n) -> Tuple[List[network_components.BaseNode], Dict[Any,\n                                                   network_components.Edge]]:\n  nodes = []\n  edges = {}\n  for i, (tensor, edge_lbls) in enumerate(zip(tensors, network_structure)):\n    if len(tensor.shape) != len(edge_lbls):\n      raise ValueError(\n          \"Incorrect number of edge labels specified tensor {}\".format(i))\n    if isinstance(tensor, network_components.BaseNode):\n      node = tensor\n    else:\n      node = network_components.Node(\n          tensor, name=\"tensor_{}\".format(i), backend=backend)\n\n    nodes.append(node)\n\n    for (axis_num, edge_lbl) in enumerate(edge_lbls):\n      if edge_lbl not in edges:\n        e = node[axis_num]\n        e.set_name(str(edge_lbl))\n        edges[edge_lbl] = e\n      else:\n        # This will raise an error if the edges are not dangling.\n        e = network_components.connect(\n            edges[edge_lbl], node[axis_num], name=str(edge_lbl))\n        edges[edge_lbl] = e\n  return nodes, edges\n", "path": "tensornetwork/ncon_interface.py"}]}