예제 #1
0
    def test_evaluate_qnode_default_input(self, get_circuit, output_dim, n_qubits):
        """Test if the _evaluate_qnode() method works correctly when the inputs argument is a
        default argument, i.e., that it gives the same result as calling the QNode directly"""
        c, w = get_circuit

        @qml.qnode(qml.device("default.qubit", wires=n_qubits), interface="torch")
        def c_default(w1, w2, w3, w4, w5, w6, w7, inputs=None):
            """Version of the circuit with inputs as a default argument"""
            qml.templates.AngleEmbedding(inputs, wires=list(range(n_qubits)))
            qml.templates.StronglyEntanglingLayers(w1, wires=list(range(n_qubits)))
            qml.RX(w2[0], wires=0)
            qml.RX(w3, wires=0)
            qml.Rot(*w4, wires=0)
            qml.templates.StronglyEntanglingLayers(w5, wires=list(range(n_qubits)))
            qml.Rot(*w6, wires=0)
            qml.RX(w7, wires=0)
            return [qml.expval(qml.PauliZ(i)) for i in range(output_dim)]

        layer = TorchLayer(c_default, w)
        x = torch.Tensor(np.ones(n_qubits))

        layer_out = layer._evaluate_qnode(x).detach().numpy()

        weights = [layer.qnode_weights[weight].detach().numpy() for weight in ordered_weights]

        circuit_out = c(x, *weights)
        assert np.allclose(layer_out, circuit_out)
예제 #2
0
    def test_str_repr(self, get_circuit):
        """Test the __str__ and __repr__ representations"""
        c, w = get_circuit
        layer = TorchLayer(c, w)

        assert layer.__str__() == "<Quantum Torch Layer: func=circuit>"
        assert layer.__repr__() == "<Quantum Torch Layer: func=circuit>"
예제 #3
0
    def test_evaluate_qnode_shuffled_args(self, get_circuit, output_dim,
                                          n_qubits):
        """Test if the _evaluate_qnode() method works correctly when the inputs argument is not the
        first positional argument, i.e., that it gives the same result as calling the QNode
        directly"""
        c, w = get_circuit

        @qml.qnode(qml.device("default.qubit", wires=n_qubits),
                   interface="torch")
        def c_shuffled(w1, inputs, w2, w3, w4, w5, w6, w7):
            """Version of the circuit with a shuffled signature"""
            qml.templates.AngleEmbedding(inputs, wires=list(range(n_qubits)))
            qml.templates.StronglyEntanglingLayers(w1,
                                                   wires=list(range(n_qubits)))
            qml.RX(w2[0], wires=0)
            qml.RX(w3, wires=0)
            qml.Rot(*w4, wires=0)
            qml.templates.StronglyEntanglingLayers(w5,
                                                   wires=list(range(n_qubits)))
            qml.Rot(*w6, wires=0)
            qml.RX(w7, wires=0)
            return [qml.expval(qml.PauliZ(i)) for i in range(output_dim)]

        layer = TorchLayer(c_shuffled, w)
        x = torch.Tensor(np.ones(n_qubits))

        layer_out = layer._evaluate_qnode(x)
        weights = layer.qnode_weights.values()
        circuit_out = c(x, *weights).type(x.dtype)

        assert torch.allclose(layer_out, circuit_out)
예제 #4
0
 def __init__(self):
     super().__init__()
     self.clayer1 = torch.nn.Linear(n_qubits, n_qubits)
     self.clayer2 = torch.nn.Linear(output_dim, n_qubits)
     self.clayer3 = torch.nn.Linear(output_dim, output_dim)
     self.qlayer1 = TorchLayer(c, w)
     self.qlayer2 = TorchLayer(c, w)
