Repository: google/TensorNetwork

Issue:
Add better __repr__ for Node class
We don't have a good repr for the `Node` class yet. 


Discussion:
@Thenerdstation How do you want the representation to be ?
Good question! I think we should discuss it on here.

Ideally we should print these things: the node's name, it's underlying tensor value, its edges and how it's connected to other Nodes.

I'm open to hearing ideas about how it should be formatted 
Hey @beinganonymous @Thenerdstation mind if I look into this? :)
Sure go ahead I was going to ask to reassign this to someone else anyways. @drigio
Go for it!
Thanks! 
```
Node( tensor : array([[[1.]]]), name : 'Node A', edges : [Edge( 'Node A'->'Node B' ), Edge( 'Node A'->'Node C' ), Edge( Dangling Edge )] )
Node( tensor : array([[[1.]]]), name : 'Node B', edges : [Edge( 'Node A'->'Node B' ), Edge( Dangling Edge ), Edge( Dangling Edge )] )
Node( tensor : array([[[1.]]]), name : 'Node C', edges : [Edge( 'Node A'->'Node C' ), Edge( Dangling Edge ), Edge( Dangling Edge )] )
Node( tensor : array([[1.]]), name : '__unnamed_node__', edges : [Edge( Dangling Edge ), Edge( Dangling Edge )] )
```
@Thenerdstation How about such a format? What are your thoughts?
Looks pretty nice!

Here is what I would change:

1. For the edges, be sure to include the axis numbers/names
2. Perhaps make this multiple lines?

Thanks for taking time in looking into this!
I don't have an idea about how to display the axis numbers. By far what I've achieved is : 
```
Node
(
name : 'Node A',
tensor : 
array([[1.],
       [1.]]),
edges : [
Edge('Node A'->'Node B' ) Axis 1 : 0, Axis 2 : 0 
, 
Edge('Node A'->'Node B' ) Axis 1 : 1, Axis 2 : 1 
] 
)

Node
(
name : 'Node B',
tensor : 
array([[1.],
       [1.]]),
edges : [
Edge('Node A'->'Node B' ) Axis 1 : 0, Axis 2 : 0 
, 
Edge('Node A'->'Node B' ) Axis 1 : 1, Axis 2 : 1 
] 
)

Node
(
name : '__unnamed_node__',
tensor : 
array([[1.]]),
edges : [
Edge( Dangling Edge )
, 
Edge( Dangling Edge )
] 
)
```
Also would be good if you'd point me how to fetch the axis names from the edges!
Apologies for the late responses.
I think this should be good for now. Thanks for building this! I look forward to the PR.
Maybe my only change would be to change
```
Edge('Node A'->'Node B' ) Axis 1 : 0, Axis 2 : 0 
```

to 
```
Edge('Node A'[0] ^ 'Node B'[0] ) 
```
since `nodea[0] ^ nodeb[0]` is a pretty common way to connect nodes together in this library.

Resolving patch:
diff --git a/tensornetwork/network_components.py b/tensornetwork/network_components.py
--- a/tensornetwork/network_components.py
+++ b/tensornetwork/network_components.py
@@ -579,7 +579,14 @@ def _load_node(cls, node_data: h5py.Group) -> "BaseNode":
     node.set_signature(signature)
     return node
 
-
+  def __repr__(self):
+    edges = self.get_all_edges()
+    return (f'{self.__class__.__name__}\n(\n'
+            f'name : {self.name!r},'
+            f'\ntensor : \n{self.tensor!r},'
+            f'\nedges : \n{edges!r} \n)'
+            )
+  
 class CopyNode(BaseNode):
 
   def __init__(self,
@@ -1033,6 +1040,13 @@ def __str__(self) -> Optional[Text]:
     if self.name:
       return self.name
     return '__unnamed_edge__'
+  
+  def __repr__(self):
+    if self.node1 is not None and self.node2 is not None:
+      return (f'\n{self.__class__.__name__}('
+              f'{self.node1.name!r}[{self.axis1}] -> '
+              f'{self.node2.name!r}[{self.axis2}] )\n')
+    return f'\n{self.__class__.__name__}(Dangling Edge)[{self.axis1}] \n'
 
   def disconnect(self,
                  edge1_name: Optional[Text] = None,


Test patch:
diff --git a/tensornetwork/tests/network_components_free_test.py b/tensornetwork/tests/network_components_free_test.py
--- a/tensornetwork/tests/network_components_free_test.py
+++ b/tensornetwork/tests/network_components_free_test.py
@@ -3,6 +3,7 @@
 import pytest
 from collections import namedtuple
 import h5py
+import re
 #pylint: disable=line-too-long
 from tensornetwork.network_components import Node, CopyNode, Edge, NodeCollection
 import tensornetwork as tn
@@ -905,3 +906,16 @@ def test_add_to_node_collection_nested():
 
   assert container1 == set()
   assert container2 == {a, b}
+
+def test_repr_for_Nodes_and_Edges(double_node_edge):
+  node1 = repr(double_node_edge.node1)
+  node1 = re.sub(r"\s", "", node1)
+  node1 = re.sub(r"\s", "", node1)
+  node2 = repr(double_node_edge.node2)
+  node2 = re.sub(r"\s", "", node2)
+  node2 = re.sub(r"\s", "", node2)
+  assert "test_node1" in str(node1)
+  assert "[[[1.,1.],[1.,1.]]]" in str(node1) and str(node2)
+  assert "Edge(DanglingEdge)[0]" in str(node1) and str(node2)
+  assert "Edge('test_node1'[1]->'test_node2'[1])" in str(node1) and str(node2)
+  assert "Edge(DanglingEdge)[2]" in str(node1) and str(node2)
\ No newline at end of file