Skip to content

Commit

Permalink
Merge pull request #82 from calculquebec/main
Browse files Browse the repository at this point in the history
Release 0.5.3
  • Loading branch information
Scirelgar authored Jan 8, 2025
2 parents f003e51 + f03284c commit 57343cf
Show file tree
Hide file tree
Showing 17 changed files with 386 additions and 92 deletions.
1 change: 1 addition & 0 deletions .github/workflows/python-publish-testpypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ jobs:
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
verbose: true
repository-url: https://test.pypi.org/legacy/
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The plugin will also take care of installing Julia and the required Julia packag

## Usage

If you need more information about how to use the plugin, you may read the [getting-started](./doc/getting_started.ipynb) jupyter notebook.
If you need more information about how to use the plugin, you may read the [getting-started](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/getting_started.ipynb) jupyter notebook.

### Running files

Expand Down Expand Up @@ -102,4 +102,4 @@ The plugin is currently in its beta phase and provides access to MonarQ directly

## References

Calcul Québec's Wiki provides a lot of information on the plugin, its components and how to use them. You can access it [here](https://docs.alliancecan.ca/wiki/Les_services_quantiques).
Calcul Québec's Wiki provides a lot of information on the plugin, its components and how to use them. You can access it [here](https://docs.alliancecan.ca/wiki/Services_d%27informatique_quantique).
4 changes: 2 additions & 2 deletions doc/for_developers/using_transpiler.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"\n",
"This allows you to change transpiling steps, or even add new steps to the processing pipelines.\n",
"\n",
"See [configurations](using_configurations.ipynb) for more information."
"See [configurations](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_configurations.ipynb) for more information."
]
},
{
Expand All @@ -90,7 +90,7 @@
"\n",
"You can also create new, custom steps to add to the processing pipeline. \n",
"\n",
"See [custom steps](using_custom_steps.ipynb) for mor information."
"See [custom steps](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_custom_steps.ipynb) for mor information."
]
}
],
Expand Down
17 changes: 9 additions & 8 deletions doc/getting_started.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"1. create a [client](using_client.ipynb) for your device"
"1. create a [client](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_client.ipynb) for your device"
]
},
{
Expand Down Expand Up @@ -103,7 +103,7 @@
"\n",
"There are 2 optional arguments : \n",
"1. a number of wire or the exact wires (as an array)\n",
"2. a [configuration](./for_developers/using_configurations.ipynb)"
"2. a [configuration](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_configurations.ipynb)"
]
},
{
Expand All @@ -128,7 +128,7 @@
"source": [
"It should be noted that because you specify wires on gates and on your device does not mean that those wires will be used on MonarQ. \n",
"\n",
"For more information, see [using transpiler](using_transpiler.ipynb)"
"For more information, see [using transpiler](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_transpiler.ipynb)"
]
},
{
Expand Down Expand Up @@ -214,11 +214,12 @@
"source": [
"## more information \n",
"\n",
"- [clients](using_client.ipynb)\n",
"- [processor configs](using_configurations.ipynb)\n",
"- [the transpiler](using_transpiler.ipynb)\n",
"- [custom processing steps](using_custom_steps.ipynb)\n",
"- [custom gates](using_custom_gates.ipynb)"
"- [clients](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_client.ipynb)\n",
"- [processor configs](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_configurations.ipynb)\n",
"- [the transpiler](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_transpiler.ipynb)\n",
"- [custom processing steps](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_custom_steps.ipynb)\n",
"- [custom gates](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_custom_gates.ipynb)\n",
"- [api adapter](https://github.com/calculquebec/pennylane-calculquebec/blob/main/doc/for_developers/using_api_adapter.ipynb)"
]
}
],
Expand Down
1 change: 0 additions & 1 deletion pennylane_calculquebec/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""this is the top level module for the Pennylane Snowflurry plugin. It is used for communicating with MonarQ.
"""

from .pennylane_converter import PennylaneConverter
from .snowflurry_device import SnowflurryQubitDevice
from .monarq_device import MonarqDevice
3 changes: 2 additions & 1 deletion pennylane_calculquebec/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
"""


__version__ = "0.5.1"
__version__ = "0.5.3"

2 changes: 1 addition & 1 deletion pennylane_calculquebec/measurements/expectation_value.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from .measurement_strategy import MeasurementStrategy
import pennylane as qml
from juliacall import convert
import numpy as np


