def test_predict(self):
        """Test for predict"""
        from torch.utils.data import DataLoader

        data_loader = DataLoader(TorchDataset([1], [1]),
                                 batch_size=1,
                                 shuffle=False)
        model = TorchConnector(self._qnn, [1])
        optimizer = Adam(model.parameters(), lr=0.1)
        loss_func = MSELoss(reduction="sum")
        # Default arguments (no provider or backend)
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
        )
        with self.assertRaises(ValueError):
            torch_runtime_client.predict(data_loader)
        # Test for the predict result
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
            provider=self._infer_provider,
            backend=self._backend,
        )
        result = torch_runtime_client.predict(data_loader)

        self.validate_infer_result(result)
    def test_circuit_qnn_without_parameters(self):
        """Tests CircuitQNN without parameters."""
        quantum_instance = self._sv_quantum_instance
        qc = QuantumCircuit(2)
        param_y = Parameter("y")
        qc.ry(param_y, range(2))

        qnn = CircuitQNN(
            circuit=qc,
            input_params=[param_y],
            sparse=False,
            quantum_instance=quantum_instance,
            input_gradients=True,
        )
        model = TorchConnector(qnn)
        self._validate_backward_pass(model)

        qnn = CircuitQNN(
            circuit=qc,
            weight_params=[param_y],
            sparse=False,
            quantum_instance=quantum_instance,
            input_gradients=True,
        )
        model = TorchConnector(qnn)
        self._validate_backward_pass(model)
示例#3
0
    def __init__(self,
                 input_size: int,
                 hidden_size: int,
                 n_qubits: int = 4,
                 n_qlayers: int = 1,
                 batch_first=True,
                 backend='statevector_simulator'):
        super(QLSTM, self).__init__()

        self.input_size = input_size
        self.hidden_size = hidden_size
        self.concat_size = input_size + hidden_size
        self.n_qubits = n_qubits
        self.n_qlayers = n_qlayers
        self.batch_first = batch_first

        self.clayer_in = nn.Linear(self.concat_size, n_qubits)
        self.clayer_out = nn.Linear(self.n_qubits, self.hidden_size)

        self.qi = QuantumInstance(Aer.get_backend('statevector_simulator'))
        feature_map = ZZFeatureMap(self.n_qubits)
        ansatz = RealAmplitudes(self.n_qubits, reps=self.n_qlayers)

        self.qnn1 = TwoLayerQNN(self.n_qubits,
                                feature_map,
                                ansatz,
                                exp_val=AerPauliExpectation(),
                                quantum_instance=self.qi)
        self.qnn2 = TwoLayerQNN(self.n_qubits,
                                feature_map,
                                ansatz,
                                exp_val=AerPauliExpectation(),
                                quantum_instance=self.qi)
        self.qnn3 = TwoLayerQNN(self.n_qubits,
                                feature_map,
                                ansatz,
                                exp_val=AerPauliExpectation(),
                                quantum_instance=self.qi)
        self.qnn4 = TwoLayerQNN(self.n_qubits,
                                feature_map,
                                ansatz,
                                exp_val=AerPauliExpectation(),
                                quantum_instance=self.qi)

        self.qlayer = {
            'forget': TorchConnector(self.qnn1),
            'input': TorchConnector(self.qnn2),
            'update': TorchConnector(self.qnn3),
            'output': TorchConnector(self.qnn4)
        }
    def _validate_output_shape(self, model: TorchConnector,
                               test_data: List) -> None:
        """Creates a Linear PyTorch module with the same in/out dimensions as the given model,
        applies the list of test input data to both, and asserts that they have the same
        output shape.

        Args:
            model: model to be tested
            test_data: list of test input tensors

        Raises:
            QiskitMachineLearningError: Invalid input.
        """
        from torch.nn import Linear

        # create benchmark model
        in_dim = model.neural_network.num_inputs
        if len(model.neural_network.output_shape) != 1:
            raise QiskitMachineLearningError(
                "Function only works for one dimensional output")
        out_dim = model.neural_network.output_shape[0]
        # we target our tests to either cpu or gpu
        linear = Linear(in_dim, out_dim, device=self._device)
        model.to(self._device)

        # iterate over test data and validate behavior of model
        for x in test_data:
            x = x.to(self._device)
            # test linear model and track whether it failed or store the output shape
            c_worked = True
            try:
                c_shape = linear(x).shape
            except Exception:  # pylint: disable=broad-except
                c_worked = False

            # test quantum model and track whether it failed or store the output shape
            q_worked = True
            try:
                q_shape = model(x).shape
            except Exception:  # pylint: disable=broad-except
                q_worked = False

            # compare results and assert that the behavior is equal
            with self.subTest("c_worked == q_worked", tensor=x):
                self.assertEqual(c_worked, q_worked)
            if c_worked and q_worked:
                with self.subTest("c_shape == q_shape", tensor=x):
                    self.assertEqual(c_shape, q_shape)
