Legacy

While, with its latest versions, Perceval tends to stabilise its public API, some changes may break existing user code.

This section lists the major breaking changes.

Breaking changes in Perceval 1.3

Global workflow change

The top layers of perceval (starting from Processor) have been completely re-written to support new features, and make the whole process more versatile and easy to write.

The former runtime workflow is still available from perceval.runtime.legacy, and should continue to work, but will not offer all the possibilities that the new workflow offers. It is strongly recommended to swap to the new workflow as soon as possible.

In the new workflow, an Experiment describes what is run, a computer describes where it is run, and an Execution controls the run. The following table shows the equivalent code for every class exported by the legacy runtime package. Most of the new classes have a one-to-one relationship with the legacy workflow, and most of their methods share the same name and signature to help the transition, but there are still a few exceptions. Also, the two workflows can’t be mixed together.

The snippets use the following imports (and placeholder values TOKEN and PROJECT_ID for remote calls):

import perceval as pcvl
from perceval.algorithm import Sampler, Analyzer
from perceval.runtime import legacy
Legacy runtime migration examples

Old class

Old workflow

New workflow

Processor

processor = legacy.Processor("SLOS", pcvl.BS(), noise)
processor.with_input(pcvl.FockState([1, 1]))
experiment = pcvl.Experiment(pcvl.BS())
experiment.with_input(pcvl.FockState([1, 1]))
computer = pcvl.SimulatedComputer("SLOS")
computer.noise = noise

RemoteProcessor

processor = legacy.RemoteProcessor("sim:slos", TOKEN)
communication_layer = pcvl.QuandelaCommunicationLayer("sim:slos", TOKEN)
computer = pcvl.RemoteComputer(communication_layer)

Sampler

sampler = Sampler(processor)
factory = pcvl.ExecutionFactory(computer, experiment)

ScalewaySession

KipuSession

QuandelaSession

session = pcvl.ScalewaySession(
  "EMU-SAMPLING-L4", PROJECT_ID, TOKEN)
with session:
    processor = session.build_remote_processor()
    # Run jobs
communication_layer = pcvl.ScalewayCommunicationLayer(
  "EMU-SAMPLING-L4", PROJECT_ID, TOKEN)
computer = pcvl.RemoteComputer(communication_layer)
with computer.acquire():
    # Run executions

Job

LocalJob

RemoteJob

sampler = Sampler(processor)
job: legacy.Job = sampler.probs
results = job(n_samples=1000)
job = sampler.probs.execute_async(n_samples = 5000)
factory = pcvl.ExecutionFactory(computer, experiment)
execution: pcvl.Execution = factory.probs
with computer.acquire():
    results = execution(n_samples=1000)
    execution = factory.probs.execute_async(n_samples = 5000)

JobGroup

group = legacy.JobGroup("my group")
group.add(Sampler(processor).samples,
          max_samples=1_000)
group.launch_async_jobs()
group = pcvl.ExecutionGroup("my group")
factory = pcvl.ExecutionFactory(computer, experiment)
group.add(factory.samples, max_samples=1_000)
with computer.acquire():
   group.launch_async_executions()
   ...

Analyzer

StateTomography

analyzer = Analyzer(processor, [pcvl.FockState([1, 1])], "*")
analyzer.compute()
with computer.acquire()
    # Same class - only the arguments change
    analyzer = Analyzer(
      experiment, computer, [pcvl.FockState([1, 1])], "*")
    analyzer.compute()

Note

LocalJob and RemoteJob both become Execution; whether the execution is local or remote is determined by its computer, allowing both remote and local Execution to be used in ExecutionGroup.

Warning

Already stored JobGroup won’t be loaded automatically when using the same name in an ExecutionGroup since the inner representation and storage location have changed.

Note that the with computer.acquire() context is not mandatory, but will handle automatically the computer’s lifetime if one exists (start/stop) and make your code more versatile. As such, it is advised to use it only once and place it at the highest level possible.