Expand All @@ -10,6 +9,7 @@ def __init__(self):
super().__init__()

def measure(self, converter, mp, shots):
from juliacall import convert
# FIXME : this measurement does work when the number of qubits measured is not equal to the number of qubits
# in the circuit
# Requires some processing to work with larger matrices
Expand Down
3 changes: 2 additions & 1 deletion pennylane_calculquebec/measurements/probabilities.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .measurement_strategy import MeasurementStrategy
from juliacall import convert



class Probabilities(MeasurementStrategy):
Expand All @@ -8,6 +8,7 @@ def __init__(self):
super().__init__()

def measure(self, converter, mp, shots):
from juliacall import convert
converter.remove_readouts()
wires_list = mp.wires.tolist()
if len(wires_list) == 0:
Expand Down
8 changes: 7 additions & 1 deletion pennylane_calculquebec/processing/config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
"""This module contains configuration classes and presets.
"""

from .processing_config import ProcessingConfig, MonarqDefaultConfig, NoPlaceNoRouteConfig, MonarqDefaultConfigNoBenchmark, EmptyConfig, FakeMonarqConfig
from .processing_config \
import ProcessingConfig, \
MonarqDefaultConfig, \
NoPlaceNoRouteConfig, \
MonarqDefaultConfigNoBenchmark, \
EmptyConfig, \
FakeMonarqConfig
19 changes: 10 additions & 9 deletions pennylane_calculquebec/processing/steps/placement.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,16 @@ def execute(self, tape):
# 2. find all unmapped nodes
missing = [node for node in circuit_topology.nodes if node not in mapping.keys()]

for node in missing:
# 3. find the best neighbour (using cost function)
most_connected_node = graph_util.find_best_neighbour(node, circuit_topology, self.use_benchmark)
for source in missing:
if source in mapping:
continue
mapping[source] = graph_util.find_best_wire(machine_topology, [machine_node for machine_node in mapping.values()], self.use_benchmark)

# 4. find machine node with shortest path from already mapped machine node
possibles = [possible for possible in machine_topology.nodes if possible not in mapping.values()]
shortest_path_mapping = graph_util.node_with_shortest_path_from_selection(mapping[most_connected_node], possibles, machine_topology, self.use_benchmark)
for destination in missing:
if (source, destination) not in circuit_topology.edges:
continue

mapping[node] = shortest_path_mapping
ASTAR(False)._recurse(source, destination, mapping, missing, machine_topology, circuit_topology)

# 5. map wires in all operations and measurements
new_tape = type(tape)([operation.map_wires(mapping) for operation in tape.operations], [measurement.map_wires(mapping) for measurement in tape.measurements], shots=tape.shots)
Expand All @@ -90,7 +91,7 @@ def execute(self, tape):
machine_topology = graph_util.machine_graph(self.use_benchmark, self.q1_acceptance, self.q2_acceptance, self.excluded_qubits, self.excluded_couplers)

if len(graph_util.find_biggest_group(circuit_topology)) > len(graph_util.find_biggest_group(machine_topology)):
raise Exception(f"There are {machine_topology.number_of_nodes} qubits on the machine but your circuit has {circuit_topology.number_of_nodes}.")
raise Exception(f"There are {machine_topology.number_of_nodes()} qubits on the machine but your circuit has {circuit_topology.number_of_nodes()}.")

# 1. find the largest common subgraph using VF2 algorithm and combinatorics
mapping = graph_util.find_largest_common_subgraph_vf2(circuit_topology, machine_topology)
Expand Down Expand Up @@ -157,7 +158,7 @@ def execute(self, tape : QuantumTape) -> QuantumTape:
for source in to_explore:
if source in mapping:
continue
mapping[source] = graph_util.find_best_wire(machine_topology, [machine_node for machine_node in mapping.value()], self.use_benchmark)
mapping[source] = graph_util.find_best_wire(machine_topology, [machine_node for machine_node in mapping.values()], self.use_benchmark)

for destination in to_explore:
if (source, destination) not in circuit_topology.edges:
Expand Down
15 changes: 14 additions & 1 deletion pennylane_calculquebec/utility/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,20 @@ def remove_global_phase(matrix):

return normalized_matrix