예제 #5
0
    def test_var_keyword(self, n_qubits, output_dim):
        """Test that variable number of keyword arguments works"""
        dev = qml.device("default.qubit", wires=n_qubits)
        w = {
            "w1": (3, n_qubits, 3),
            "w2": (1,),
            "w3": 1,
            "w4": [3],
            "w5": (2, n_qubits, 3),
            "w6": 3,
            "w7": 0,
        }

        @qml.qnode(dev, interface="torch")
        def c(inputs, **kwargs):
            """A circuit that embeds data using the AngleEmbedding and then performs a variety of
            operations. The output is a PauliZ measurement on the first output_dim qubits. One set of
            parameters, w5, are specified as non-trainable."""
            qml.templates.AngleEmbedding(inputs, wires=list(range(n_qubits)))
            qml.templates.StronglyEntanglingLayers(kwargs["w1"], wires=list(range(n_qubits)))
            qml.RX(kwargs["w2"][0], wires=0 % n_qubits)
            qml.RX(kwargs["w3"], wires=1 % n_qubits)
            qml.Rot(*kwargs["w4"], wires=2 % n_qubits)
            qml.templates.StronglyEntanglingLayers(kwargs["w5"], wires=list(range(n_qubits)))
            qml.Rot(*kwargs["w6"], wires=3 % n_qubits)
            qml.RX(kwargs["w7"], wires=4 % n_qubits)
            return [qml.expval(qml.PauliZ(i)) for i in range(output_dim)]

        layer = TorchLayer(c, w)
        x = torch.ones(n_qubits)

        layer_out = layer._evaluate_qnode(x)
        circuit_out = c(x, **layer.qnode_weights).type(x.dtype)

        assert torch.allclose(layer_out, circuit_out)
예제 #6
0
    def test_forward_single_input(self, get_circuit, output_dim, n_qubits):
        """Test if the forward() method accepts a single input (i.e., not with an extra batch
        dimension) and returns a tensor of the right shape"""
        c, w = get_circuit
        layer = TorchLayer(c, w)
        x = torch.Tensor(np.ones(n_qubits))

        layer_out = layer.forward(x)
        assert layer_out.shape == torch.Size((output_dim,))
예제 #7
0
    def test_forward(self, get_circuit, output_dim, n_qubits):
        """Test if the forward() method accepts a batched input and returns a tensor of the right
        shape"""
        c, w = get_circuit
        layer = TorchLayer(c, w)
        x = torch.Tensor(np.ones((2, n_qubits)))

        layer_out = layer.forward(x)
        assert layer_out.shape == torch.Size((2, output_dim))
예제 #8
0
    def test_evaluate_qnode(self, get_circuit, n_qubits):
        """Test if the _evaluate_qnode() method works correctly, i.e., that it gives the same
        result as calling the QNode directly"""
        c, w = get_circuit
        layer = TorchLayer(c, w)
        x = torch.ones(n_qubits)

        layer_out = layer._evaluate_qnode(x)
        weights = layer.qnode_weights.values()
        circuit_out = c(x, *weights).type(x.dtype)

        assert torch.allclose(layer_out, circuit_out)
예제 #9
0
    def test_evaluate_qnode(self, get_circuit, n_qubits):
        """Test if the _evaluate_qnode() method works correctly, i.e., that it gives the same
        result as calling the QNode directly"""
        c, w = get_circuit
        layer = TorchLayer(c, w)
        x = torch.ones(n_qubits)

        layer_out = layer._evaluate_qnode(x).detach().numpy()

        weights = [layer.qnode_weights[weight].detach().numpy() for weight in ordered_weights]

        circuit_out = c(x, *weights)
        assert np.allclose(layer_out, circuit_out)
예제 #10
0
 def test_weight_shape_unspecified(self, get_circuit):
     """Test if a ValueError is raised when instantiated with a weight missing from the
     weight_shapes dictionary"""
     c, w = get_circuit
     del w["w1"]
     with pytest.raises(ValueError, match="Must specify a shape for every non-input parameter"):
         TorchLayer(c, w)
예제 #11
0
    def test_forward_broadcasting(self, get_circuit, output_dim, middle_dim, batch_size, n_qubits):
        """Test if the forward() method accepts a batched input with multiple dimensions and returns a tensor of the
        right shape by broadcasting. Also tests if gradients are still backpropagated correctly."""
        c, w = get_circuit
        layer = TorchLayer(c, w)
        x = torch.Tensor(np.ones((batch_size, middle_dim, n_qubits)))

        weights = layer.qnode_weights.values()

        layer_out = layer.forward(x)
        layer_out.backward(torch.ones_like(layer_out))

        g_layer = [w.grad for w in weights]

        assert g_layer.count(None) == 0
        assert layer_out.shape == torch.Size((batch_size, middle_dim, output_dim))
예제 #12
0
 def test_no_input(self, get_circuit):
     """Test if a TypeError is raised when instantiated with a QNode that does not have an
     argument with name equal to the input_arg class attribute of TorchLayer"""
     c, w = get_circuit
     del c.func.sig[qml.qnn.torch.TorchLayer._input_arg]
     with pytest.raises(TypeError, match="QNode must include an argument with name"):
         TorchLayer(c, w)
