Repository: Qiskit/qiskit

Issue:
Figure of large circuit with initialize gives an error on rendering with Matplotlib backend
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: 0.17.0
- **Python version**: 3.8.8
- **Operating system**: Windows 8.1 (Although I am working in the IBM Quantum Lab environment)

### What is the current behavior?
I am trying to initialize a quantum circuit and draw the circuit using the Matplotlib backend:
`size = 4`
`q = QuantumRegister(size, name='q')`
`ckt = QuantumCircuit(q, name='ckt')`
`initial_state=np.zeros(2**size)`
`initial_state[size] = 1`
`ckt.initialize(initial_state)`
`ckt.draw(output='mpl')`

When I set `size` to a relatively small value, say 4, I get the figure as expected:

![image](https://user-images.githubusercontent.com/61590679/117193806-ee872780-ae00-11eb-8cda-77b569b4fdbf.png)

Note that the state vector to which I initialize the state is displayed in the box denoting the Initialize instruction.

When `size` is increased to, say, 7, the figure gets stretched too much, according to the length of the state vector:

![image](https://user-images.githubusercontent.com/61590679/117194126-58073600-ae01-11eb-8198-380a21b59918.png)

Continuing likewise, I increase `size` to the value 13, on doing which, the figure is not generated, and after a stack trace, I see the following error:

`/opt/conda/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in __init__(self, width, height, dpi)`
`     89         self.width = width`
`     90         self.height = height`
`---> 91         self._renderer = _RendererAgg(int(width), int(height), dpi)`
`     92         self._filter_renderers = []`
`     93 `

`ValueError: Image size of 1604x65702 pixels is too large. It must be less than 2^16 in each direction.`

`<Figure size 1604.72x65702.3 with 1 Axes>`

### Steps to reproduce the problem
Outlined above.

### What is the expected behavior?
I think the problem is caused by the excessive length of the state vector, and won't be unique to the Quantum Lab environment. As 1 possible solution, I would like to have a way to disable the state vector supplied to `Initialize` getting printed in the box. I believe this should stop the error being reported.
As an alternative, another possible solution would be to limit the size of the vector getting printed.


### Suggested solutions
I think both the above solutions can be achieved by adding options to the `style` kwarg for use with the Matplotlib backend. See https://qiskit.org/documentation/tutorials/circuits_advanced/03_advanced_circuit_visualization.html.
For the first solution, one can add an option (bool) to disable the state vector from getting printed in `Initialize`. By default, the value of this option can be kept false, and it can print the vector, as before.
For the second solution, one can add an option (int) to limit the size of the vector getting displayed to this number.




Discussion:
If you think this is a good issue, I would like to try the fix.
This definitely should be fixed. We had a similar problem in the latex drawer and just limited the number of parameters it would display and then put '...' at the end. In the mpl drawer, there's a lot more room to display so I'd suggest the following.

The default fold number for the drawer is 25. This is essentially how many X gates would display on a qubit before wrapping to a new row of qubits. So if there were 30 X gates on q0, then 25 would show on the first row and 5 on the second.

I think if we keep the `initialize` to a width less than the default fold, this would be sufficient. I hacked a worst case for this, (the widest possible parameters) for 4 qubits,

![image](https://user-images.githubusercontent.com/16268251/117317007-f9ac7880-ae3d-11eb-94de-fadbacbad2f2.png)

The `initialize` is 19 wide, so it fits on one row using the default fold. A solution would then be to show parameters for up to 4 qubits, and don't display parameters for an `initialize` with > 4 qubits. I think this would be fine since there are a number of gates, like Hamiltonian, that have numpy arrays as parameters, and we do not display the parameters at all for these.

Resolving patch:
diff --git a/qiskit/visualization/matplotlib.py b/qiskit/visualization/matplotlib.py
--- a/qiskit/visualization/matplotlib.py
+++ b/qiskit/visualization/matplotlib.py
@@ -436,13 +436,16 @@ def _get_text_width(self, text, fontsize, param=False):
                 sum_text *= self._subfont_factor
             return sum_text
 
-    def _param_parse(self, v):
-        param_parts = [None] * len(v)
-        for i, e in enumerate(v):
+    def _param_parse(self, params):
+        param_parts = []
+        for i, param in enumerate(params):
+            if i > 16:
+                param_parts.append("...")
+                break
             try:
-                param_parts[i] = pi_check(e, output="mpl", ndigits=3)
+                param_parts.append(pi_check(param, output="mpl", ndigits=3))
             except TypeError:
-                param_parts[i] = str(e)
+                param_parts.append(str(param))
         param_parts = ", ".join(param_parts).replace("-", "$-$")
         return param_parts
 


Test patch:
diff --git a/test/ipynb/mpl/references/wide_params.png b/test/ipynb/mpl/references/wide_params.png
new file mode 100644
Binary files /dev/null and b/test/ipynb/mpl/references/wide_params.png differ
diff --git a/test/ipynb/mpl/test_circuit_matplotlib_drawer.py b/test/ipynb/mpl/test_circuit_matplotlib_drawer.py
--- a/test/ipynb/mpl/test_circuit_matplotlib_drawer.py
+++ b/test/ipynb/mpl/test_circuit_matplotlib_drawer.py
@@ -549,6 +549,14 @@ def test_6095(self):
             filename="6095.png",
         )
 
+    def test_overwide_gates(self):
+        """Test gates don't exceed width of default fold"""
+        circuit = QuantumCircuit(5)
+        initial_state = np.zeros(2 ** 5)
+        initial_state[5] = 1
+        circuit.initialize(initial_state)
+        self.circuit_drawer(circuit, filename="wide_params.png")
+
 
 if __name__ == "__main__":
     unittest.main(verbosity=1)