Repository: Qiskit/qiskit

Issue:
Memory leak of qiskit.quantum_info.Operator
<!-- ⚠️ 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.14.1
- **Python version**: 3.7.7
- **Operating system**: Ubuntu 20.04

### What is the current behavior?

`qiskit.quantum_info.Operator(QuantumCircuit)` seems to have a memory leak.
`qiskit.quantum_info.Operator(ndarray)` does not have this issue.

### Steps to reproduce the problem

I ran the following code and saw that RSS and DATA increase.

Code:
https://gist.github.com/t-imamichi/c0405d57e61b3393b773d182ec7777ac
```python
from qiskit import QuantumCircuit, transpile
from qiskit.quantum_info import Operator
from os import getpid
import psutil

N = 10000
K = 1000
Q = 2

circ = QuantumCircuit(Q)
circ.h(range(Q))
circ.s(range(Q))
circ.h(range(Q))
qc = transpile(circ, basis_gates=['u3', 'cx'], optimization_level=1)

p = psutil.Process(getpid())
for i in range(N):
    Operator(qc)
    if i % K == 0:
        print(p.memory_info())
```

Output:
```
$ python op.py
pmem(rss=184262656, vms=569442304, shared=38907904, text=3039232, lib=0, data=182874112, dirty=0)
pmem(rss=187293696, vms=572325888, shared=38973440, text=3039232, lib=0, data=185757696, dirty=0)
pmem(rss=189919232, vms=574947328, shared=38973440, text=3039232, lib=0, data=188379136, dirty=0)
pmem(rss=192561152, vms=577703936, shared=38973440, text=3039232, lib=0, data=191135744, dirty=0)
pmem(rss=195461120, vms=580587520, shared=38973440, text=3039232, lib=0, data=194019328, dirty=0)
pmem(rss=198365184, vms=583471104, shared=38973440, text=3039232, lib=0, data=196902912, dirty=0)
pmem(rss=200986624, vms=586092544, shared=38973440, text=3039232, lib=0, data=199524352, dirty=0)
pmem(rss=203890688, vms=588976128, shared=38973440, text=3039232, lib=0, data=202407936, dirty=0)
pmem(rss=206508032, vms=591597568, shared=38973440, text=3039232, lib=0, data=205029376, dirty=0)
pmem(rss=209399808, vms=594481152, shared=38973440, text=3039232, lib=0, data=207912960, dirty=0)

```


### What is the expected behavior?

No increase of RSS and DATA.

### Suggested solutions

No idea.




Discussion:

Resolving patch:
diff --git a/qiskit/converters/circuit_to_gate.py b/qiskit/converters/circuit_to_gate.py
--- a/qiskit/converters/circuit_to_gate.py
+++ b/qiskit/converters/circuit_to_gate.py
@@ -20,7 +20,7 @@
 from qiskit.exceptions import QiskitError
 
 
-def circuit_to_gate(circuit, parameter_map=None):
+def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None):
     """Build a ``Gate`` object from a ``QuantumCircuit``.
 
     The gate is anonymous (not tied to a named quantum register),
@@ -33,6 +33,8 @@ def circuit_to_gate(circuit, parameter_map=None):
            parameters in the circuit to parameters to be used in the gate.
            If None, existing circuit parameters will also parameterize the
            Gate.
+        equivalence_library (EquivalenceLibrary): Optional equivalence library
+           where the converted gate will be registered.
 
     Raises:
         QiskitError: if circuit is non-unitary or if
@@ -81,10 +83,8 @@ def find_bit_position(bit):
 
     target = circuit.assign_parameters(parameter_dict, inplace=False)
 
-    # pylint: disable=cyclic-import
-    from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel
-    # pylint: enable=cyclic-import
-    sel.add_equivalence(gate, target)
+    if equivalence_library is not None:
+        equivalence_library.add_equivalence(gate, target)
 
     definition = target.data
 
diff --git a/qiskit/converters/circuit_to_instruction.py b/qiskit/converters/circuit_to_instruction.py
--- a/qiskit/converters/circuit_to_instruction.py
+++ b/qiskit/converters/circuit_to_instruction.py
@@ -20,7 +20,7 @@
 from qiskit.circuit.classicalregister import ClassicalRegister
 
 
-def circuit_to_instruction(circuit, parameter_map=None):
+def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None):
     """Build an ``Instruction`` object from a ``QuantumCircuit``.
 
     The instruction is anonymous (not tied to a named quantum register),
@@ -33,6 +33,8 @@ def circuit_to_instruction(circuit, parameter_map=None):
            parameters in the circuit to parameters to be used in the instruction.
            If None, existing circuit parameters will also parameterize the
            instruction.
+        equivalence_library (EquivalenceLibrary): Optional equivalence library
+           where the converted instruction will be registered.
 
     Raises:
         QiskitError: if parameter_map is not compatible with circuit
@@ -88,10 +90,8 @@ def find_bit_position(bit):
 
     target = circuit.assign_parameters(parameter_dict, inplace=False)
 
-    # pylint: disable=cyclic-import
-    from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel
-    # pylint: enable=cyclic-import
-    sel.add_equivalence(instruction, target)
+    if equivalence_library is not None:
+        equivalence_library.add_equivalence(instruction, target)
 
     definition = target.data
 


Test patch:
diff --git a/test/python/circuit/test_equivalence.py b/test/python/circuit/test_equivalence.py
--- a/test/python/circuit/test_equivalence.py
+++ b/test/python/circuit/test_equivalence.py
@@ -21,6 +21,7 @@
 
 from qiskit.circuit import QuantumCircuit, Parameter, Gate
 from qiskit.circuit.exceptions import CircuitError
+from qiskit.converters import circuit_to_instruction, circuit_to_gate
 
 from qiskit.circuit import EquivalenceLibrary
 
@@ -405,15 +406,15 @@ def test_converter_gate_registration(self):
         qc_gate.h(0)
         qc_gate.cx(0, 1)
 
-        bell_gate = qc_gate.to_gate()
+        from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel
+        bell_gate = circuit_to_gate(qc_gate, equivalence_library=sel)
 
         qc_inst = QuantumCircuit(2)
         qc_inst.h(0)
         qc_inst.cx(0, 1)
 
-        bell_inst = qc_inst.to_instruction()
+        bell_inst = circuit_to_instruction(qc_inst, equivalence_library=sel)
 
-        from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel
         gate_entry = sel.get_entry(bell_gate)
         inst_entry = sel.get_entry(bell_inst)
 
@@ -429,7 +430,8 @@ def test_gate_decomposition_properties(self):
         qc.h(0)
         qc.cx(0, 1)
 
-        gate = qc.to_gate()
+        from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel
+        gate = circuit_to_gate(qc, equivalence_library=sel)
 
         decomps = gate.decompositions