def validate_output_shape(self, qnn: OpflowQNN, test_data: List[np.ndarray]) -> None:
        """
        Asserts that the opflow qnn returns results of the correct output shape.

        Args:
            qnn: QNN to be tested
            test_data: list of test input arrays

        Raises:
            QiskitMachineLearningError: Invalid input.
        """

        # get weights
        weights = np.random.rand(qnn.num_weights)

        # iterate over test data and validate behavior of model
        for x in test_data:

            # evaluate network
            forward_shape = qnn.forward(x, weights).shape
            grad = qnn.backward(x, weights)
            backward_shape_input = grad[0].shape
            backward_shape_weights = grad[1].shape

            # derive batch shape form input
            batch_shape = x.shape[:-len(qnn.output_shape)]
            if len(batch_shape) == 0:
                batch_shape = (1,)

            # compare results and assert that the behavior is equal
            self.assertEqual(forward_shape, (*batch_shape, *qnn.output_shape))
            self.assertEqual(backward_shape_input,
                             (*batch_shape, *qnn.output_shape, qnn.num_inputs))
            self.assertEqual(backward_shape_weights,
                             (*batch_shape, *qnn.output_shape, qnn.num_weights))
    def test_composed_op(self):
        """Tests OpflowQNN with ComposedOp as an operator."""
        qc = QuantumCircuit(1)
        param = Parameter("param")
        qc.rz(param, 0)

        h_1 = PauliSumOp.from_list([("Z", 1.0)])
        h_2 = PauliSumOp.from_list([("Z", 1.0)])

        h_op = ListOp([h_1, h_2])
        op = ~StateFn(h_op) @ StateFn(qc)

        # initialize QNN
        qnn = OpflowQNN(op, [], [param])

        # create random data and weights for testing
        input_data = np.random.rand(2, qnn.num_inputs)
        weights = np.random.rand(qnn.num_weights)

        qnn.forward(input_data, weights)
        qnn.backward(input_data, weights)
class TestOpflowQNN(QiskitMachineLearningTestCase):
    """Opflow QNN Tests."""
    def setUp(self):
        super().setUp()

        # specify "run configuration"
        backend = Aer.get_backend('statevector_simulator')
        quantum_instance = QuantumInstance(backend)

        # specify how to evaluate expected values and gradients
        expval = PauliExpectation()
        gradient = Gradient()

        # construct parametrized circuit
        params = [Parameter('input1'), Parameter('weight1')]
        qc = QuantumCircuit(1)
        qc.h(0)
        qc.ry(params[0], 0)
        qc.rx(params[1], 0)
        qc_sfn = StateFn(qc)

        # construct cost operator
        cost_operator = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)]))

        # combine operator and circuit to objective function
        op = ~cost_operator @ qc_sfn

        # define QNN
        self.qnn = OpflowQNN(op, [params[0]], [params[1]],
                             expval,
                             gradient,
                             quantum_instance=quantum_instance)

    def test_opflow_qnn1(self):
        """ Opflow QNN Test """

        input_data = np.zeros(self.qnn.num_inputs)
        weights = np.zeros(self.qnn.num_weights)

        # test forward pass
        result = self.qnn.forward(input_data, weights)
        self.assertEqual(result.shape, (1, *self.qnn.output_shape))

        # test backward pass
        result = self.qnn.backward(input_data, weights)
        self.assertEqual(result[0].shape,
                         (1, self.qnn.num_inputs, *self.qnn.output_shape))
        self.assertEqual(result[1].shape,
                         (1, self.qnn.num_weights, *self.qnn.output_shape))