Repository: Qiskit/qiskit

Issue:
Text drawer option vertically_compressed is not exposed into higher drawing APIs
_Originally posted by @1ucian0 in https://github.com/Qiskit/qiskit-terra/pull/2480#issuecomment-494994853_

The text drawer has an option to enable and disable vertical compression: `vertically_compressed`. However, this option is not exposed by `circuit_drawer` or `QuantumCircuit.draw`.


Discussion:

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -512,7 +512,7 @@ def qasm(self):
 
     def draw(self, scale=0.7, filename=None, style=None, output=None,
              interactive=False, line_length=None, plot_barriers=True,
-             reverse_bits=False, justify=None):
+             reverse_bits=False, justify=None, vertical_compression='medium'):
         """Draw the quantum circuit
 
         Using the output parameter you can specify the format. The choices are:
@@ -549,7 +549,9 @@ def draw(self, scale=0.7, filename=None, style=None, output=None,
                 gates should be placed in the output circuit if there is an option.
                 `none` results in each gate being placed in its own column. Currently
                 only supported by text drawer.
-
+            vertical_compression (string): `high`, `medium` or `low`. It merges the
+                lines generated by `text` so the drawing will take less vertical room.
+                Default is `medium`. It is ignored if output is not `text`.
         Returns:
             PIL.Image or matplotlib.figure or str or TextDrawing:
                 * PIL.Image: (output `latex`) an in-memory representation of the
@@ -572,7 +574,8 @@ def draw(self, scale=0.7, filename=None, style=None, output=None,
                               line_length=line_length,
                               plot_barriers=plot_barriers,
                               reverse_bits=reverse_bits,
-                              justify=justify)
+                              justify=justify,
+                              vertical_compression=vertical_compression)
 
     def size(self):
         """Returns total number of gate operations in circuit.
diff --git a/qiskit/visualization/circuit_visualization.py b/qiskit/visualization/circuit_visualization.py
--- a/qiskit/visualization/circuit_visualization.py
+++ b/qiskit/visualization/circuit_visualization.py
@@ -53,7 +53,8 @@ def circuit_drawer(circuit,
                    line_length=None,
                    plot_barriers=True,
                    reverse_bits=False,
-                   justify=None):
+                   justify=None,
+                   vertical_compression='medium'):
     """Draw a quantum circuit to different formats (set by output parameter):
     0. text: ASCII art TextDrawing that can be printed in the console.
     1. latex: high-quality images, but heavy external software dependencies
@@ -93,7 +94,9 @@ def circuit_drawer(circuit,
             gates should be placed in the output circuit if there is an option.
             `none` results in each gate being placed in its own column. Currently
             only supported by text drawer.
-
+        vertical_compression (string): `high`, `medium` or `low`. It merges the
+            lines generated by `text` so the drawing will take less vertical room.
+            Default is `medium`. It is ignored if output is not `text`.
     Returns:
         PIL.Image: (output `latex`) an in-memory representation of the image
             of the circuit diagram.
@@ -198,7 +201,8 @@ def circuit_drawer(circuit,
                                     line_length=line_length,
                                     reverse_bits=reverse_bits,
                                     plotbarriers=plot_barriers,
-                                    justify=justify)
+                                    justify=justify,
+                                    vertical_compression=vertical_compression)
     elif output == 'latex':
         image = _latex_circuit_drawer(circuit, scale=scale,
                                       filename=filename, style=style,
@@ -305,7 +309,7 @@ def qx_color_scheme():
 
 
 def _text_circuit_drawer(circuit, filename=None, line_length=None, reverse_bits=False,
-                         plotbarriers=True, justify=None, vertically_compressed=True):
+                         plotbarriers=True, justify=None, vertical_compression='high'):
     """
     Draws a circuit using ascii art.
     Args:
