Skip to content

Commit

Permalink
Fix integration tests and adapt test for par_domainremoval (#90)
Browse files Browse the repository at this point in the history
* Fix first try

* Remove a wild print

* Fix.

* More fix.

* More fix.

* More fix.

* Typo

* Check list.

* Typo.

* Shape.

* Change test.

* Remove init.

* Shape.

* print

* Tol.

* Update setup.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Changelog

Co-authored-by: antalszava <antalszava@gmail.com>
  • Loading branch information
rmoyard and antalszava committed Dec 8, 2021
1 parent 0d18b0f commit 5a70aef
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 115 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Release 0.18.0-dev
# Release 0.20.0-dev

### New features since last release

Expand All @@ -10,10 +10,16 @@

### Bug fixes

* Fix a bug where array parameters where not accepted when building circuits on
pyQuil side.
[(#90)](https://github.com/PennyLaneAI/pennylane-forest/pull/90)

### Contributors

This release contains contributions from (in alphabetical order):

Romain Moyard.

---

# Release 0.17.0
Expand Down
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"]

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

0 comments on commit 5a70aef

Please sign in to comment.