Noise and Experiment

The NoiseModel used to be an attribute of the Experiment class. While this is still possible, and should still work, it is strongly advised to set this as a member of the Computer that will execute the computation.

RemoteProcessor vs RemoteComputer

A few things have been removed between the RemoteProcessor and the RemoteComputer. Here is a list of changes:

  • The name attribute doesn’t exist anymore, so platforms can no longer be changed on-the-fly

  • The get_rpc_handler() method no longer exists as RemoteComputer do not have to rely on it. Using a RPCHandler manually should not be done anymore.

  • The resume_job() method no longer exists. To retrieve an Execution from the cloud, it must have been serialized before (manually, or automatically by using an ExecutionGroup)

Algorithms

The algorithm classes (Analyzer, Tomography) now store a copy of the given Experiment (including if it’s given as a Processor), so any change to it after instantiating the algorithm won’t affect the results of the algorithm anymore (including setting values to the original parameters - that must be done before instantiating the algorithm).

Breaking changes in Perceval 1.2.3

BSDistribution must be imported from Perceval

The BSDistribution class now relies on 2 utility classes and cannot be imported from exqalibur anymore.

Breaking changes in Perceval 1.2

Processor place in the package

The AProcessor and Processor classes have been moved from perceval.components to perceval.runtime, so they are at the same place as the RemoteProcessor. While importing from perceval.components should still work, it is expected to be completely removed in a few versions, and now produces a warning. Any code importing these classes directly from the root of perceval should continue to work fine.

Also, the build_processor() method from the catalog items is now deprecated. The method build_experiment() should now be used instead.

Although some classes were ported to Exqalibur, their python versions can still be accessed if needed. No class name has been changed, so Simulator or SLOSBackend still points to the python version. The python SLOS is still available in Processors under the name "SLOS_LEGACY"

Tokens that were saved before perceval 0.13 will no longer be loaded by perceval due to previous changes to their storage. Loading them and saving them in a perceval 1.1 should be enough to do the transition.

The new PlatformSpecs object that is now returned by RemoteProcessor.specs should be accessed through its attributes and no longer as a dictionary. While this remains possible, it is now deprecated.

Breaking changes in Perceval 1.1

JobGroup number of parallel launch

The number of jobs that a user can run is now directly retrieved from the cloud. AS such, the set_cloud_maximal_job_count and get_cloud_maximal_job_count from RemoteConfig are now deprecated and no longer work.

Breaking changes in Perceval 1.0

FockState was split in three different classes

To achieve better optimisation in noisy simulation and to clarify the intent of different states usage, it has been decided to get rid of the former generic FockState that could hold richly annotated photons as well as just a plain perfect state.

Definition of the new classes

  • FockState: A light-weight object only containing photon positions in mode (e.g. |1,0,1>). Can be used to represent detections.

  • NoisyFockState: A collection of indistinguishable photon groups, that are totally distinguishable. The distinguishability index is an integer and is referred to as the noise tag (e.g. |{0},{1},{0}{2}> contains three groups of indistinguishable photons tagged 0, 1 and 2).

  • AnnotatedFockState: Replace the previous FockState by allowing rich annotations, having one or more string types, each having a complex number for value. This enables to accurately encode physical parameters and play with partial distinguishability (e.g. |{P:H,lambda:0.625},{P:V,lambda:0.618}>). Please note that apart from polarisation, Perceval does not provide a generic algorithm to separate rich annotated states, and the user would have to write one.

The most breaking change here is that perceval is not able to simulate AnnotatedFockState, apart from polarized ones. Any code manually using annotations to generate distinguishability must be changed to use the new NoisyFockState class. For instance, a BasicState("|{_:0}, {_:1}>") from perceval 0.x must be changed to BasicState("|{0}, {1}>") to be able to be simulated.

