Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix integration tests and adapt test for par_domainremoval #90

Merged
merged 17 commits into from
Dec 8, 2021
18 changes: 13 additions & 5 deletions pennylane_forest/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@

from ._version import __version__


pyquil_config = PyquilConfig()


Expand Down Expand Up @@ -82,7 +81,8 @@ def qubit_unitary(par, *wires):
list: list of PauliX matrix operators acting on each wire
"""
# Get the Quil definition for the new gate
gate_definition = DefGate("U_{}".format(str(uuid.uuid4())[:8]), par)
u_str = str(uuid.uuid4())[:8]
gate_definition = DefGate(f"U_{u_str}", par)
# Get the gate constructor
gate_constructor = gate_definition.get_constructor()
return [gate_definition, gate_constructor(*wires)]
Expand Down Expand Up @@ -217,11 +217,19 @@ def apply(self, operations, **kwargs):
device_wires = self.map_wires(operation.wires)
par = operation.parameters

if isinstance(par, list) and par:
if isinstance(par[0], np.ndarray) and par[0].shape == ():
# Array not supported
par = [float(i) for i in par]

if i > 0 and operation.name in ("QubitStateVector", "BasisState"):
name = operation.name
short_name = self.short_name
raise DeviceError(
"Operation {} cannot be used after other Operations have already "
"been applied on a {} device.".format(operation.name, self.short_name)
f"Operation {name} cannot be used after other Operations have already "
f"been applied on a {short_name} device."
)

self.prog += self._operation_map[operation.name](*par, *device_wires.labels)

self.prog += self.apply_rotations(rotations)
Expand Down Expand Up @@ -271,7 +279,7 @@ def mat_vec_product(self, mat, vec, device_wire_labels):

if mat.shape != (2 ** num_wires, 2 ** num_wires):
raise ValueError(
f"Please specify a {2**num_wires} x {2**num_wires} matrix for {num_wires} wires."
f"Please specify a {2 ** num_wires} x {2 ** num_wires} matrix for {num_wires} wires."
)

# first, we need to reshape both the matrix and vector
Expand Down
1 change: 0 additions & 1 deletion pennylane_forest/wavefunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ def __init__(self, wires, *, shots=None, **kwargs):

def apply(self, operations, **kwargs):
super().apply(operations, **kwargs)

self._state = self.qc.wavefunction(self.prog).amplitudes

# pyQuil uses the convention that the first qubit is the least significant
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pyquil>=2.16,<2.28.3
pennylane>=0.17
git+git://github.com/PennyLaneAI/pennylane
networkx
flaky
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
version = f.readlines()[-1].split()[-1].strip("\"'")


requirements = ["pyquil>=2.16,<2.28.3", "pennylane>=0.17"]
requirements = ["pyquil>=2.16,<2.28.3", "pennylane @ git+https://github.com/PennyLaneAI/pennylane.git@master"]
rmoyard marked this conversation as resolved.
Show resolved Hide resolved

info = {
"name": "PennyLane-Forest",
Expand Down
105 changes: 51 additions & 54 deletions tests/test_qvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,60 +288,54 @@ def test_var_hermitian(self, shots, qvm, compiler):
self.assertAlmostEqual(var, expected, delta=0.3)

@pytest.mark.parametrize(
"gate", plf.QVMDevice._operation_map
) # pylint: disable=protected-access
def test_apply(self, gate, apply_unitary, shots, qvm, compiler):
"""Test the application of gates"""
"op",
[
qml.QubitUnitary(np.array(U), wires=0),
qml.BasisState(np.array([1, 1, 1]), wires=list(range(3))),
qml.PauliX(wires=0),
qml.PauliY(wires=0),
qml.PauliZ(wires=0),
qml.S(wires=0),
qml.T(wires=0),
qml.RX(0.432, wires=0),
qml.RY(0.432, wires=0),
qml.RZ(0.432, wires=0),
qml.Hadamard(wires=0),
qml.Rot(0.432, 2, 0.324, wires=0),
qml.Toffoli(wires=[0, 1, 2]),
qml.SWAP(wires=[0, 1]),
qml.CSWAP(wires=[0, 1, 2]),
qml.CZ(wires=[0, 1]),
qml.CNOT(wires=[0, 1]),
qml.PhaseShift(0.432, wires=0),
qml.CSWAP(wires=[0, 1, 2]),
plf.CPHASE(0.432, 2, wires=[0, 1]),
plf.ISWAP(wires=[0, 1]),
plf.PSWAP(0.432, wires=[0, 1]),
],
)
def test_apply(self, op, apply_unitary, shots, qvm, compiler):
"""Test the application of gates to a state"""
dev = plf.QVMDevice(device="3q-qvm", shots=shots, parametric_compilation=False)

try:
# get the equivalent pennylane operation class
op = getattr(qml.ops, gate)
except AttributeError:
# get the equivalent pennylane-forest operation class
op = getattr(plf, gate)

# the list of wires to apply the operation to
w = list(range(op.num_wires))

if op.par_domain == "A":
# the parameter is an array
if gate == "QubitUnitary":
p = np.array(U)
w = [0]
state = apply_unitary(U, 3)
elif gate == "BasisState":
p = np.array([1, 1, 1])
state = np.array([0, 0, 0, 0, 0, 0, 0, 1])
w = list(range(dev.num_wires))

with qml.tape.QuantumTape() as tape:
op(p, wires=w)
obs = qml.expval(qml.PauliZ(0))
obs = qml.expval(qml.PauliZ(0))

if op.name == "QubitUnitary":
state = apply_unitary(U, 3)
elif op.name == "BasisState":
state = np.array([0, 0, 0, 0, 0, 0, 0, 1])
elif op.name == "CPHASE":
state = apply_unitary(test_operation_map["CPHASE"](0.432, 2), 3)
elif op.name == "ISWAP":
state = apply_unitary(test_operation_map["ISWAP"], 3)
elif op.name == "PSWAP":
state = apply_unitary(test_operation_map["PSWAP"](0.432), 3)
else:
p = [0.432_423, 2, 0.324][: op.num_params]
fn = test_operation_map[gate]
if callable(fn):
# if the default.qubit is an operation accepting parameters,
# initialise it using the parameters generated above.
O = fn(*p)
else:
# otherwise, the operation is simply an array.
O = fn

# calculate the expected output
state = apply_unitary(O, 3)
# Creating the tape using a parametrized operation
if p:
with qml.tape.QuantumTape() as tape:
op(*p, wires=w)
obs = qml.expval(qml.PauliZ(0))

# Creating the tape using an operation that take no parameters
else:
with qml.tape.QuantumTape() as tape:
op(wires=w)
obs = qml.expval(qml.PauliZ(0))
state = apply_unitary(op.matrix, 3)

with qml.tape.QuantumTape() as tape:
qml.apply(op)
obs

dev.apply(tape.operations, rotations=tape.diagonalizing_gates)

Expand Down Expand Up @@ -902,7 +896,8 @@ def test_compiled_program_was_used(self, qvm, device, monkeypatch):
obs_list = obs * number_of_qnodes

qnodes = qml.map(qml.templates.StronglyEntanglingLayers, obs_list, dev)
params = qml.init.strong_ent_layers_normal(n_layers=4, n_wires=dev.num_wires)
shape = qml.StronglyEntanglingLayers.shape(n_layers=4, n_wires=dev.num_wires)
params = np.random.random(size=shape)

# For the first evaluation, use the real compile method
qnodes[0](params)
Expand Down Expand Up @@ -934,7 +929,9 @@ def test_compiled_program_was_correct_compared_with_default_qubit(self, qvm, dev
obs_list = obs * number_of_qnodes

dev = qml.device("forest.qvm", device=device, timeout=100)
params = qml.init.strong_ent_layers_normal(n_layers=4, n_wires=dev.num_wires)

shape = qml.StronglyEntanglingLayers.shape(n_layers=4, n_wires=dev.num_wires)
params = np.random.random(size=shape)

qnodes = qml.map(qml.templates.StronglyEntanglingLayers, obs_list, dev)

Expand All @@ -945,7 +942,7 @@ def test_compiled_program_was_correct_compared_with_default_qubit(self, qvm, dev

results2 = qnodes2(params)

assert np.allclose(results, results2, atol=2e-02, rtol=0)
assert np.allclose(results, results2, atol=6e-02, rtol=0)
assert dev.circuit_hash in dev._compiled_program_dict
assert len(dev._compiled_program_dict.items()) == 1

Expand Down
95 changes: 43 additions & 52 deletions tests/test_wavefunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,65 +70,56 @@ def test_var_hermitian(self, tol, qvm):
self.assertAlmostEqual(var, expected, delta=tol)

@pytest.mark.parametrize(
"gate", plf.WavefunctionDevice._operation_map
) # pylint: disable=protected-access
def test_apply(self, gate, apply_unitary, tol, qvm, compiler):
"op",
[
qml.QubitUnitary(np.array(U), wires=0),
qml.BasisState(np.array([1, 1, 1]), wires=list(range(3))),
qml.PauliX(wires=0),
qml.PauliY(wires=0),
qml.PauliZ(wires=0),
qml.S(wires=0),
qml.T(wires=0),
qml.RX(0.432, wires=0),
qml.RY(0.432, wires=0),
qml.RZ(0.432, wires=0),
qml.Hadamard(wires=0),
qml.Rot(0.432, 2, 0.324, wires=0),
qml.Toffoli(wires=[0, 1, 2]),
qml.SWAP(wires=[0, 1]),
qml.CSWAP(wires=[0, 1, 2]),
qml.CZ(wires=[0, 1]),
qml.CNOT(wires=[0, 1]),
qml.PhaseShift(0.432, wires=0),
qml.CSWAP(wires=[0, 1, 2]),
plf.CPHASE(0.432, 2, wires=[0, 1]),
plf.ISWAP(wires=[0, 1]),
plf.PSWAP(0.432, wires=[0, 1]),
],
)
def test_apply(self, op, apply_unitary, tol):
"""Test the application of gates to a state"""
dev = plf.WavefunctionDevice(wires=3)

try:
# get the equivalent pennylane operation class
op = getattr(qml.ops, gate)
except AttributeError:
# get the equivalent pennylane-forest operation class
op = getattr(plf, gate)

# the list of wires to apply the operation to
w = list(range(op.num_wires))

obs = qml.expval(qml.PauliZ(0))
if op.par_domain == "A":
# the parameter is an array
if gate == "QubitUnitary":
p = np.array(U)
w = [0]
state = apply_unitary(U, 3)
elif gate == "BasisState":
p = np.array([1, 1, 1])
state = np.array([0, 0, 0, 0, 0, 0, 0, 1])
w = list(range(dev.num_wires))

with qml.tape.QuantumTape() as tape:
op(p, wires=w)
obs

if op.name == "QubitUnitary":
state = apply_unitary(U, 3)
elif op.name == "BasisState":
state = np.array([0, 0, 0, 0, 0, 0, 0, 1])
elif op.name == "CPHASE":
state = apply_unitary(test_operation_map["CPHASE"](0.432, 2), 3)
elif op.name == "ISWAP":
state = apply_unitary(test_operation_map["ISWAP"], 3)
elif op.name == "PSWAP":
state = apply_unitary(test_operation_map["PSWAP"](0.432), 3)
else:
p = [0.432_423, 2, 0.324][: op.num_params]
fn = test_operation_map[gate]
if callable(fn):
# if the default.qubit is an operation accepting parameters,
# initialise it using the parameters generated above.
O = fn(*p)
else:
# otherwise, the operation is simply an array.
O = fn

# calculate the expected output
state = apply_unitary(O, 3)
# Creating the tape using a parametrized operation
if p:
with qml.tape.QuantumTape() as tape:
op(*p, wires=w)
obs

# Creating the tape using an operation that take no parameters
else:
with qml.tape.QuantumTape() as tape:
op(wires=w)
obs
state = apply_unitary(op.matrix, 3)

dev.apply(tape.operations, rotations=tape.diagonalizing_gates)
with qml.tape.QuantumTape() as tape:
qml.apply(op)
obs

res = dev.expval(obs)
dev.apply(tape.operations, rotations=tape.diagonalizing_gates)

# verify the device is now in the expected state
self.assertAllAlmostEqual(dev._state, state, delta=tol)
Expand Down