# TOFIX : this method doesnt work for some matrices
def are_tape_same_probs(tape1, tape2):
"""
returns true if both tapes yield the same probabilities
"""
dev = qml.device("default.qubit")
results1 = qml.execute([tape1], dev)
results2 = qml.execute([tape2], dev)

results1 = np.round(results1, 5)
results2 = np.round(results2, 5)

return np.array_equal(results1, results2)


def is_equal_matrices(matrix1, matrix2, tolerance=1e-9):
"""
Checks if two matrices are equal up to a complex multiplicative factor.
Expand Down
3 changes: 1 addition & 2 deletions pennylane_calculquebec/utility/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def _find_isomorphisms(circuit : nx.Graph, machine : nx.Graph) -> dict[int, int]
returns (dict[int, int]) : a mapping between the circuit's wires and the machines qubits
"""
vf2 = nx.isomorphism.GraphMatcher(machine, circuit)
for mono in vf2.subgraph_isomorphisms_iter():
for mono in vf2.subgraph_monomorphisms_iter():
return {v : k for k, v in mono.items()}
return None

Expand Down Expand Up @@ -208,7 +208,6 @@ def find_closest_wire(source : int, machine_graph : nx.Graph, excluding : list[i
return min(nodes, key=lambda dest: shortest_path(source,
dest,
machine_graph,
excluding=excluding,
use_benchmark=use_benchmark))

def node_with_shortest_path_from_selection(source : int, selection : list[int], graph : nx.Graph, use_benchmark = True):
Expand Down
6 changes: 4 additions & 2 deletions pennylane_calculquebec/utility/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ def fit(x_axis : list, array : list, label : str):
"""
def fit_func(x, a, b): return a*(x**b)

params, _ = curve_fit(fit_func, x_axis, array)
params, _ = curve_fit(fit_func, [int(x) for x in x_axis], array)
a, b = params

plt.ylabel("seconds")
plt.xlabel("num qubits")
plt.scatter(x_axis, array, label=label)
plt.plot(x_axis, fit_func(x_axis, a, b), label=f"fitted {label}: $y={a:.3f} x^{{{b:.2f}}}$")
plt.plot(x_axis, fit_func(x_axis, a, b), label=f"fitted {label}: $y={a:.3f} x^{{{b:.2f}}}$", )
20 changes: 5 additions & 15 deletions tests/test_base_decomp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pennylane_calculquebec.processing.steps.base_decomposition import CliffordTDecomposition, BaseDecomposition
from pennylane_calculquebec.processing.steps.native_decomposition import MonarqDecomposition
from pennylane_calculquebec.utility.api import instructions
from pennylane_calculquebec.utility.debug import is_equal_matrices
from pennylane_calculquebec.utility.debug import is_equal_matrices, are_tape_same_probs
import pennylane as qml
from pennylane.tape import QuantumTape
import pytest
Expand All @@ -19,26 +19,19 @@ def test_base_decomp_toffoli():
new_tape = step.execute(tape)
assert all(op.name in step.base_gates for op in new_tape.operations)

mat1 = reduce(lambda i, s: i @ s.matrix(wire_order=tape.wires), tape.operations, np.identity(1 << len(tape.wires)))
mat2 = reduce(lambda i, s: i @ s.matrix(wire_order=new_tape.wires), new_tape.operations, np.identity(1 << len(new_tape.wires)))

assert is_equal_matrices(mat1, mat2)
assert are_tape_same_probs(tape, new_tape)


@pytest.mark.xfail
def test_base_decomp_unitary():
step = CliffordTDecomposition()

ops = [qml.Hadamard(0), qml.QubitUnitary(np.array([[-1, 1], [1, 1]])/np.sqrt(2), 0)]
tape = QuantumTape(ops=ops, measurements=[qml.probs()])
new_tape = step.execute(tape)
assert all(op.name in step.base_gates for op in new_tape.operations)
assert are_tape_same_probs(tape, new_tape)

mat1 = reduce(lambda i, s: i @ s.matrix(wire_order=tape.wires), tape.operations, np.identity(1 << len(tape.wires)))
mat2 = reduce(lambda i, s: i @ s.matrix(wire_order=new_tape.wires), new_tape.operations, np.identity(1 << len(new_tape.wires)))

assert is_equal_matrices(mat1, mat2)

@pytest.mark.xfail
def test_base_decomp_cu():
step = CliffordTDecomposition()

Expand All @@ -47,8 +40,5 @@ def test_base_decomp_cu():
new_tape = step.execute(tape)

assert all(op.name in step.base_gates for op in new_tape.operations)
assert are_tape_same_probs(tape, new_tape)

mat1 = reduce(lambda i, s: i @ s.matrix(wire_order=tape.wires), tape.operations, np.identity(1 << len(tape.wires)))
mat2 = reduce(lambda i, s: i @ s.matrix(wire_order=new_tape.wires), new_tape.operations, np.identity(1 << len(new_tape.wires)))

assert is_equal_matrices(mat1, mat2)
52 changes: 7 additions & 45 deletions tests/test_native_decomp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,11 @@
from pennylane_calculquebec.utility.api import instructions
import pennylane as qml
from pennylane.tape import QuantumTape
from pennylane_calculquebec.utility.debug import are_tape_same_probs
import pytest

from functools import reduce


def are_matrices_equivalent(matrix1, matrix2, tolerance=1e-9):
"""
Checks if two matrices are equal up to a complex multiplicative factor.
Args:
matrix1 (ndarray): First matrix.
matrix2 (ndarray): Second matrix.
tolerance (float): Numerical tolerance for comparison.
Returns:
bool: True if the matrices are equal up to a complex factor, False otherwise.
"""

tolerance = tolerance + 1j*tolerance

if matrix1.shape != matrix2.shape:
return False

matrix2_dag = np.transpose(np.conjugate(matrix2))
id = np.round(matrix1 @ matrix2_dag, 4)
value = id[0][0]

for i in range(id.shape[0]):
if abs(id[i][i] - value) > tolerance:
return False
return True



def test_native_decomp_toffoli():
preproc = CliffordTDecomposition()
step = MonarqDecomposition()
Expand All @@ -48,11 +19,8 @@ def test_native_decomp_toffoli():
new_tape = step.execute(new_tape)

assert all(op.name in instructions for op in new_tape.operations)

mat1 = reduce(lambda i, s: i @ s.matrix(wire_order=tape.wires), tape.operations, np.identity(1 << len(tape.wires)))
mat2 = reduce(lambda i, s: i @ s.matrix(wire_order=new_tape.wires), new_tape.operations, np.identity(1 << len(new_tape.wires)))

assert are_matrices_equivalent(mat1, mat2)

assert are_tape_same_probs(tape, new_tape)

def test_native_decomp_unitary():
preproc = CliffordTDecomposition()
Expand All @@ -64,11 +32,8 @@ def test_native_decomp_unitary():
new_tape = step.execute(new_tape)

assert all(op.name in instructions for op in new_tape.operations)

mat1 = reduce(lambda i, s: i @ s.matrix(wire_order=tape.wires), tape.operations, np.identity(1 << len(tape.wires)))
mat2 = reduce(lambda i, s: i @ s.matrix(wire_order=new_tape.wires), new_tape.operations, np.identity(1 << len(new_tape.wires)))

assert are_matrices_equivalent(mat1, mat2)

assert are_tape_same_probs(tape, new_tape)

def test_native_decomp_cu():
preproc = CliffordTDecomposition()
Expand All @@ -80,11 +45,8 @@ def test_native_decomp_cu():
new_tape = step.execute(new_tape)

assert all(op.name in instructions for op in new_tape.operations)

mat1 = reduce(lambda i, s: i @ s.matrix(wire_order=tape.wires), tape.operations, np.identity(1 << len(tape.wires)))
mat2 = reduce(lambda i, s: i @ s.matrix(wire_order=new_tape.wires), new_tape.operations, np.identity(1 << len(new_tape.wires)))

assert are_matrices_equivalent(mat1, mat2)

assert are_tape_same_probs(tape, new_tape)

def test_gate_not_in_decomp_map():
ops = [qml.Toffoli([0, 1, 2])]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pennylaneConverter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from pennylane_calculquebec import measurements
from pennylane_calculquebec import PennylaneConverter
from pennylane_calculquebec.pennylane_converter import PennylaneConverter
import pennylane as qml
import numpy as np
from pennylane.tape import QuantumTape
Expand Down
Loading

0 comments on commit 57343cf

Please sign in to comment.