For more advanced usage of AnnotatedFockState and NoisyFockState, see the new Quantum States notebook.

Some calls will use or return only the type that makes sense (e.g. AnnotatedFockState::threshold_detection() always returns a FockState as a detected state naturally loses all kinds of photon annotation.

Note

Note that arithmetic still works between states of different types. The result is the most complex type of both operands (e.g. NoisyFockStateFockState gives a NoisyFockState).

Usage in Perceval

The BasicState class still exists and has the same responsibility as before: representing any non superposed pure state. It can construct any of the forementioned state type from a string representation, of vectors of position, and optionally noise tags or annotations.

Even though, Perceval code makes it so isinstance(any_fockstate, BasicState) returns True, the type hinting of user code in an IDE could alert that the types do not match after the update.

Note

StateVector (and therefore SVDistribution) accepts any of the three Fock state types as components.

Processor add with Component or Circuit

When adding a Circuit or a Component to a Processor on non-consecutive modes, a permutation was added so that we could add the component to the Processor. The inverse permutation is now also added after the component so that the in-between modes are not impacted by the addition, similarly to what was already done when adding a Processor to a Processor.

BSDistribution and SVDistribution

These classes have been moved to Exqalibur with a C++ implementation. As such, they are no longer Python dictionaries and may not support some advanced dict features. This has several consequences:

  • You can no longer instantiate BSDistribution or SVDistribution using a dictionary with mixed type keys, nor with non-BasicState or non-StateVector keys.

  • BSDistribution and SVDistribution can no longer be compared to a regular dict (for example by using ==).

  • The order of insertion is no longer preserved.

  • keys() and values() methods now return an iterator, so methods like len no longer work on their result.

Also, note that:

  • Inserting a StateVector in SVDistribution no longer normalises it.

  • Using the tensor product with an empty distribution now always returns an empty distribution. To keep the same behaviour as before (the result was the non-empty distribution), one would have to replace the empty distribution by a distribution containing a void state (BSDistribution(BasicState())) for tensor product or a 0-photon state (BSDistribution(BasicState(m))) for a merge.

StateVector

The method StateVector.keys() now returns an iterator on the keys instead of a BSSamples. This avoids doing unnecessary copy.

Please note that due to this change:

  • Keys must now be copied before being modified when iterating on StateVector.keys().

  • StateVector.keys() no longer has list methods such as len, __getitem__

Removal of deprecated methods and classes

The following methods and classes have been removed or definitely modified as they were deprecated:

  • TokenProvider (deprecated since 0.13, replaced by RemoteConfig)

  • AProbAmpliBackend (deprecated since 0.12, replaced by AStrongSimulationBackend)

  • postselect_independent (deprecated since 0.12, replaced by PostSelect method is_independent_with)

  • The n parameter of SLOS backend (deprecated since 0.12, now automatically chosen when using set_input_state)

  • thresholded_output method of Processor and RemoteProcessor (deprecated since 0.12, replaced by adding several Detector.threshold())

  • with_polarized_input method of Processor (because Processor.with_input is now able to handle a polarized AnnotatedFockState transparently)

  • tensorproduct(states: list) from perceval.utils (due to tensor products being handled well by multiplication operators and specific methods - see BSDistribution.list_tensor_product, for instance)

  • JobGroup.list_existing() has been renamed into JobGroup.list_locally_saved()

NoiseModel

The way of NoiseModel to handle its attributes has changed to be more pythonic. Now, your IDE should be able to tell that the attributes exist in the class, and the attributes can be changed using a syntax like noise_model.g2 = 0.1.

This change is accompanied by the removal of some methods:

  • The __getitem__ has been removed since it was giving a class that is not accessible anymore

  • The set_value method has been removed, and can be replaced either by spelling directly the attribute (noise_model.g2 = 0.1) or by using the python method setattr(noise_model, "g2", 0.1).

Older changes

The documentation to update from an older legacy version to a more recent one can still be found here.