@@ -320,8 +324,8 @@ def _text_circuit_drawer(circuit, filename=None, line_length=None, reverse_bits=
         plotbarriers (bool): Draws the barriers when they are there.
         justify (str) : `left`, `right` or `none`. Defaults to `left`. Says how
                         the circuit should be justified.
-        vertically_compressed (bool): Default is `True`. It merges the lines so the
-                                      drawing will take less vertical room.
+        vertical_compression (string): `high`, `medium`, or `low`. It merges the
+            lines so the drawing will take less vertical room. Default is `high`.
     Returns:
         TextDrawing: An instances that, when printed, draws the circuit in ascii art.
     """
@@ -331,7 +335,7 @@ def _text_circuit_drawer(circuit, filename=None, line_length=None, reverse_bits=
     text_drawing = _text.TextDrawing(qregs, cregs, ops)
     text_drawing.plotbarriers = plotbarriers
     text_drawing.line_length = line_length
-    text_drawing.vertically_compressed = vertically_compressed
+    text_drawing.vertical_compression = vertical_compression
 
     if filename:
         text_drawing.dump(filename)
diff --git a/qiskit/visualization/text.py b/qiskit/visualization/text.py
--- a/qiskit/visualization/text.py
+++ b/qiskit/visualization/text.py
@@ -442,14 +442,16 @@ class TextDrawing():
     """ The text drawing"""
 
     def __init__(self, qregs, cregs, instructions, plotbarriers=True,
-                 line_length=None, vertically_compressed=True):
+                 line_length=None, vertical_compression='high'):
         self.qregs = qregs
         self.cregs = cregs
         self.instructions = instructions
 
         self.plotbarriers = plotbarriers
         self.line_length = line_length
-        self.vertically_compressed = vertically_compressed
+        if vertical_compression not in ['high', 'medium', 'low']:
+            raise ValueError("Vertical compression can only be 'high', 'medium', or 'low'")
+        self.vertical_compression = vertical_compression
 
     def __str__(self):
         return self.single_string()
@@ -550,7 +552,7 @@ def lines(self, line_length=None):
         lines = []
         for layer_group in layer_groups:
             wires = [i for i in zip(*layer_group)]
-            lines += TextDrawing.draw_wires(wires, self.vertically_compressed)
+            lines += self.draw_wires(wires)
 
         return lines
 
@@ -576,14 +578,23 @@ def wire_names(self, with_initial_value=True):
 
         return qubit_labels + clbit_labels
 
-    @staticmethod
-    def draw_wires(wires, vertically_compressed=True):
+    def should_compress(self, top_line, bot_line):
+        """Decides if the top_line and bot_line should be merged,
+        based on `self.vertical_compression`."""
+        if self.vertical_compression == 'high':
+            return True
+        if self.vertical_compression == 'low':
+            return False
+        for top, bot in zip(top_line, bot_line):
+            if top == '┴' and bot == '┬':
+                return False
+        return True
+
+    def draw_wires(self, wires):
         """
         Given a list of wires, creates a list of lines with the text drawing.
         Args:
             wires (list): A list of wires with instructions.
-            vertically_compressed (bool): Default is `True`. It merges the lines
-                                     so the drawing will take less vertical room.
         Returns:
             list: A list of lines with the text drawing.
         """
@@ -598,7 +609,7 @@ def draw_wires(wires, vertically_compressed=True):
             if bot_line is None:
                 lines.append(top_line)
             else:
-                if vertically_compressed:
+                if self.should_compress(top_line, bot_line):
                     lines.append(TextDrawing.merge_lines(lines.pop(), top_line))
                 else:
                     lines.append(TextDrawing.merge_lines(lines[-1], top_line, icod="bot"))


Test patch:
diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py
--- a/test/python/visualization/test_circuit_text_drawer.py
+++ b/test/python/visualization/test_circuit_text_drawer.py
@@ -922,8 +922,8 @@ def test_text_sympy_constant(self):
         self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
 
-class TestTextDrawerVerticallyExtended(QiskitTestCase):
-    """Test vertically_compressed=False"""
+class TestTextDrawerVerticalCompressionLow(QiskitTestCase):
+    """Test vertical_compression='low' """
 
     def test_text_conditional_1(self):
         """ Conditional drawing with 1-bit-length regs."""
@@ -947,7 +947,7 @@ def test_text_conditional_1(self):
                               "               └─────┘"])
 
         circuit = QuantumCircuit.from_qasm_str(qasm_string)
-        self.assertEqual(str(_text_circuit_drawer(circuit, vertically_compressed=False)), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, vertical_compression='low')), expected)
 
     def test_text_justify_right(self):
         """ Drawing with right justify """
@@ -972,7 +972,35 @@ def test_text_justify_right(self):
         circuit.measure(qr1[1], cr1[1])
         self.assertEqual(str(_text_circuit_drawer(circuit,
                                                   justify='right',
-                                                  vertically_compressed=False)), expected)
+                                                  vertical_compression='low')), expected)
+
+
+class TestTextDrawerVerticalCompressionMedium(QiskitTestCase):
+    """Test vertical_compression='medium' """
+
+    def test_text_conditional_1(self):
+        """ Medium vertical compression avoids box overlap."""
+        qasm_string = """
+        OPENQASM 2.0;
+        include "qelib1.inc";
+        qreg q[1];
+        creg c0[1];
+        creg c1[1];
+        if(c0==1) x q[0];
+        if(c1==1) x q[0];
+        """
+        expected = '\n'.join(["         ┌───┐  ┌───┐ ",
+                              "q_0: |0>─┤ X ├──┤ X ├─",
+                              "         └─┬─┘  └─┬─┘ ",
+                              "        ┌──┴──┐   │   ",
+                              "c0_0: 0 ╡ = 1 ╞═══╪═══",
+                              "        └─────┘┌──┴──┐",
+                              "c1_0: 0 ═══════╡ = 1 ╞",
+                              "               └─────┘"])
+
+        circuit = QuantumCircuit.from_qasm_str(qasm_string)
+        self.assertEqual(str(_text_circuit_drawer(circuit,
+                                                  vertical_compression='medium')), expected)
 
 
 class TestTextConditional(QiskitTestCase):