Repository: Qiskit/qiskit

Issue:
VQE property setter method updates
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

I noticed during the development of this PR (https://github.com/Qiskit/qiskit-nature/pull/380) that the VQE algorithm does not expose all of its attributes via property setters. In particular the following initializer arguments do not have property setters:
- `include_custom`
- `max_evals_grouped`
- `callback`
- `sort_parameters_by_name`

Furthermore, the reversion to default behavior when passing `None` to any of the initializer arguments cannot be achieved via the property setters. E.g. it would be great to have the following:
```python
vqe = VQE(optimizer=SPSA())
# vqe has non-default SPSA optimizer
vqe.optimizer = None
# vqe now falls back to default SLSQP optimizer
```
The  current behavior simply sets the internal optimizer object to `None` resulting in subsequent errors.


Discussion:
Hi! Is it possible to work on this one? :)
@tnemoz, sure go ahead - I'll assign it to you so others know.

Resolving patch:
diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py
--- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py
+++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py
@@ -97,7 +97,6 @@ def __init__(
         max_evals_grouped: int = 1,
         callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None,
         quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None,
-        sort_parameters_by_name: Optional[bool] = None,
     ) -> None:
         """
 
@@ -133,49 +132,30 @@ def __init__(
                 These are: the evaluation count, the optimizer parameters for the
                 ansatz, the evaluated mean and the evaluated standard deviation.`
             quantum_instance: Quantum Instance or Backend
-            sort_parameters_by_name: Deprecated. If True, the initial point is bound to the ansatz
-                parameters strictly sorted by name instead of the default circuit order. That means
-                that the ansatz parameters are e.g. sorted as ``x[0] x[1] x[10] x[2] ...`` instead
-                of ``x[0] x[1] x[2] ... x[10]``. Set this to ``True`` to obtain the behavior prior
-                to Qiskit Terra 0.18.0.
+
         """
         validate_min("max_evals_grouped", max_evals_grouped, 1)
 
-        if sort_parameters_by_name is not None:
-            warnings.warn(
-                "The ``sort_parameters_by_name`` attribute is deprecated and will be "
-                "removed no sooner than 3 months after the release date of Qiskit Terra "
-                "0.18.0.",
-                DeprecationWarning,
-                stacklevel=2,
-            )
-
-        if ansatz is None:
-            ansatz = RealAmplitudes()
-
-        if optimizer is None:
-            optimizer = SLSQP()
-
-        if quantum_instance is not None:
-            if not isinstance(quantum_instance, QuantumInstance):
-                quantum_instance = QuantumInstance(quantum_instance)
-
         super().__init__()
 
         self._max_evals_grouped = max_evals_grouped
         self._circuit_sampler = None  # type: Optional[CircuitSampler]
-        self._expectation = expectation
+        self._expectation = None
+        self.expectation = expectation
         self._include_custom = include_custom
 
         # set ansatz -- still supporting pre 0.18.0 sorting
-        self._sort_parameters_by_name = sort_parameters_by_name
         self._ansatz_params = None
         self._ansatz = None
         self.ansatz = ansatz
 
-        self._optimizer = optimizer
-        self._initial_point = initial_point
-        self._gradient = gradient
+        self._optimizer = None
+        self.optimizer = optimizer
+
+        self._initial_point = None
+        self.initial_point = initial_point
+        self._gradient = None
+        self.gradient = gradient
         self._quantum_instance = None
 
         if quantum_instance is not None:
@@ -183,8 +163,8 @@ def __init__(
 
         self._eval_time = None
         self._eval_count = 0
-        self._optimizer.set_max_evals_grouped(max_evals_grouped)
-        self._callback = callback
+        self._callback = None
+        self.callback = callback
 
         logger.info(self.print_settings())
 
@@ -192,7 +172,7 @@ def __init__(
         self._ret = None
 
     @property
-    def ansatz(self) -> Optional[QuantumCircuit]:
+    def ansatz(self) -> QuantumCircuit:
         """Returns the ansatz."""
         return self._ansatz
 
@@ -202,14 +182,14 @@ def ansatz(self, ansatz: Optional[QuantumCircuit]):
 
         Args:
             ansatz: The parameterized circuit used as an ansatz.
+            If None is passed, RealAmplitudes is used by default.
 
         """
+        if ansatz is None:
+            ansatz = RealAmplitudes()
+
         self._ansatz = ansatz
-        if ansatz is not None:
-            if self._sort_parameters_by_name:
-                self._ansatz_params = sorted(ansatz.parameters, key=lambda p: p.name)
-            else:
-                self._ansatz_params = list(ansatz.parameters)
+        self._ansatz_params = list(ansatz.parameters)
 
     @property
     def gradient(self) -> Optional[Union[GradientBase, Callable]]:
@@ -230,9 +210,10 @@ def quantum_instance(self) -> Optional[QuantumInstance]:
     def quantum_instance(
         self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend]
     ) -> None:
-        """set quantum_instance"""
+        """Sets quantum_instance"""
         if not isinstance(quantum_instance, QuantumInstance):
             quantum_instance = QuantumInstance(quantum_instance)
+
         self._quantum_instance = quantum_instance
         self._circuit_sampler = CircuitSampler(
             quantum_instance, param_qobj=is_aer_provider(quantum_instance.backend)
@@ -248,6 +229,41 @@ def initial_point(self, initial_point: np.ndarray):
         """Sets initial point"""
         self._initial_point = initial_point
 
+    @property
+    def max_evals_grouped(self) -> int:
+        """Returns max_evals_grouped"""
+        return self._max_evals_grouped
+
+    @max_evals_grouped.setter
+    def max_evals_grouped(self, max_evals_grouped: int):
+        """Sets max_evals_grouped"""
+        self._max_evals_grouped = max_evals_grouped
+        self.optimizer.set_max_evals_grouped(max_evals_grouped)
+
+    @property
+    def include_custom(self) -> bool:
+        """Returns include_custom"""
+        return self._include_custom
+
+    @include_custom.setter
+    def include_custom(self, include_custom: bool):
+        """Sets include_custom. If set to another value than the one that was previsously set,
+        the expectation attribute is reset to None.
+        """
+        if include_custom != self._include_custom:
+            self._include_custom = include_custom
+            self.expectation = None
+
+    @property
+    def callback(self) -> Optional[Callable[[int, np.ndarray, float, float], None]]:
+        """Returns callback"""
+        return self._callback
+
+    @callback.setter
+    def callback(self, callback: Optional[Callable[[int, np.ndarray, float, float], None]]):
+        """Sets callback"""
+        self._callback = callback
+
     @property
     def expectation(self) -> Optional[ExpectationBase]:
         """The expectation value algorithm used to construct the expectation measurement from
@@ -274,16 +290,23 @@ def _check_operator_ansatz(self, operator: OperatorBase):
                     ) from ex
 
     @property
-    def optimizer(self) -> Optional[Optimizer]:
+    def optimizer(self) -> Optimizer:
         """Returns optimizer"""
         return self._optimizer
 
     @optimizer.setter
-    def optimizer(self, optimizer: Optimizer):
-        """Sets optimizer"""
+    def optimizer(self, optimizer: Optional[Optimizer]):
+        """Sets the optimizer attribute.
+
+        Args:
+            optimizer: The optimizer to be used. If None is passed, SLSQP is used by default.
+
+        """
+        if optimizer is None:
+            optimizer = SLSQP()
+
+        optimizer.set_max_evals_grouped(self.max_evals_grouped)
         self._optimizer = optimizer
-        if optimizer is not None:
-            optimizer.set_max_evals_grouped(self._max_evals_grouped)
 
     @property
     def setting(self):


Test patch:
diff --git a/test/python/algorithms/test_vqe.py b/test/python/algorithms/test_vqe.py
--- a/test/python/algorithms/test_vqe.py
+++ b/test/python/algorithms/test_vqe.py
@@ -33,7 +33,7 @@
     SPSA,
     TNC,
 )
-from qiskit.circuit.library import EfficientSU2, TwoLocal
+from qiskit.circuit.library import EfficientSU2, RealAmplitudes, TwoLocal
 from qiskit.exceptions import MissingOptionalLibraryError
 from qiskit.opflow import (
     AerPauliExpectation,
@@ -160,14 +160,12 @@ def test_missing_varform_params(self):
     @unpack
     def test_max_evals_grouped(self, optimizer, places, max_evals_grouped):
         """VQE Optimizers test"""
-        with self.assertWarns(DeprecationWarning):
-            vqe = VQE(
-                ansatz=self.ryrz_wavefunction,
-                optimizer=optimizer,
-                max_evals_grouped=max_evals_grouped,
-                quantum_instance=self.statevector_simulator,
-                sort_parameters_by_name=True,
-            )
+        vqe = VQE(
+            ansatz=self.ryrz_wavefunction,
+            optimizer=optimizer,
+            max_evals_grouped=max_evals_grouped,
+            quantum_instance=self.statevector_simulator,
+        )
         result = vqe.compute_minimum_eigenvalue(operator=self.h2_op)
         self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=places)
 
@@ -473,6 +471,26 @@ def wrapped_run(circuits, **kwargs):
 
         self.assertEqual(callcount["count"], expected)
 
+    def test_set_ansatz_to_none(self):
+        """Tests that setting the ansatz to None results in the default behavior"""
+        vqe = VQE(
+            ansatz=self.ryrz_wavefunction,
+            optimizer=L_BFGS_B(),
+            quantum_instance=self.statevector_simulator,
+        )
+        vqe.ansatz = None
+        self.assertIsInstance(vqe.ansatz, RealAmplitudes)
+
+    def test_set_optimizer_to_none(self):
+        """Tests that setting the optimizer to None results in the default behavior"""
+        vqe = VQE(
+            ansatz=self.ryrz_wavefunction,
+            optimizer=L_BFGS_B(),
+            quantum_instance=self.statevector_simulator,
+        )
+        vqe.optimizer = None
+        self.assertIsInstance(vqe.optimizer, SLSQP)
+
     def test_aux_operators_list(self):
         """Test list-based aux_operators."""
         wavefunction = self.ry_wavefunction