예제 #13
0
 def test_no_torch(self, get_circuit, monkeypatch):
     """Test if an ImportError is raised when instantiated without PyTorch"""
     c, w = get_circuit
     with monkeypatch.context() as m:
         m.setattr(qml.qnn.torch, "TORCH_IMPORTED", False)
         with pytest.raises(ImportError, match="TorchLayer requires PyTorch"):
             TorchLayer(c, w)
예제 #14
0
    def test_deprecation_warning(self, get_circuit):
        """Test if deprecation warning is raised"""
        if int(qml.__version__.split(".")[1]) >= 16:
            pytest.fail(
                "Deprecation warnings for the qnn module should be removed")

        c, w = get_circuit
        with pytest.warns(DeprecationWarning, match=WARNING_STRING):
            TorchLayer(c, w)
예제 #15
0
    def test_qnode_weights_registered(self, get_circuit, n_qubits):
        """Test if the weights in qnode_weights are registered to the internal _parameters
        dictionary and that requires_grad == True"""
        c, w = get_circuit
        layer = TorchLayer(c, w)

        for name, weight in layer.qnode_weights.items():
            assert torch.allclose(weight, layer._parameters[name])
            assert weight.requires_grad
예제 #16
0
    def test_nonspecified_init(self, get_circuit, n_qubits, monkeypatch):
        """Test if weights are initialized according to the uniform distribution in [0, 2 pi]"""
        c, w = get_circuit

        uniform_ = mock.MagicMock(return_value=torch.Tensor(1))
        with monkeypatch.context() as m:
            m.setattr(torch.nn.init, "uniform_", uniform_)
            TorchLayer(c, w)
            kwargs = uniform_.call_args[1]
            assert kwargs["b"] == 2 * math.pi
예제 #17
0
    def test_non_input_defaults_tape_mode(self, n_qubits, output_dim):
        """Test that everything works when default arguments that are not the input argument are
        present in the QNode in tape mode"""
        if not qml.tape_mode_active():
            pytest.skip("This functionality is only supported in tape mode.")

        dev = qml.device("default.qubit", wires=n_qubits)
        w = {
            "w1": (3, n_qubits, 3),
            "w2": (1, ),
            "w3": 1,
            "w4": [3],
            "w5": (2, n_qubits, 3),
            "w6": 3,
            "w7": 0,
        }

        @qml.qnode(dev, interface="torch")
        def c(inputs, w1, w2, w4, w5, w6, w7, w3=0.5):
            """A circuit that embeds data using the AngleEmbedding and then performs a variety of
            operations. The output is a PauliZ measurement on the first output_dim qubits. One set of
            parameters, w5, are specified as non-trainable."""
            qml.templates.AngleEmbedding(inputs, wires=list(range(n_qubits)))
            qml.templates.StronglyEntanglingLayers(w1,
                                                   wires=list(range(n_qubits)))
            qml.RX(w2[0], wires=0 % n_qubits)
            qml.RX(w3, wires=1 % n_qubits)
            qml.Rot(*w4, wires=2 % n_qubits)
            qml.templates.StronglyEntanglingLayers(w5,
                                                   wires=list(range(n_qubits)))
            qml.Rot(*w6, wires=3 % n_qubits)
            qml.RX(w7, wires=4 % n_qubits)
            return [qml.expval(qml.PauliZ(i)) for i in range(output_dim)]

        layer = TorchLayer(c, w)
        x = torch.ones(n_qubits)

        layer_out = layer._evaluate_qnode(x)
        circuit_out = c(x, **layer.qnode_weights).type(x.dtype)

        assert torch.allclose(layer_out, circuit_out)
예제 #18
0
    def test_no_input(self):
        """Test if a TypeError is raised when instantiated with a QNode that does not have an
        argument with name equal to the input_arg class attribute of TorchLayer"""
        dev = qml.device("default.qubit", wires=1)
        weight_shapes = {"w1": (3, 3), "w2": 1}

        @qml.qnode(dev, interface="torch")
        def circuit(w1, w2):
            return qml.expval(qml.PauliZ(0))

        with pytest.raises(TypeError, match="QNode must include an argument with name"):
            TorchLayer(circuit, weight_shapes)
예제 #19
0
 def test_input_in_weight_shapes(self, get_circuit, n_qubits):
     """Test if a ValueError is raised when instantiated with a weight_shapes dictionary that
     contains the shape of the input argument given by the input_arg class attribute of
     TorchLayer"""
     c, w = get_circuit
     w[qml.qnn.torch.TorchLayer._input_arg] = n_qubits
     with pytest.raises(
             ValueError,
             match="{} argument should not have its dimension".format(
                 qml.qnn.torch.TorchLayer._input_arg),
     ):
         TorchLayer(c, w)