示例#5
0
    def test_opflow_qnn_2_1(self, q_i):
        """ Test Torch Connector + Opflow QNN with input/output dimension 2/1."""

        if q_i == 'sv':
            quantum_instance = self.sv_quantum_instance
        else:
            quantum_instance = self.qasm_quantum_instance

        # construct QNN
        qnn = TwoLayerQNN(2, quantum_instance=quantum_instance)
        try:
            model = TorchConnector(qnn)

            test_data = [
                Tensor(1),
                Tensor([1, 2]),
                Tensor([[1, 2]]),
                Tensor([[1], [2]]),
                Tensor([[[1], [2]], [[3], [4]]])
            ]

            # test model
            self.validate_output_shape(model, test_data)
            if q_i == 'sv':
                self.validate_backward_pass(model)
        except MissingOptionalLibraryError as ex:
            self.skipTest(str(ex))
示例#6
0
    def __init__(self, h, w, outputs):
        super(DQN, self).__init__()

        qi = QuantumInstance(Aer.get_backend('statevector_simulator'))

        num_inputs=4

        feature_map = ZZFeatureMap(num_inputs)
        ansatz = RealAmplitudes(num_inputs, entanglement='linear', reps=1)

        qc = QuantumCircuit(num_inputs)
        qc.append(feature_map, range(num_inputs))
        qc.append(ansatz, range(num_inputs))

        parity = lambda x: '{:b}'.format(x).count('1') % num_inputs
        output_shape =num_inputs # parity = 0, 1

        qnn = CircuitQNN(qc, input_params=feature_map.parameters, weight_params=ansatz.parameters,
                interpret=parity, output_shape=output_shape, quantum_instance=qi)

        #set up PyTorch Module
        initial_weights = 0.1*(2*np.random.rand(qnn.num_weights) - 1)

        # Our final network choice. It is wide enough to capture a lot of detail but not too
        # large to have problems with vanishing gradients on such a small sample size
        self.conv1 = nn.Conv2d(1, 256, kernel_size=2, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(256)
        self.fcl1 = nn.Linear(20736,10000)
        self.fcl2 = nn.Linear(10000, num_inputs)
        self.qnn = TorchConnector(qnn, initial_weights)
示例#7
0
    def test_circuit_qnn_sampling(self, interpret):
        """Test Torch Connector + Circuit QNN for sampling."""

        qc = QuantumCircuit(2)

        # construct simple feature map
        param_x1, param_x2 = Parameter('x1'), Parameter('x2')
        qc.ry(param_x1, range(2))
        qc.ry(param_x2, range(2))

        # construct simple feature map
        param_y = Parameter('y')
        qc.ry(param_y, range(2))

        qnn = CircuitQNN(qc, [param_x1, param_x2], [param_y],
                         sparse=False,
                         sampling=True,
                         interpret=interpret,
                         output_shape=None,
                         quantum_instance=self.qasm_quantum_instance)
        model = TorchConnector(qnn)

        test_data = [Tensor([2, 2]), Tensor([[1, 1], [2, 2]])]
        for i, x in enumerate(test_data):
            if i == 0:
                self.assertEqual(model(x).shape, qnn.output_shape)
            else:
                shape = model(x).shape
                self.assertEqual(shape, (len(x), *qnn.output_shape))
    def test_opflow_qnn_2_1(self, q_i):
        """Test Torch Connector + Opflow QNN with input/output dimension 2/1."""
        from torch import Tensor

        if q_i == "sv":
            quantum_instance = self._sv_quantum_instance
        else:
            quantum_instance = self._qasm_quantum_instance

        # construct QNN
        qnn = TwoLayerQNN(2,
                          quantum_instance=quantum_instance,
                          input_gradients=True)
        model = TorchConnector(qnn)

        test_data = [
            Tensor([1]),
            Tensor([1, 2]),
            Tensor([[1, 2]]),
            Tensor([[1], [2]]),
            Tensor([[[1], [2]], [[3], [4]]]),
        ]

        # test model
        self._validate_output_shape(model, test_data)
        if q_i == "sv":
            self._validate_backward_pass(model)
示例#9
0
 def __init__(self):
     super().__init__()
     self.fc1 = Linear(4, 2)
     self.qnn = TorchConnector(
         qnn, np.array([0.1] * qnn.num_weights)
     )  # Apply torch connector
     self.fc2 = Linear(output_size, 1)  # shape depends on the type of the QNN
    def test_opflow_qnn_2_2(self, q_i):
        """Test Torch Connector + Opflow QNN with input/output dimension 2/2."""
        from torch import Tensor

        if q_i == "sv":
            quantum_instance = self._sv_quantum_instance
        else:
            quantum_instance = self._qasm_quantum_instance

        # construct parametrized circuit
        params_1 = [Parameter("input1"), Parameter("weight1")]
        qc_1 = QuantumCircuit(1)
        qc_1.h(0)
        qc_1.ry(params_1[0], 0)
        qc_1.rx(params_1[1], 0)
        qc_sfn_1 = StateFn(qc_1)

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

        # combine operator and circuit to objective function
        op_1 = ~h_1 @ qc_sfn_1

        # construct parametrized circuit
        params_2 = [Parameter("input2"), Parameter("weight2")]
        qc_2 = QuantumCircuit(1)
        qc_2.h(0)
        qc_2.ry(params_2[0], 0)
        qc_2.rx(params_2[1], 0)
        qc_sfn_2 = StateFn(qc_2)

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

        # combine operator and circuit to objective function
        op_2 = ~h_2 @ qc_sfn_2

        op = ListOp([op_1, op_2])

        qnn = OpflowQNN(
            op,
            [params_1[0], params_2[0]],
            [params_1[1], params_2[1]],
            quantum_instance=quantum_instance,
            input_gradients=True,
        )
        model = TorchConnector(qnn)

        test_data = [
            Tensor([1]),
            Tensor([1, 2]),
            Tensor([[1], [2]]),
            Tensor([[1, 2], [3, 4]]),
        ]

        # test model
        self._validate_output_shape(model, test_data)
        if q_i == "sv":
            self._validate_backward_pass(model)
示例#11
0
    def test_opflow_qnn_2_2(self, q_i):
        """ Test Torch Connector + Opflow QNN with input/output dimension 2/2."""

        if q_i == 'sv':
            quantum_instance = self.sv_quantum_instance
        else:
            quantum_instance = self.qasm_quantum_instance

        # construct parametrized circuit
        params_1 = [Parameter('input1'), Parameter('weight1')]
        qc_1 = QuantumCircuit(1)
        qc_1.h(0)
        qc_1.ry(params_1[0], 0)
        qc_1.rx(params_1[1], 0)
        qc_sfn_1 = StateFn(qc_1)

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

        # combine operator and circuit to objective function
        op_1 = ~h_1 @ qc_sfn_1

        # construct parametrized circuit
        params_2 = [Parameter('input2'), Parameter('weight2')]
        qc_2 = QuantumCircuit(1)
        qc_2.h(0)
        qc_2.ry(params_2[0], 0)
        qc_2.rx(params_2[1], 0)
        qc_sfn_2 = StateFn(qc_2)

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

        # combine operator and circuit to objective function
        op_2 = ~h_2 @ qc_sfn_2

        op = ListOp([op_1, op_2])

        qnn = OpflowQNN(op, [params_1[0], params_2[0]],
                        [params_1[1], params_2[1]],
                        quantum_instance=quantum_instance)
        try:
            model = TorchConnector(qnn)

            test_data = [
                Tensor(1),
                Tensor([1, 2]),
                Tensor([[1], [2]]),
                Tensor([[1, 2], [3, 4]])
            ]

            # test model
            self.validate_output_shape(model, test_data)
            if q_i == 'sv':
                self.validate_backward_pass(model)
        except MissingOptionalLibraryError as ex:
            self.skipTest(str(ex))
    def test_fit(self):
        """Test for fit"""
        from torch.utils.data import DataLoader

        train_loader = DataLoader(TorchDataset([1], [1]),
                                  batch_size=1,
                                  shuffle=False)
        model = TorchConnector(self._qnn, [1])
        optimizer = Adam(model.parameters(), lr=0.1)
        loss_func = MSELoss(reduction="sum")
        # Default arguments (no provider or backend)
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
        )
        with self.assertRaises(ValueError):
            torch_runtime_client.fit(train_loader)

        # Default arguments for fit
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
            provider=self._trainer_provider,
            backend=self._backend,
        )
        result = torch_runtime_client.fit(train_loader)
        self.validate_train_result(result)

        # Specify arguments
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
            provider=self._trainer_provider,
            epochs=1,
            backend=self._backend,
            shots=1024,
        )
        result = torch_runtime_client.fit(train_loader, start_epoch=0, seed=42)
        self.validate_train_result(result)
    def test_score(self):
        """Test for score"""
        from torch.utils.data import DataLoader

        data_loader = DataLoader(TorchDataset([1], [1]),
                                 batch_size=1,
                                 shuffle=False)
        model = TorchConnector(self._qnn, [1])
        optimizer = Adam(model.parameters(), lr=0.1)
        loss_func = MSELoss(reduction="sum")
        # Default arguments (no provider or backend)
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
        )
        with self.assertRaises(ValueError):
            torch_runtime_client.score(data_loader, score_func="regression")
        # Test for the score result
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
            provider=self._infer_provider,
            backend=self._backend,
        )
        # Test different score functions
        result = torch_runtime_client.score(data_loader,
                                            score_func="regression")
        self.validate_infer_result(result, score=True)
        result = torch_runtime_client.score(data_loader,
                                            score_func="classification")
        self.validate_infer_result(result, score=True)

        def score_classification(output: Tensor, target: Tensor) -> float:
            pred = output.argmax(dim=1, keepdim=True)
            correct = pred.eq(target.view_as(pred)).sum().item()
            return correct

        result = torch_runtime_client.score(data_loader,
                                            score_func=score_classification)
        self.validate_infer_result(result, score=True)
    def test_fit_with_validation_set(self):
        """Test for fit with a validation set"""
        from torch.utils.data import DataLoader

        train_loader = DataLoader(TorchDataset([1], [1]),
                                  batch_size=1,
                                  shuffle=False)
        model = TorchConnector(self._qnn, [1])
        optimizer = Adam(model.parameters(), lr=0.1)
        loss_func = MSELoss(reduction="sum")
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
            provider=self._trainer_provider,
            backend=self._backend,
        )
        validation_loader = DataLoader(TorchDataset([0], [0]),
                                       batch_size=1,
                                       shuffle=False)
        result = torch_runtime_client.fit(train_loader,
                                          val_loader=validation_loader)
        self.validate_train_result(result, val_loader=True)
    def test_circuit_qnn_2_4(self, config):
        """Torch Connector + Circuit QNN with no interpret, dense output,
        and input/output shape 1/8 ."""
        from torch import Tensor

        interpret, output_shape, sparse, q_i = config
        if sparse and not _optionals.HAS_SPARSE:
            self.skipTest("sparse library is required to run this test")
            return
        if q_i == "sv":
            quantum_instance = self._sv_quantum_instance
        else:
            quantum_instance = self._qasm_quantum_instance

        qc = QuantumCircuit(2)

        # construct simple feature map
        param_x_1, param_x_2 = Parameter("x1"), Parameter("x2")
        qc.ry(param_x_1, range(2))
        qc.ry(param_x_2, range(2))

        # construct simple feature map
        param_y = Parameter("y")
        qc.ry(param_y, range(2))

        qnn = CircuitQNN(
            qc,
            [param_x_1, param_x_2],
            [param_y],
            sparse=sparse,
            sampling=False,
            interpret=interpret,
            output_shape=output_shape,
            quantum_instance=quantum_instance,
            input_gradients=True,
        )
        model = TorchConnector(qnn)

        test_data = [
            Tensor([1]),
            Tensor([1, 2]),
            Tensor([[1], [2]]),
            Tensor([[1, 2], [3, 4]]),
            Tensor([[[1], [2]], [[3], [4]]]),
        ]

        # test model
        self._validate_output_shape(model, test_data)
        if q_i == "sv":
            self._validate_backward_pass(model)
    def test_fit_with_hooks(self):
        """Test for fit with hooks"""
        from torch.utils.data import DataLoader

        train_loader = DataLoader(TorchDataset([1], [1]),
                                  batch_size=1,
                                  shuffle=False)
        model = TorchConnector(self._qnn, [1])
        optimizer = Adam(model.parameters(), lr=0.1)
        loss_func = MSELoss(reduction="sum")
        torch_runtime_client = TorchRuntimeClient(
            model=model,
            optimizer=optimizer,
            loss_func=loss_func,
            provider=self._trainer_provider,
            backend=self._backend,
        )
        hook = HookBase()
        # a single hook
        result = torch_runtime_client.fit(train_loader, hooks=hook)
        self.validate_train_result(result)
        # a hook list
        result = torch_runtime_client.fit(train_loader, hooks=[hook, hook])
        self.validate_train_result(result)
    def __init__(self, h, w, outputs):
        super(DQN, self).__init__()
        # We did a lot of experimentation with model sizes and types, so we decided to leave some
        # of our attempts here
        #1
        # self.conv1 = nn.Conv2d(1, 16, kernel_size=5, stride=2)
        # self.bn1 = nn.BatchNorm2d(16)
        # self.conv2 = nn.Conv2d(16, 32, kernel_size=5, stride=2)
        # self.bn2 = nn.BatchNorm2d(32)
        # self.conv3 = nn.Conv2d(32, 32, kernel_size=5, stride=2)
        # self.bn3 = nn.BatchNorm2d(32)

        #2
        # # Number of Linear input connections depends on output of conv2d layers
        # # and therefore the input image size, so compute it.
        # def conv2d_size_out(size, kernel_size = 5, stride = 2):
        #     return (size - (kernel_size - 1) - 1) // stride  + 1
        # convw = conv2d_size_out(conv2d_size_out(conv2d_size_out(w)))
        # convh = conv2d_size_out(conv2d_size_out(conv2d_size_out(h)))
        # linear_input_size = convw * convh * 32

        #3
        # self.conv1 = nn.Conv2d(1, 128, kernel_size=2, stride=1, padding=1)
        # self.bn1 = nn.BatchNorm2d(128)
        # self.conv2 = nn.Conv2d(128, 256, kernel_size=5, stride=1, padding=1)
        # self.bn2 = nn.BatchNorm2d(256)
        # self.fcl1 = nn.Linear(12544,5000)
        # self.fcl2 = nn.Linear(5000, 64)
        qi = QuantumInstance(Aer.get_backend('statevector_simulator'))

        feature_map = ZZFeatureMap(4)
        ansatz = RealAmplitudes(4, reps=1)
        qnn = TwoLayerQNN(4,
                          feature_map,
                          ansatz,
                          exp_val=AerPauliExpectation(),
                          quantum_instance=qi)

        #4
        # Our final network choice. It is wide enough to capture a lot of detail but not too
        # large to have problems with vanishing gradients on such a small sample size
        self.conv1 = nn.Conv2d(1, 256, kernel_size=2, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(256)
        self.fcl1 = nn.Linear(20736, 10000)
        self.fcl2 = nn.Linear(10000, 4)
        self.qnn = TorchConnector(qnn)
示例#18
0
    def test_circuit_qnn_2_4(self, config):
        """Torch Connector + Circuit QNN with no interpret, dense output,
        and input/output shape 1/8 ."""

        interpret, output_shape, sparse, q_i = config
        if q_i == 'sv':
            quantum_instance = self.sv_quantum_instance
        else:
            quantum_instance = self.qasm_quantum_instance

        qc = QuantumCircuit(2)

        # construct simple feature map
        param_x_1, param_x_2 = Parameter('x1'), Parameter('x2')
        qc.ry(param_x_1, range(2))
        qc.ry(param_x_2, range(2))

        # construct simple feature map
        param_y = Parameter('y')
        qc.ry(param_y, range(2))

        qnn = CircuitQNN(qc, [param_x_1, param_x_2], [param_y],
                         sparse=sparse,
                         sampling=False,
                         interpret=interpret,
                         output_shape=output_shape,
                         quantum_instance=quantum_instance)
        try:
            model = TorchConnector(qnn)

            test_data = [
                Tensor(1),
                Tensor([1, 2]),
                Tensor([[1], [2]]),
                Tensor([[1, 2], [3, 4]]),
                Tensor([[[1], [2]], [[3], [4]]])
            ]

            # test model
            self.validate_output_shape(model, test_data)
            if q_i == 'sv':
                self.validate_backward_pass(model)
        except MissingOptionalLibraryError as ex:
            self.skipTest(str(ex))
示例#19
0
    def test_opflow_qnn_1_1(self, q_i):
        """ Test Torch Connector + Opflow QNN with input/output dimension 1/1."""

        if q_i == 'sv':
            quantum_instance = self.sv_quantum_instance
        else:
            quantum_instance = self.qasm_quantum_instance

        # construct simple feature map
        param_x = Parameter('x')
        feature_map = QuantumCircuit(1, name='fm')
        feature_map.ry(param_x, 0)

        # construct simple feature map
        param_y = Parameter('y')
        ansatz = QuantumCircuit(1, name='vf')
        ansatz.ry(param_y, 0)

        # construct QNN with statevector simulator
        qnn = TwoLayerQNN(1,
                          feature_map,
                          ansatz,
                          quantum_instance=quantum_instance)
        try:
            model = TorchConnector(qnn)

            test_data = [
                Tensor(1),
                Tensor([1]),
                Tensor([1, 2]),
                Tensor([[1], [2]]),
                Tensor([[[1], [2]], [[3], [4]]])
            ]

            # test model
            self.validate_output_shape(model, test_data)
            if q_i == 'sv':
                self.validate_backward_pass(model)
        except MissingOptionalLibraryError as ex:
            self.skipTest(str(ex))
示例#20
0
    def test_circuit_qnn_1_8(self, config):
        """Torch Connector + Circuit QNN with no interpret, dense output,
        and input/output shape 1/8 ."""

        interpret, output_shape, sparse, q_i = config
        if q_i == 'sv':
            quantum_instance = self.sv_quantum_instance
        else:
            quantum_instance = self.qasm_quantum_instance

        qc = QuantumCircuit(3)

        # construct simple feature map
        param_x = Parameter('x')
        qc.ry(param_x, range(3))

        # construct simple feature map
        param_y = Parameter('y')
        qc.ry(param_y, range(3))

        qnn = CircuitQNN(qc, [param_x], [param_y],
                         sparse=sparse,
                         sampling=False,
                         interpret=interpret,
                         output_shape=output_shape,
                         quantum_instance=quantum_instance)
        model = TorchConnector(qnn)

        test_data = [
            Tensor(1),
            Tensor([1, 2]),
            Tensor([[1], [2]]),
            Tensor([[[1], [2]], [[3], [4]]])
        ]

        # test model
        self.validate_output_shape(model, test_data)
        if q_i == 'sv':
            self.validate_backward_pass(model)
    def test_opflow_qnn_1_1(self, q_i):
        """Test Torch Connector + Opflow QNN with input/output dimension 1/1."""
        from torch import Tensor

        if q_i == "sv":
            quantum_instance = self._sv_quantum_instance
        else:
            quantum_instance = self._qasm_quantum_instance

        # construct simple feature map
        param_x = Parameter("x")
        feature_map = QuantumCircuit(1, name="fm")
        feature_map.ry(param_x, 0)

        # construct simple feature map
        param_y = Parameter("y")
        ansatz = QuantumCircuit(1, name="vf")
        ansatz.ry(param_y, 0)

        # construct QNN with statevector simulator
        qnn = TwoLayerQNN(1,
                          feature_map,
                          ansatz,
                          quantum_instance=quantum_instance,
                          input_gradients=True)
        model = TorchConnector(qnn)

        test_data = [
            Tensor([1]),
            Tensor([1, 2]),
            Tensor([[1], [2]]),
            Tensor([[[1], [2]], [[3], [4]]]),
        ]

        # test model
        self._validate_output_shape(model, test_data)
        if q_i == "sv":
            self._validate_backward_pass(model)
示例#22
0
    def test_batch_gradients(self):
        """Test backward pass for batch input."""

        # construct random data set
        num_inputs = 2
        num_samples = 10
        x = np.random.rand(num_samples, num_inputs)

        # set up QNN
        qnn = TwoLayerQNN(num_qubits=num_inputs,
                          quantum_instance=self.sv_quantum_instance)

        # set up PyTorch module
        initial_weights = np.random.rand(qnn.num_weights)
        model = TorchConnector(qnn, initial_weights=initial_weights)

        # test single gradient
        w = model.weights.detach().numpy()
        res_qnn = qnn.forward(x[0, :], w)

        # construct finite difference gradient for weights
        eps = 1e-4
        grad = np.zeros(w.shape)
        for k in range(len(w)):
            delta = np.zeros(w.shape)
            delta[k] += eps

            f_1 = qnn.forward(x[0, :], w + delta)
            f_2 = qnn.forward(x[0, :], w - delta)

            grad[k] = (f_1 - f_2) / (2 * eps)

        grad_qnn = qnn.backward(x[0, :], w)[1][0, 0, :]
        self.assertAlmostEqual(np.linalg.norm(grad - grad_qnn), 0.0, places=4)

        model.zero_grad()
        res_model = model(Tensor(x[0, :]))
        self.assertAlmostEqual(np.linalg.norm(res_model.detach().numpy() -
                                              res_qnn[0]),
                               0.0,
                               places=4)
        res_model.backward()
        grad_model = model.weights.grad
        self.assertAlmostEqual(np.linalg.norm(grad_model.detach().numpy() -
                                              grad_qnn),
                               0.0,
                               places=4)

        # test batch input
        batch_grad = np.zeros((*w.shape, num_samples, 1))
        for k in range(len(w)):
            delta = np.zeros(w.shape)
            delta[k] += eps

            f_1 = qnn.forward(x, w + delta)
            f_2 = qnn.forward(x, w - delta)

            batch_grad[k] = (f_1 - f_2) / (2 * eps)

        batch_grad = np.sum(batch_grad, axis=1)
        batch_grad_qnn = np.sum(qnn.backward(x, w)[1], axis=0)
        self.assertAlmostEqual(np.linalg.norm(batch_grad -
                                              batch_grad_qnn.transpose()),
                               0.0,
                               places=4)

        model.zero_grad()
        batch_res_model = sum(model(Tensor(x)))
        batch_res_model.backward()
        self.assertAlmostEqual(np.linalg.norm(model.weights.grad.numpy() -
                                              batch_grad.transpose()[0]),
                               0.0,
                               places=4)
    def test_circuit_qnn_batch_gradients(self, config):
        """Test batch gradient computation of CircuitQNN gives the same result as the sum of
        individual gradients."""
        import torch
        from torch.nn import MSELoss
        from torch.optim import SGD

        output_shape, interpret = config
        num_inputs = 2

        feature_map = ZZFeatureMap(num_inputs)
        ansatz = RealAmplitudes(num_inputs, entanglement="linear", reps=1)

        qc = QuantumCircuit(num_inputs)
        qc.append(feature_map, range(num_inputs))
        qc.append(ansatz, range(num_inputs))

        qnn = CircuitQNN(
            qc,
            input_params=feature_map.parameters,
            weight_params=ansatz.parameters,
            interpret=interpret,
            output_shape=output_shape,
            quantum_instance=self._sv_quantum_instance,
        )

        # set up PyTorch module
        initial_weights = np.array([0.1] * qnn.num_weights)
        model = TorchConnector(qnn, initial_weights)
        model.to(self._device)

        # random data set
        x = torch.rand(5, 2)
        y = torch.rand(5, output_shape)

        # define optimizer and loss
        optimizer = SGD(model.parameters(), lr=0.1)
        f_loss = MSELoss(reduction="sum")

        sum_of_individual_losses = 0.0
        for x_i, y_i in zip(x, y):
            x_i = x_i.to(self._device)
            y_i = y_i.to(self._device)
            output = model(x_i)
            sum_of_individual_losses += f_loss(output, y_i)
        optimizer.zero_grad()
        sum_of_individual_losses.backward()
        sum_of_individual_gradients = model.weight.grad.detach().cpu()

        x = x.to(self._device)
        y = y.to(self._device)
        output = model(x)
        batch_loss = f_loss(output, y)
        optimizer.zero_grad()
        batch_loss.backward()
        batch_gradients = model.weight.grad.detach().cpu()

        self.assertAlmostEqual(np.linalg.norm(sum_of_individual_gradients -
                                              batch_gradients),
                               0.0,
                               places=4)

        self.assertAlmostEqual(
            sum_of_individual_losses.detach().cpu().numpy(),
            batch_loss.detach().cpu().numpy(),
            places=4,
        )