# Code source de qiskit_machine_learning.neural_networks.two_layer_qnn

# This code is part of Qiskit.
#
# (C) Copyright IBM 2020, 2022.
#
# obtain a copy of this license in the LICENSE.txt file in the root directory
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""A Two Layer Neural Network consisting of a first parametrized circuit representing a feature map
to map the input data to a quantum states and a second one representing a ansatz that can
be trained to solve a particular tasks."""
from __future__ import annotations

from qiskit import QuantumCircuit
from qiskit.opflow import PauliSumOp, StateFn, OperatorBase, ExpectationBase
from qiskit.providers import Backend
from qiskit.utils import QuantumInstance, deprecate_function

from .opflow_qnn import OpflowQNN
from ..utils import derive_num_qubits_feature_map_ansatz

[docs]class TwoLayerQNN(OpflowQNN):
"""Pending deprecation: Two Layer Quantum Neural Network consisting of a feature map, a ansatz,
and an observable.
"""

@deprecate_function(
"The TwoLayerQNN class is pending deprecation and has no direct replacement. Make use of "
"This class will be deprecated in a future release and subsequently "
"removed after that.",
stacklevel=3,
category=PendingDeprecationWarning,
)
def __init__(
self,
num_qubits: int | None = None,
feature_map: QuantumCircuit | None = None,
ansatz: QuantumCircuit | None = None,
observable: OperatorBase | QuantumCircuit | None = None,
exp_val: ExpectationBase | None = None,
quantum_instance: QuantumInstance | Backend | None = None,
):
r"""
Args:
num_qubits: The number of qubits to represent the network. If None is given,
the number of qubits is derived from the feature map or ansatz. If neither of those
is given, raises an exception. The number of qubits in the feature map and ansatz
are adjusted to this number if required.
feature_map: The (parametrized) circuit to be used as a feature map. If None is given,
the ZZFeatureMap is used if the number of qubits is larger than 1. For
a single qubit two-layer QNN the ZFeatureMap circuit is used per default.
ansatz: The (parametrized) circuit to be used as an ansatz. If None is given,
the RealAmplitudes circuit is used.
observable: observable to be measured to determine the output of the network. If
None is given, the :math:Z^{\otimes num\_qubits} observable is used.
exp_val: The Expected Value converter to be used for the operator obtained from the
feature map and ansatz.
quantum_instance: The quantum instance to evaluate the network.
Note that this parameter is False by default, and must be explicitly set to
True for a proper gradient computation when using
:class:~qiskit_machine_learning.connectors.TorchConnector.
Raises:
QiskitMachineLearningError: Needs at least one out of num_qubits, feature_map or
ansatz to be given. Or the number of qubits in the feature map and/or ansatz
can't be adjusted to num_qubits.
"""

num_qubits, feature_map, ansatz = derive_num_qubits_feature_map_ansatz(
num_qubits, feature_map, ansatz
)

self._feature_map = feature_map
input_params = list(self._feature_map.parameters)

self._ansatz = ansatz
weight_params = list(self._ansatz.parameters)

# construct circuit
self._circuit = QuantumCircuit(num_qubits)
self._circuit.append(self._feature_map, range(num_qubits))
self._circuit.append(self._ansatz, range(num_qubits))

# construct observable
self.observable = (
observable if observable is not None else PauliSumOp.from_list([("Z" * num_qubits, 1)])
)

# combine all to operator
operator = StateFn(self.observable, is_measurement=True) @ StateFn(self._circuit)

super().__init__(
operator=operator,
input_params=input_params,
weight_params=weight_params,
exp_val=exp_val,
quantum_instance=quantum_instance,
)

@property
def feature_map(self) -> QuantumCircuit:
"""Returns the used feature map."""
return self._feature_map

@property
def ansatz(self) -> QuantumCircuit:
"""Returns the used ansatz."""
return self._ansatz

@property
def circuit(self) -> QuantumCircuit:
"""Returns the underlying quantum circuit."""
return self._circuit

@property
def num_qubits(self) -> int:
"""Returns the number of qubits used by ansatz and feature map."""
return self._circuit.num_qubits