예제 #20
0
    def test_var_pos(self):
        """Test if a TypeError is raised when instantiated with a variable number of positional
        arguments"""
        dev = qml.device("default.qubit", wires=1)
        weight_shapes = {"w1": (3, 3), "w2": 1}

        @qml.qnode(dev, interface="torch")
        def circuit(inputs, w1, w2, *args):
            return qml.expval(qml.PauliZ(0))

        with pytest.raises(TypeError, match="Cannot have a variable number of positional"):
            TorchLayer(circuit, weight_shapes)
예제 #21
0
    def test_gradients(self, get_circuit, n_qubits):
        """Test if the gradients of the TorchLayer are equal to the gradients of the circuit when
        taken with respect to the trainable variables"""
        c, w = get_circuit
        layer = TorchLayer(c, w)
        x = torch.ones(n_qubits)

        weights = [layer.qnode_weights[weight] for weight in ordered_weights]

        out_layer = layer(x)
        out_layer.backward()

        g_layer = [w.grad.numpy() for w in weights]

        out_circuit = c(x, *weights)
        out_circuit.backward()

        g_circuit = [w.grad.numpy() for w in weights]

        for g1, g2 in zip(g_layer, g_circuit):
            assert np.allclose(g1, g2)
        assert len(weights) == len(list(layer.parameters()))
예제 #22
0
    def test_non_input_defaults(self, get_circuit, n_qubits):
        """Test if a TypeError is raised when default arguments that are not the input argument are
        present in the QNode"""
        c, w = get_circuit

        @qml.qnode(qml.device("default.qubit", wires=n_qubits), interface="torch")
        def c_dummy(inputs, w1, w2, w3, w4, w5, w6, w7, w8=None):
            """Dummy version of the circuit with a default argument"""
            return c(inputs, w1, w2, w3, w4, w5, w6, w7)

        with pytest.raises(
            TypeError,
            match="Only the argument {} is permitted".format(qml.qnn.torch.TorchLayer._input_arg),
        ):
            TorchLayer(c_dummy, {**w, **{"w8": 1}})
예제 #23
0
    def test_var_keyword(self, get_circuit, monkeypatch):
        """Test if a TypeError is raised when instantiated with a variable number of keyword
        arguments"""
        c, w = get_circuit

        class FuncPatch:
            """Patch for variable number of keyword arguments"""

            sig = c.func.sig
            var_pos = False
            var_keyword = True

        with monkeypatch.context() as m:
            m.setattr(c, "func", FuncPatch)

            with pytest.raises(TypeError, match="Cannot have a variable number of keyword"):
                TorchLayer(c, w)
예제 #24
0
    def test_qnode_weights_shapes(self, get_circuit, n_qubits):
        """Test if the weights in qnode_weights have the correct shape"""
        c, w = get_circuit
        layer = TorchLayer(c, w)

        ideal_shapes = {
            "w1": torch.Size((3, n_qubits, 3)),
            "w2": torch.Size((1,)),
            "w3": torch.Size([]),
            "w4": torch.Size((3,)),
            "w5": torch.Size((2, n_qubits, 3)),
            "w6": torch.Size((3,)),
            "w7": torch.Size([]),
        }

        for name, weight in layer.qnode_weights.items():
            assert weight.shape == ideal_shapes[name]
예제 #25
0
    def test_var_keyword(self, get_circuit, monkeypatch):
        """Test if a TypeError is raised when instantiated with a variable number of keyword
        arguments"""
        if qml.tape_mode_active():
            pytest.skip("This functionality is supported in tape mode.")

        c, w = get_circuit

        class FuncPatch:
            """Patch for variable number of keyword arguments"""

            sig = c.func.sig
            var_pos = False
            var_keyword = True

        with monkeypatch.context() as m:
            m.setattr(c, "func", FuncPatch)

            with pytest.raises(TypeError, match="Cannot have a variable number of keyword"):
                TorchLayer(c, w)
예제 #26
0
def test_interface_conversion(get_circuit, skip_if_no_tf_support):
    """Test if input QNodes with all types of interface are converted internally to the PyTorch
    interface"""
    c, w = get_circuit
    layer = TorchLayer(c, w)
    assert layer.qnode.interface == "torch"