Repository: Quantum-TII/qibo (python)

Issue:
Improvements on drawing circuits.
Following some discussions with @joseignaciolatorre I would like to make some proposals on changes do the `.draw()` feature for ease of reading.

The `line_wrap` flag should be initialized at some value instead of being set to unlimited by default. We should decide a sensible value in order to not affect small and simple circuits, but at the same time if a large circuit needs to be drawn, have it split into chunks so that it can be properly interpreted.

Additionally, when splitting the circuits into different lines, it would be very useful to have the qubit labels repeated also on each new line in order to easily follow gates acting on a qubit on the following lines as well. The same argument can be made for having labels on the right side of the circuit as well.

If it does not crowd the screen too much, having extra qubit labels helps a lot when analyzing large circuits.

Pull request description:
Updating multiline circuit draw
Closes #353. @igres26 could you please give a try and let me know if you like it?

Patch:
diff --git a/src/qibo/abstractions/circuit.py b/src/qibo/abstractions/circuit.py
index 82b543cf4..c88bfbb68 100644
--- a/src/qibo/abstractions/circuit.py
+++ b/src/qibo/abstractions/circuit.py
@@ -933,7 +933,7 @@ class AbstractCircuit(ABC):
 
         return len(qubits), gate_list
 
-    def draw(self, line_wrap=None) -> str:
+    def draw(self, line_wrap=70) -> str:
         """Draw text circuit using unicode symbols.
 
         Args:
@@ -1010,24 +1010,29 @@ class AbstractCircuit(ABC):
 
         # line wrap
         if line_wrap:
-            output = output.splitlines()
+            loutput = output.splitlines()
             def chunkstring(string, length):
                 nchunks = range(0, len(string), length)
                 return (string[i:length+i] for i in nchunks), len(nchunks)
             for row in range(self.nqubits):
-                chunks, nchunks = chunkstring(output[row], line_wrap)
+                chunks, nchunks = chunkstring(loutput[row][3 + len(str(self.nqubits)):], line_wrap)
+                if nchunks == 1:
+                    loutput = None
+                    break
                 for i, c in enumerate(chunks):
-                    output += ['' for _ in range(self.nqubits)]
-                    suffix = ' ...\n'
+                    loutput += ['' for _ in range(self.nqubits)]
+                    suffix = f' ...\n'
+                    prefix = f'q{row}' + ' ' * (len(str(self.nqubits))-len(str(row))) + ': '
                     if i == 0:
-                        prefix = ''
+                        prefix += ' ' * 4
                     elif row == 0:
-                        prefix = '\n... '
+                        prefix = '\n' + prefix + '... '
                     else:
-                        prefix = '... '
+                        prefix += '... '
                     if i == nchunks-1:
                         suffix = '\n'
-                    output[row + i * self.nqubits] = prefix + c + suffix
-            output = ''.join(output)
+                    loutput[row + i * self.nqubits] = prefix + c + suffix
+            if loutput is not None:
+                output = ''.join(loutput)
 
         return output.rstrip('\n')


Test patch:
diff --git a/src/qibo/tests_new/test_abstract_circuit.py b/src/qibo/tests_new/test_abstract_circuit.py
index 007193535..155bb38a7 100644
--- a/src/qibo/tests_new/test_abstract_circuit.py
+++ b/src/qibo/tests_new/test_abstract_circuit.py
@@ -495,36 +495,37 @@ def test_circuit_draw():
 def test_circuit_draw_line_wrap():
     """Test circuit text draw with line wrap."""
     ref_line_wrap_50 = \
-          'q0: ─H─U1─U1─U1─U1───────────────────────────x───I ...\n' \
-          'q1: ───o──|──|──|──H─U1─U1─U1────────────────|─x─I ...\n' \
-          'q2: ──────o──|──|────o──|──|──H─U1─U1────────|─|── ...\n' \
-          'q3: ─────────o──|───────o──|────o──|──H─U1───|─x── ...\n' \
-          'q4: ────────────o──────────o───────o────o──H─x──── ...\n' \
-          '\n' \
-          '... ───f─o────gf───M─\n' \
-          '... ───|─U3───|──o─M─\n' \
-          '... ───|────X─gf─o─M─\n' \
-          '... ─M─|────o────o───\n' \
-          '... ───f────o────X───'
+        'q0:     ─H─U1─U1─U1─U1───────────────────────────x───I───f ...\n' \
+        'q1:     ───o──|──|──|──H─U1─U1─U1────────────────|─x─I───| ...\n' \
+        'q2:     ──────o──|──|────o──|──|──H─U1─U1────────|─|─────| ...\n' \
+        'q3:     ─────────o──|───────o──|────o──|──H─U1───|─x───M─| ...\n' \
+        'q4:     ────────────o──────────o───────o────o──H─x───────f ...\n' \
+        '\n' \
+        'q0: ... ─o────gf───M─\n' \
+        'q1: ... ─U3───|──o─M─\n' \
+        'q2: ... ────X─gf─o─M─\n' \
+        'q3: ... ────o────o───\n' \
+        'q4: ... ────o────X───'
 
     ref_line_wrap_30 = \
-           'q0: ─H─U1─U1─U1─U1──────────── ...\n' \
-           'q1: ───o──|──|──|──H─U1─U1─U1─ ...\n' \
-           'q2: ──────o──|──|────o──|──|── ...\n' \
-           'q3: ─────────o──|───────o──|── ...\n' \
-           'q4: ────────────o──────────o── ...\n' \
-           '\n' \
-           '... ───────────────x───I───f─o──── ...\n' \
-           '... ───────────────|─x─I───|─U3─── ...\n' \
-           '... H─U1─U1────────|─|─────|────X─ ...\n' \
-           '... ──o──|──H─U1───|─x───M─|────o─ ...\n' \
-           '... ─────o────o──H─x───────f────o─ ...\n' \
-           '\n' \
-           '... gf───M─\n' \
-           '... |──o─M─\n' \
-           '... gf─o─M─\n' \
-           '... ───o───\n' \
-           '... ───X───'
+        'q0:     ─H─U1─U1─U1─U1──────────────── ...\n' \
+        'q1:     ───o──|──|──|──H─U1─U1─U1───── ...\n' \
+        'q2:     ──────o──|──|────o──|──|──H─U1 ...\n' \
+        'q3:     ─────────o──|───────o──|────o─ ...\n' \
+        'q4:     ────────────o──────────o────── ...\n' \
+        '\n' \
+        'q0: ... ───────────x───I───f─o────gf── ...\n' \
+        'q1: ... ───────────|─x─I───|─U3───|──o ...\n' \
+        'q2: ... ─U1────────|─|─────|────X─gf─o ...\n' \
+        'q3: ... ─|──H─U1───|─x───M─|────o────o ...\n' \
+        'q4: ... ─o────o──H─x───────f────o────X ...\n' \
+        '\n' \
+        'q0: ... ─M─\n' \
+        'q1: ... ─M─\n' \
+        'q2: ... ─M─\n' \
+        'q3: ... ───\n' \
+        'q4: ... ───'
+
     import numpy as np
     circuit = Circuit(5)
     for i1 in range(5):