Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion perceval/components/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ def _compose_experiment(self, connector: ModeConnector, experiment: Experiment,
# Add PERM, component, (PERM^-1 if is_symmetrical)
perm_modes, perm_component = connector.generate_permutation(mode_mapping)
new_components = []
out_mode_type = self._out_mode_type # Store for later use
if perm_component is not None:
get_logger().debug(
f" Add {perm_component.perm_vector} permutation before experiment compose", channel.general)
Expand Down Expand Up @@ -476,6 +477,10 @@ def _compose_experiment(self, connector: ModeConnector, experiment: Experiment,
self.add_port(port_mode, port, PortLocation.OUTPUT)

new_components = simplify(new_components, self.circuit_size)
for component in new_components:
for m in component[0]:
if m < len(out_mode_type) and out_mode_type[m] == ModeType.CLASSICAL:
raise UnavailableModeException(m, "Can't add components on classical modes")
self._components += new_components

# Retrieve ports from the other experiment
Expand Down Expand Up @@ -503,9 +508,10 @@ def _compose_experiment(self, connector: ModeConnector, experiment: Experiment,
for m in range(experiment.circuit_size):
# The heralded modes detectors have already been added at the bottom modes
d = experiment.detectors[m]
new_mode = list(mode_mapping.keys())[list(mode_mapping.values()).index(m)]
if m not in experiment.heralds and d is not None:
new_mode = list(mode_mapping.keys())[list(mode_mapping.values()).index(m)]
self._detectors[new_mode] = d
self._out_mode_type[new_mode] = experiment._out_mode_type[m]

if self._postselect is not None and perm_component is not None and not is_symmetrical:
c_first = perm_modes[0]
Expand Down Expand Up @@ -537,6 +543,9 @@ def _add_component(self, mode_mapping, component, keep_port: bool):

perm_modes, perm_component = ModeConnector.generate_permutation(mode_mapping)
if perm_component is not None:
for m in perm_modes:
if self._out_mode_type[m] == ModeType.CLASSICAL:
raise UnavailableModeException(m, "Can't add permutations on classical modes")
self._components.append((perm_modes, perm_component))

sorted_modes = tuple(range(min(mode_mapping), min(mode_mapping) + component.m))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,32 @@
import math
import pytest

from perceval import PortLocation
from perceval import PortLocation, ModeType
from perceval.components import (catalog, Circuit, BS, PS, PERM, Detector, UnavailableModeException,
FFConfigurator, FFCircuitProvider, Unitary, Barrier)
FFConfigurator, FFCircuitProvider, Unitary, Barrier, Experiment)
from perceval.utils import Matrix, P, LogicalState
from perceval.runtime import RemoteProcessor, Processor

from tests.runtime._mock_rpc_handler import get_rpc_handler_for_tests


def test_processor_composition():
p = catalog['postprocessed cnot'].build_processor() # Circuit with [0,1] and [2,3] post-selection conditions
p = catalog['postprocessed cnot'].build_experiment() # Circuit with [0,1] and [2,3] post-selection conditions
p.add((0, 1), BS()) # Composing with a component on modes [0,1] should work
with pytest.raises(AssertionError):
p.add((1, 2), BS()) # Composing with a component on modes [1,2] should fail
p_bs = Processor("SLOS", BS())
p_bs = Experiment(BS())
p.add((0, 1), p_bs) # Composing with a processor on modes [0,1] should work
with pytest.raises(AssertionError):
p.add((1, 2), p_bs) # Composing with a processor on modes [1,2] should fail


def test_composition_error_post_selection():
processor = catalog['postprocessed cnot'].build_processor()
processor = catalog['postprocessed cnot'].build_experiment()
# Composing 2 CNOTs on the exact same modes should work in theory, but not in the current implementation,
# it's still possible to apply a PostSelect manually to the resulting Processor.
with pytest.raises(AssertionError):
processor.add(0, processor)

processor2 = Processor("SLOS", 5)
pp_cnot = catalog['postprocessed cnot'].build_processor()
processor2 = Experiment(5)
pp_cnot = catalog['postprocessed cnot'].build_experiment()
processor2.add(0, pp_cnot)
# It's 100% valid that this 2nd case is blocked
with pytest.raises(AssertionError):
Expand All @@ -69,16 +66,16 @@ def test_processor_composition_mismatch_modes():
# tests composing a smaller processor into larger one works
# without breaking simplification (verifies it works with gates based circuits too)
def sub_size_processor():
h_cnot = catalog['heralded cnot'].build_processor()
p = Processor('SLOS', m_circuit=4, name='my_example')
h_cnot = catalog['heralded cnot'].build_experiment()
p = Experiment(4, name='my_example')
p.add(0, BS.H())
p.add(0, h_cnot)
p.add(1, PS(math.pi / 4))
p.add(0, h_cnot)
return p

smaller_processor = sub_size_processor()
p = Processor('SLOS', m_circuit=5, name='to_Which_i_add')
p = Experiment(5, name='to_Which_i_add')
p.add(0, smaller_processor)

assert len(p.components) == 7 # 3 PERMs get added because heralds need to move
Expand All @@ -105,24 +102,23 @@ def sub_size_processor():


def test_processor_add_detector():
p = Processor("SLOS", 4)
p.add(0, Detector.pnr())
p = Experiment(4)
p.add(1, Detector.pnr())
with pytest.raises(UnavailableModeException):
p.add(0, PS(phi=0)) # Cannot add an optical component after a detector
p.add(1, PS(phi=0)) # Cannot add an optical component after a detector
with pytest.raises(UnavailableModeException):
p.add(0, Detector.pnr()) # Cannot add a detector after a detector


def test_remote_processor_creation():
rp = RemoteProcessor(rpc_handler=get_rpc_handler_for_tests(), m=8)
rp.add(0, BS())
p.add(1, Detector.pnr()) # Cannot add a detector after a detector
with pytest.raises(UnavailableModeException):
p.add([0, 2], BS()) # Cannot add an automatically generated permutation after a detector
with pytest.raises(UnavailableModeException):
p.add([0, 2], Experiment(BS())) # Cannot add an automatically generated permutation after a detector


def test_processor_composition_ports():
ls = LogicalState([0, 0])
cnot = catalog['postprocessed cnot'].build_processor()
cnot = catalog['postprocessed cnot'].build_experiment()

rp = RemoteProcessor(rpc_handler=get_rpc_handler_for_tests(), m=4)
rp = Experiment(4)
rp.min_detected_photons_filter(2)
rp.add(0, cnot)
rp.with_input(ls)
Expand All @@ -145,7 +141,7 @@ def test_processor_composition_ports():
def test_processor_building_feed_forward():
m = 4
u = Unitary(Matrix.random_unitary(m), "U0")
p = Processor("SLOS", u)
p = Experiment(u)

ffm = FFCircuitProvider(1, 0, Unitary(Matrix.random_unitary(1)), name="D2")

Expand All @@ -170,7 +166,7 @@ def test_processor_building_feed_forward():
def test_processor_feed_forward_multiple_layers():
m = 4
u = Unitary(Matrix.random_unitary(m), "U0")
p = Processor("SLOS", u)
p = Experiment(u)
p.add(2, Detector.pnr())
mzi = catalog["mzi phase last"].build_circuit()
mzi.name = "U1"
Expand All @@ -191,7 +187,7 @@ def test_processor_feed_forward_multiple_layers():
def test_ff_controlled_circuit_size():
m = 4
u = Unitary(Matrix.random_unitary(m), "U0")
p = Processor("SLOS", u)
p = Experiment(u)

ffm = FFCircuitProvider(1, 0, Circuit(1), name="D2")
ffm.add_configuration((1,), Circuit(2)) # Can add a larger circuit than the default one before it's used
Expand All @@ -204,17 +200,19 @@ def test_ff_controlled_circuit_size():


def test_asymmetrical_composition():
p = Processor("SLOS", 3)
p = Experiment(3)
p.add(0, BS())
p.add(1, BS())
p.add_herald(0, 0, location=PortLocation.OUTPUT)
p.add_herald(2, 1, location=PortLocation.INPUT)

p2 = Processor("SLOS", 3)
p2 = Experiment(3)
p2.add(0, BS())
p2.add(1, BS())
p2.add_herald(0, 2, location=PortLocation.INPUT)
p2.add_herald(2, 3, location=PortLocation.OUTPUT)
detector_2 = Detector.ppnr(2)
p2.add(0, detector_2)

p.add(1, p2)

Expand All @@ -223,19 +221,23 @@ def test_asymmetrical_composition():
assert p.in_heralds == {2: 1, 3: 2}
assert p.heralds == {0: 0, 3: 3}

assert p._out_mode_type == [ModeType.HERALD, ModeType.CLASSICAL, ModeType.PHOTONIC, ModeType.HERALD]


def test_detector_composition():
detector_2 = Detector.ppnr(2)
detector_3 = Detector.ppnr(3)
detector_4 = Detector.ppnr(4)

p = Processor("SLOS", 3)
p = Experiment(4)
p.add(1, detector_2)

p2 = Processor("SLOS", 2)
p2 = Experiment(2)
p2.add(0, detector_3)
p2.add(1, detector_4)

p.add({2: 0, 0: 1}, p2)

assert p.detectors == [detector_4, detector_2, detector_3]
assert p.detectors == [detector_4, detector_2, detector_3, None]

assert p._out_mode_type == [ModeType.CLASSICAL, ModeType.CLASSICAL, ModeType.CLASSICAL, ModeType.PHOTONIC]
8 changes: 7 additions & 1 deletion tests/runtime/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@
from unittest.mock import patch

import perceval as pcvl
from perceval import BSDistribution, FockState, NoisyFockState, Experiment, FFCircuitProvider
from perceval import BSDistribution, FockState, NoisyFockState, Experiment, FFCircuitProvider, RemoteProcessor
from perceval.components import Circuit, BS, PS, catalog, UnavailableModeException, Port, PortLocation, \
PERM, Detector
from perceval.utils import BasicState, StateVector, SVDistribution, Encoding, NoiseModel, P
from perceval.backends import Clifford2017Backend
from perceval.runtime import Processor

from ._mock_rpc_handler import get_rpc_handler_for_tests
from .._test_utils import LogChecker, assert_svd_close, assert_bsd_close


Expand Down Expand Up @@ -429,3 +430,8 @@ def test_get_parameters():
params = e.get_circuit_parameters()

assert set(params.keys()) == {"theta0", "theta1", "theta2"}


def test_remote_processor_creation():
rp = RemoteProcessor(rpc_handler=get_rpc_handler_for_tests(), m=8)
rp.add(0, BS())
Loading