diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 846381b..41a6311 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: fail-fast: false matrix: os: ['ubuntu-latest', 'windows-2022', 'macos-latest'] - python: ['3.8', '3.9', '3.10', '3.11'] + python: ['3.9', '3.10', '3.11', '3.12'] name: "Python ${{ matrix.python }} / ${{ matrix.os }}" runs-on: ${{ matrix.os }} diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index 2b79c46..e037a75 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -49,7 +49,7 @@ Then we define a circuit: # generate a slightly modified GHZ state generation pattern circuit = Circuit(3) simple_circ(circuit) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern # plot the pattern nodes, edges = pattern.get_graph() diff --git a/graphix_perceval/converter.py b/graphix_perceval/converter.py index 214dc16..155c440 100644 --- a/graphix_perceval/converter.py +++ b/graphix_perceval/converter.py @@ -3,7 +3,13 @@ import graphix import perceval as pcvl import sympy as sp -from graphix.extraction import ResourceGraph, ResourceType, get_fusion_network_from_graph +from graphix.clifford import Clifford +from graphix.command import M +from graphix.extraction import ( + ResourceGraph, + ResourceType, + get_fusion_network_from_graph, +) from perceval import components as comp from graphix_perceval.clifford import CLIFFORD_TO_PERCEVAL_POLAR @@ -29,12 +35,15 @@ def pattern2graphstate( output_nodes : list List of output nodes. """ - nodes, edges = pattern.get_graph() + graph = pattern.extract_graph() + nodes = list(graph.nodes()) + edges = list(graph.edges()) vop_init = pattern.get_vops() graph_state = graphix.GraphState(nodes=nodes, edges=edges, vops=vop_init) phasedict = {} - for command in pattern.get_measurement_commands(): - phasedict[command[1]] = command[3] + for command in pattern: + if isinstance(command, M): + phasedict[command.node] = command.angle output_nodes = pattern.output_nodes return graph_state, phasedict, output_nodes @@ -104,10 +113,10 @@ def add_resourcegraph(self, ResourceGraph: ResourceGraph, phasedict: dict[int, f self.num_photons += 1 self.photons.append(ph) - if ResourceGraph.type in (ResourceType.GHZ, ResourceType.LINEAR): + if ResourceGraph.cltype in (ResourceType.GHZ, ResourceType.LINEAR): self.ResourceGraphs.append(ResourceGraph) else: - raise TypeError(f"ResourceType {ResourceGraph.type} is not supported") + raise TypeError(f"ResourceType {ResourceGraph.cltype} is not supported") def get_readouts(self) -> list[Photon]: return [ph for ph in self.photons if ph.type == PhotonType.READOUT] @@ -144,13 +153,13 @@ def setup_perceval_circuit(self, name: str | None = None, merge: bool = False) - # Create circuits for all the ResourceGraphs photon_idx = 0 for cl in self.ResourceGraphs: - if cl.type == ResourceType.GHZ: + if cl.cltype == ResourceType.GHZ: circ.add( [idx for idx in range(photon_idx, photon_idx + len(cl.graph.nodes))], ghz_circuit(len(cl.graph.nodes)), merge, ) - elif cl.type == ResourceType.LINEAR: + elif cl.cltype == ResourceType.LINEAR: circ.add( [idx for idx in range(photon_idx, photon_idx + len(cl.graph.nodes))], linear_circuit(len(cl.graph.nodes)), @@ -207,21 +216,24 @@ def apply_local_clifford(self, vops: dict[int, int]) -> None: self._clifford_applied = True -def local_clifford_circuit(clifford_id: int) -> pcvl.Circuit: +def local_clifford_circuit(clifford_id: int | Clifford) -> pcvl.Circuit: """Create a Perceval Circuit for a local clifford. Parameters ---------- mode_id : int Mode id. - clifford_id : int - Clifford id. + clifford_id : int or Clifford + Clifford id (0-23) or Clifford enum. Returns ------- perceval.Circuit Perceval Circuit for a local clifford. """ + # Convert Clifford enum to int if needed + if isinstance(clifford_id, Clifford): + clifford_id = clifford_id.value if not 0 <= clifford_id <= 23: raise ValueError("clifford_id must be in [0, 23]") circ = pcvl.Circuit(m=1, name="LOCAL CLIFFORD ID:" + str(clifford_id)) diff --git a/graphix_perceval/experiment.py b/graphix_perceval/experiment.py index 4d070e6..831a62f 100644 --- a/graphix_perceval/experiment.py +++ b/graphix_perceval/experiment.py @@ -4,11 +4,11 @@ import itertools import sys import warnings +from _collections_abc import dict_items from enum import Enum import perceval as pcvl import sympy as sp -from _collections_abc import dict_items from perceval.algorithm import Sampler from perceval.utils import PostSelect from tabulate import tabulate @@ -76,15 +76,13 @@ def __init__(self, circuit: pcvl.Circuit, photons: list[Photon]): self.input_state = None self.output_states: dict[str, str] | None = None - def set_local_processor(self, backend: str, source: pcvl.Source = pcvl.Source(), name: str = None): + def set_local_processor(self, backend: str, name: str = None): r"""Set the local computing backend. Parameters ---------- backend : str Name of a local backend. - source : :class:`perceval.Source` object, optional - Setting of single-photon source. name : str, optional Name for the processor. """ @@ -93,7 +91,7 @@ def set_local_processor(self, backend: str, source: pcvl.Source = pcvl.Source(), self.to_perceval() if self.processor is not None: warnings.warn("The processor has already been set. The previous processor will be overwritten.") - self.processor = pcvl.Processor(backend=backend, m_circuit=self.circ, source=source, name=name) + self.processor = pcvl.Processor(backend=backend, m_circuit=self.circ, name=name) self.backend = backend self.set_input_state() @@ -136,7 +134,7 @@ def set_input_state(self): input_state = input_state + ">" self.input_state = pcvl.BasicState(input_state) - self.processor.with_polarized_input(self.input_state) # not with_input (it will not work for polarized input) + self.processor.with_input(self.input_state) def set_output_states(self): r"""Set the output states. @@ -198,12 +196,17 @@ def get_probability_distribution( self.set_postselection() sampler = Sampler(self.processor) - probs = PhotonDistribution(sampler.probs()["results"]) + result = sampler.probs() - if format_result: - probs.replace_keys(self.output_states) + # Convert BSDistribution to PhotonDistribution + dist = {} + for state, prob in result['results'].items(): + key = str(state) + if format_result and self.output_states and key in self.output_states: + key = self.output_states[key] + dist[key] = float(prob) - return probs + return PhotonDistribution(dist) def sample(self, num_samples=1024, format_result: bool = True, postselection: bool = True) -> PhotonCount: """Run the MBQC pattern on IBMQ devices @@ -241,11 +244,18 @@ def set_postselection(self): """Postselect the results according to the pattern.""" ps = PostSelect() for ph in self.get_readout_photons(): - ps.eq([2 * ph.id, 2 * ph.id + 1], 1) + ps_ = PostSelect(f"[{2*ph.id}, {2*ph.id + 1}] == 1") + ps.merge(ps_) for ph in self.get_compute_photons(): - ps.eq([2 * ph.id], 0).eq([2 * ph.id + 1], 1) + ps_ = PostSelect(f"[{2*ph.id}] == 0") + ps.merge(ps_) + ps_ = PostSelect(f"[{2*ph.id + 1}] == 1") + ps.merge(ps_) for ph in self.get_witness_photons(): - ps.eq([2 * ph.id], 0).eq([2 * ph.id + 1], 1) + ps_ = PostSelect(f"[{2*ph.id}] == 0") + ps.merge(ps_) + ps_ = PostSelect(f"[{2*ph.id + 1}] == 1") + ps.merge(ps_) self.processor.set_postselection(ps) diff --git a/graphix_perceval/version.py b/graphix_perceval/version.py index 3b93d0b..27fdca4 100644 --- a/graphix_perceval/version.py +++ b/graphix_perceval/version.py @@ -1 +1 @@ -__version__ = "0.0.2" +__version__ = "0.0.3" diff --git a/requirements.txt b/requirements.txt index 11fc390..e325a2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -graphix>=0.2.8 -perceval-quandela>=0.9.1 +graphix>=0.3.0 +perceval-quandela>=0.12.0 diff --git a/setup.py b/setup.py index 53746fe..1534385 100644 --- a/setup.py +++ b/setup.py @@ -27,15 +27,16 @@ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Science/Research", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Topic :: Scientific/Engineering :: Physics", ], - "python_requires": ">=3.8,<3.12", + "python_requires": ">=3.9,<3.14", "install_requires": requirements, "extras_require": {}, } diff --git a/tests/test_clifford.py b/tests/test_clifford.py index 47b3657..eae5035 100644 --- a/tests/test_clifford.py +++ b/tests/test_clifford.py @@ -1,10 +1,13 @@ import unittest -from graphix.clifford import CLIFFORD -from graphix_perceval.clifford import CLIFFORD_TO_PERCEVAL_BS, CLIFFORD_TO_PERCEVAL_POLAR -from sympy import matrix2numpy import numpy as np import perceval as pcvl +from graphix.clifford import CLIFFORD +from graphix_perceval.clifford import ( + CLIFFORD_TO_PERCEVAL_BS, + CLIFFORD_TO_PERCEVAL_POLAR, +) +from sympy import matrix2numpy class TestConverter(unittest.TestCase): diff --git a/tests/test_converter.py b/tests/test_converter.py index dbd16b6..1a9db86 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -2,7 +2,6 @@ import numpy as np from graphix import Circuit - from graphix_perceval.converter import to_perceval from graphix_perceval.experiment import PhotonDistribution @@ -12,7 +11,7 @@ def test_sampling_circuit_wo_postselection(self): circuit = Circuit(2) circuit.h(1) circuit.cnot(0, 1) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() @@ -28,7 +27,7 @@ def test_sampling_circuit_w_postselection(self): circuit = Circuit(2) circuit.h(1) circuit.cnot(0, 1) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() @@ -44,7 +43,7 @@ def test_sampling_circuit_w_postselection(self): def test_zero_state_creation_wo_pauli_meas(self): circuit = Circuit(1) # initialize with |+> circuit.h(0) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() @@ -60,7 +59,7 @@ def test_one_state_creation_wo_pauli_meas(self): circuit = Circuit(1) # initialize with |+> circuit.h(0) circuit.x(0) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() @@ -76,7 +75,7 @@ def test_rotated_one_qubit_state_creation_wo_pauli_meas(self): circuit = Circuit(1) # initialize with |+> circuit.h(0) circuit.rx(0, np.pi / 1.23) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() @@ -93,7 +92,7 @@ def test_bell_state_phi_plus_creation_wo_pauli_meas(self): circuit = Circuit(2) # initialize with |+> \otimes |+> circuit.h(1) circuit.cnot(0, 1) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() @@ -110,7 +109,7 @@ def test_bell_state_phi_plus_creation_with_pauli_meas(self): circuit = Circuit(2) # initialize with |+> \otimes |+> circuit.h(1) circuit.cnot(0, 1) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() pattern.perform_pauli_measurements() @@ -130,7 +129,7 @@ def test_ghz_state_creation_with_pauli_meas(self): circuit.h(2) circuit.cnot(0, 1) circuit.cnot(1, 2) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() pattern.perform_pauli_measurements() @@ -149,7 +148,7 @@ def test_bell_state_and_ry_with_pauli_meas(self): circuit.h(1) circuit.cnot(0, 1) circuit.ry(1, np.pi / 4) - pattern = circuit.transpile() + pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() pattern.perform_pauli_measurements() diff --git a/tox.ini b/tox.ini index 1a35ab4..65b0c72 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,12 @@ [tox] -envlist = py38, py39, py310, lint +envlist = py39, py310, py311, py312, lint [gh-actions] python = - 3.8: lint, py38 - 3.9: py39 + 3.9: lint, py39 3.10: py310 3.11: py311 + 3.12: py312 [testenv] description = Run the unit tests