Repository: Qiskit/qiskit-terra (python)

Issue:
QuantumCircuit `.clear()` and `.copy_empty_like()` functionality
### What should we add?

Sometimes it can be useful to erase all instructions in a QuantumCircuit. This request is for:
1. [Clear functionality](https://github.com/pedrorrivero/qiskit-terra/blob/794db4792e97adca4a78eb4f3411c04e50665df7/qiskit/circuit/quantumcircuit.py#L2311-L2313) to erase all instructions from a QuantumCircuit → `.clear()`.
We not only need to clear all data, but (at least) also update the parameter table:
```python
self.data.clear()
self._parameter_table.clear()
```
2. [Copy empty functionality](https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/dagcircuit/dagcircuit.py#L511-L538) to get a cleared copy of a QuantumCircuit → `.copy_empty_like()`.
This functionality is already implemented for DAGCircuit, and should retain all relevant circuit properties other than the instructions and associated parameters (e.g. registers, bits, name, metadata). Notice that `quantum_circuit.copy().clear()` would give the desired outcome, however `.copy()` performs a deepcopy of the circuit, which we would then erase almost completely when deleting the instructions, so this method should provide a more efficient way to do this.


TODO:
- [ ] `.clear()`
- [ ] `.copy_empty_like()`

Pull request:
Add `.clear()` and `.copy_empty_like()` functionality to QuantumCircuit.
<!--
⚠️ If you do not respect this template, your pull request will be closed.
⚠️ Your pull request title should be short detailed and understandable for all.
⚠️ Also, please add a release note file using reno if the change needs to be
  documented in the release notes.
⚠️ If your pull request fixes an open issue, please link to the issue.

- [ ] I have added the tests to cover my changes.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the CONTRIBUTING document.
-->

### Summary
Adds `.clear()` and `.copy_empty_like()` functionality to QuantumCircuit.

Closes #8132 
Changelog: New Feature

### Details and comments
1. Clear functionality to erase all instructions from a QuantumCircuit. 
We clear both the data and the parameter table.

2. Copy empty functionality to get a cleared copy of a QuantumCircuit.
This is logically equivalent to `qc.copy().clear()` however, it is done avoiding a deepcopy of the original QuantumCircuit, and therefore it is more efficient.
This functionality is already implemented for DAGCircuit, and retains all relevant circuit properties other than the instructions and associated parameters (e.g. registers, bits, name, metadata). Contrary to the implementation in DAGCircuit, this method allows for an optional parameter to update the name of the copied circuit, mimicking `qc.copy(name)`.

### To do
- [x] Add tests
- [x] Add release note
- [x] Check docstring


Patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
index 7acb5d10f..628063aad 100644
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -2118,18 +2118,9 @@ class QuantumCircuit:
         Returns:
           QuantumCircuit: a deepcopy of the current circuit, with the specified name
         """
-        cpy = copy.copy(self)
-        # copy registers correctly, in copy.copy they are only copied via reference
-        cpy.qregs = self.qregs.copy()
-        cpy.cregs = self.cregs.copy()
-        cpy._qubits = self._qubits.copy()
-        cpy._ancillas = self._ancillas.copy()
-        cpy._clbits = self._clbits.copy()
-        cpy._qubit_indices = self._qubit_indices.copy()
-        cpy._clbit_indices = self._clbit_indices.copy()
+        cpy = self.copy_empty_like(name)
 
         instr_instances = {id(instr): instr for instr, _, __ in self._data}
-
         instr_copies = {id_: instr.copy() for id_, instr in instr_instances.items()}
 
         cpy._parameter_table = ParameterTable(
@@ -2147,6 +2138,35 @@ class QuantumCircuit:
             for inst, qargs, cargs in self._data
         ]
 
+        return cpy
+
+    def copy_empty_like(self, name: Optional[str] = None) -> "QuantumCircuit":
+        """Return a copy of self with the same structure but empty.
+
+        That structure includes:
+            * name, calibrations and other metadata
+            * global phase
+            * all the qubits and clbits, including the registers
+
+        Args:
+            name (str): Name for the copied circuit. If None, then the name stays the same.
+
+        Returns:
+            QuantumCircuit: An empty copy of self.
+        """
+        cpy = copy.copy(self)
+        # copy registers correctly, in copy.copy they are only copied via reference
+        cpy.qregs = self.qregs.copy()
+        cpy.cregs = self.cregs.copy()
+        cpy._qubits = self._qubits.copy()
+        cpy._ancillas = self._ancillas.copy()
+        cpy._clbits = self._clbits.copy()
+        cpy._qubit_indices = self._qubit_indices.copy()
+        cpy._clbit_indices = self._clbit_indices.copy()
+
+        cpy._parameter_table = ParameterTable()
+        cpy._data = []
+
         cpy._calibrations = copy.deepcopy(self._calibrations)
         cpy._metadata = copy.deepcopy(self._metadata)
 
@@ -2154,6 +2174,14 @@ class QuantumCircuit:
             cpy.name = name
         return cpy
 
+    def clear(self) -> None:
+        """Clear all instructions in self.
+
+        Clearing the circuits will keep the metadata and calibrations.
+        """
+        self._data.clear()
+        self._parameter_table.clear()
+
     def _create_creg(self, length: int, name: str) -> ClassicalRegister:
         """Creates a creg, checking if ClassicalRegister with same name exists"""
         if name in [creg.name for creg in self.cregs]:
diff --git a/releasenotes/notes/clear-circuit-b8edd4126f47d75a.yaml b/releasenotes/notes/clear-circuit-b8edd4126f47d75a.yaml
new file mode 100644
index 000000000..0a4a4df22
--- /dev/null
+++ b/releasenotes/notes/clear-circuit-b8edd4126f47d75a.yaml
@@ -0,0 +1,13 @@
+---
+features:
+  - |
+    Added a new method :py:meth:`.QuantumCircuit.clear` which is used to to erase all instructions
+    from a QuantumCircuit. We clear both the data and the parameter table.
+  - |
+    Added a new method :py:meth:`QuantumCircuit.copy_empty_like` which is used to get a cleared copy of a
+    :class:`~.QuantumCircuit` instnace. This is logically equivalent to `qc.copy().clear()` however, it is done
+    avoiding a deepcopy of the original QuantumCircuit, and therefore it is more efficient. This functionality
+    already implemented for :class:`~.DAGCircuit`, and retains all relevant circuit properties other than the
+    instructions and associated parameters (e.g. registers, bits, name, metadata). Unlike the
+    :meth:`.DAGCircuit.copy_empty_like` method, this new :class:`~.QuantumCircuit` method allows for
+    an optional parameter to update the name of the copied circuit, mimicking ``qc.copy(name)``.


Test patch:
diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py
index 4fe619402..281a6ea4e 100644
--- a/test/python/circuit/test_circuit_operations.py
+++ b/test/python/circuit/test_circuit_operations.py
@@ -27,6 +27,7 @@ from qiskit.circuit.quantumregister import AncillaQubit, AncillaRegister, Qubit
 from qiskit.test import QiskitTestCase
 from qiskit.circuit.library.standard_gates import SGate
 from qiskit.quantum_info import Operator
+from qiskit.pulse import Schedule, Play, Gaussian, DriveChannel
 
 
 @ddt
@@ -478,6 +479,41 @@ class TestCircuitOperations(QiskitTestCase):
         self.assertEqual(len(qc.cregs), 1)
         self.assertEqual(len(copied.cregs), 2)
 
+    def test_copy_empty_like_circuit(self):
+        """Test copy_empty_like method makes a clear copy."""
+        qr = QuantumRegister(2)
+        cr = ClassicalRegister(2)
+        qc = QuantumCircuit(qr, cr, global_phase=1.0, name="qc", metadata={"key": "value"})
+        qc.h(qr[0])
+        qc.measure(qr[0], cr[0])
+        qc.measure(qr[1], cr[1])
+        sched = Schedule(Play(Gaussian(160, 0.1, 40), DriveChannel(0)))
+        qc.add_calibration("h", [0, 1], sched)
+        copied = qc.copy_empty_like()
+        qc.clear()
+
+        self.assertEqual(qc, copied)
+        self.assertEqual(qc.global_phase, copied.global_phase)
+        self.assertEqual(qc.name, copied.name)
+        self.assertEqual(qc.metadata, copied.metadata)
+        self.assertEqual(qc.calibrations, copied.calibrations)
+
+        copied = qc.copy_empty_like("copy")
+        self.assertEqual(copied.name, "copy")
+
+    def test_clear_circuit(self):
+        """Test clear method deletes instructions in circuit."""
+        qr = QuantumRegister(2)
+        cr = ClassicalRegister(2)
+        qc = QuantumCircuit(qr, cr)
+        qc.h(qr[0])
+        qc.measure(qr[0], cr[0])
+        qc.measure(qr[1], cr[1])
+        qc.clear()
+
+        self.assertEqual(len(qc.data), 0)
+        self.assertEqual(len(qc._parameter_table), 0)
+
     def test_measure_active(self):
         """Test measure_active
         Applies measurements only to non-idle qubits. Creates a ClassicalRegister of size equal to