Repository: Qiskit/qiskit

Issue:
QAOA does not update the operator when calling construct_circuit
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Optimization version**: The one on the IBM Quantum Experience
- **Python version**: The one on the IBM Quantum Experience
- **Operating system**: The one on the IBM Quantum Experience

### What is the current behavior?

If you call construct_circuit with two different operators, the second time the operator is not updated and the operator of the first call is used instead. 


### Steps to reproduce the problem

from qiskit.algorithms import QAOA
from qiskit.opflow.primitive_ops import PauliOp
from qiskit.quantum_info import Pauli
from qiskit import Aer

op = PauliOp(Pauli("ZZ"))
qaoa = QAOA()
qaoa.construct_circuit([0,0],op)[0].draw(output="text")

op = PauliOp(Pauli("IZ")) 
qaoa.construct_circuit([0,0],op)[0].draw(output="text")

### What is the expected behavior?

The second call to construct_circuit should update the operator and return the correct circuit

### Suggested solutions





Discussion:
Thank you for your report. Because `QAOA` is part of Terra, could you report it there? https://github.com/Qiskit/qiskit-terra/issues
It seems this comes about since QAOA extends VQE and while there is some additional check in there for QAOA the anstaz is not built out again if the number of qubits is the same when the operator changes. Eg. change your second call above to 
```
op = PauliOp(Pauli("IIZ"))
qaoa.construct_circuit([0,0],op)[0].draw(output="text")
```
and it works as expected.
I see. We should compare `operator` and `cost_operator` in the following line. I will make a PR to fix it.
https://github.com/Qiskit/qiskit-terra/blob/0a4c666ca09e129768f83f7c95ee7168455d869d/qiskit/algorithms/minimum_eigen_solvers/qaoa.py#L130

Resolving patch:
diff --git a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py b/qiskit/algorithms/minimum_eigen_solvers/qaoa.py
--- a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py
+++ b/qiskit/algorithms/minimum_eigen_solvers/qaoa.py
@@ -112,6 +112,7 @@ def __init__(
         self._reps = reps
         self._mixer = mixer
         self._initial_state = initial_state
+        self._cost_operator = None
 
         super().__init__(
             ansatz=None,
@@ -127,7 +128,8 @@ def __init__(
 
     def _check_operator_ansatz(self, operator: OperatorBase) -> OperatorBase:
         # Recreates a circuit based on operator parameter.
-        if operator.num_qubits != self.ansatz.num_qubits:
+        if operator != self._cost_operator:
+            self._cost_operator = operator
             self.ansatz = QAOAAnsatz(
                 operator, self._reps, initial_state=self._initial_state, mixer_operator=self._mixer
             ).decompose()  # TODO remove decompose once #6674 is fixed
diff --git a/qiskit/circuit/library/evolved_operator_ansatz.py b/qiskit/circuit/library/evolved_operator_ansatz.py
--- a/qiskit/circuit/library/evolved_operator_ansatz.py
+++ b/qiskit/circuit/library/evolved_operator_ansatz.py
@@ -201,8 +201,10 @@ def _validate_prefix(parameter_prefix, operators):
 
 
 def _is_pauli_identity(operator):
-    from qiskit.opflow import PauliOp
+    from qiskit.opflow import PauliOp, PauliSumOp
 
+    if isinstance(operator, PauliSumOp):
+        operator = operator.to_pauli_op()
     if isinstance(operator, PauliOp):
         return not np.any(np.logical_or(operator.primitive.x, operator.primitive.z))
     return False


Test patch:
diff --git a/test/python/algorithms/test_qaoa.py b/test/python/algorithms/test_qaoa.py
--- a/test/python/algorithms/test_qaoa.py
+++ b/test/python/algorithms/test_qaoa.py
@@ -23,7 +23,7 @@
 from qiskit.algorithms import QAOA
 from qiskit.algorithms.optimizers import COBYLA, NELDER_MEAD
 
-from qiskit.opflow import I, X, PauliSumOp
+from qiskit.opflow import I, X, Z, PauliSumOp
 
 from qiskit import BasicAer, QuantumCircuit, QuantumRegister
 
@@ -299,6 +299,17 @@ def test_qaoa_random_initial_point(self):
 
         self.assertLess(result.eigenvalue, -0.97)
 
+    def test_qaoa_construct_circuit_update(self):
+        """Test updating operators with QAOA construct_circuit"""
+        qaoa = QAOA()
+        ref = qaoa.construct_circuit([0, 0], I ^ Z)[0]
+        circ2 = qaoa.construct_circuit([0, 0], I ^ Z)[0]
+        self.assertEqual(circ2, ref)
+        circ3 = qaoa.construct_circuit([0, 0], Z ^ I)[0]
+        self.assertNotEqual(circ3, ref)
+        circ4 = qaoa.construct_circuit([0, 0], I ^ Z)[0]
+        self.assertEqual(circ4, ref)
+
     def _get_operator(self, weight_matrix):
         """Generate Hamiltonian for the max-cut problem of a graph.
 
diff --git a/test/python/circuit/library/test_qaoa_ansatz.py b/test/python/circuit/library/test_qaoa_ansatz.py
--- a/test/python/circuit/library/test_qaoa_ansatz.py
+++ b/test/python/circuit/library/test_qaoa_ansatz.py
@@ -18,7 +18,7 @@
 from qiskit.circuit import QuantumCircuit, Parameter
 from qiskit.circuit.library import HGate, RXGate, YGate, RYGate, RZGate
 from qiskit.circuit.library.n_local.qaoa_ansatz import QAOAAnsatz
-from qiskit.opflow import I, Y, Z
+from qiskit.opflow import I, Y, Z, PauliSumOp
 from qiskit.test import QiskitTestCase
 
 
@@ -158,3 +158,16 @@ def test_num_qubits(self, num_qubits):
 
         circuit = QAOAAnsatz(cost_operator=I ^ num_qubits, reps=5)
         self.assertEqual(circuit.num_qubits, num_qubits)
+
+    def test_identity(self):
+        """Test construction with identity"""
+        reps = 4
+        num_qubits = 3
+        pauli_sum_op = PauliSumOp.from_list([("I" * num_qubits, 1)])
+        pauli_op = I ^ num_qubits
+        for cost in [pauli_op, pauli_sum_op]:
+            for mixer in [None, pauli_op, pauli_sum_op]:
+                with self.subTest(f"cost: {type(cost)}, mixer:{type(mixer)}"):
+                    circuit = QAOAAnsatz(cost_operator=cost, mixer_operator=mixer, reps=reps)
+                    target = reps if mixer is None else 0
+                    self.assertEqual(circuit.num_parameters, target)