loom.executor.eka_to_stim_converter

Copyright 2024 Entropica Labs Pte Ltd

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

class loom.executor.eka_to_stim_converter.EkaToStimConverter(**data)[source]

Bases: ], dtype[bool]], dict[Channel, tuple[str, GateTarget]]]]

Convert an InterpretationStep to a stim.Circuit.

Here’s a simple example of how to use this method to execute Eka experiment in Stim:

from loom.executor import EkaToStimConverter

stim_converter = EkaToStimConverter()
stim_program, quant_register, class_register = stim_converter.convert(
    eka
)

import stim
st = stim_program
sampler = st.compile_sampler()
result = sampler.sample(shots=5)

print(stim_converter.parse_target_run_outcome((result, class_register)))

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

ALLOW_ERROR_MODELS: bool
STIM_CLASSICALLY_CONTROLLED_OPS: frozenset[str]
SUPPORTED_ERROR_TYPES: frozenset[ErrorType]
SUPPORTED_OPERATIONS: frozenset[OpSignature]
convert(interpreted_eka, error_models=None, with_ticks=False)[source]

Convert a InterpretationStep. Call the default convert method to convert the final circuit of the step. Then, add the detectors and logical observables to the stim circuit.

Return type:

tuple[Circuit, dict[Channel, tuple[str, GateTarget]], dict[Channel, int]]

convert_circuit(input_circuit, error_models=None, with_ticks=False)[source]

Convert a Circuit to a text in stim format and a mapping of channel labels to stim target or measurement record indices.

Parameters:
  • input_circuit (Circuit) – The EKA circuit to convert.

  • with_ticks (bool, optional) – Whether to include TICK instructions in the stim circuit, by default False.

Returns:

A tuple containing the stim circuit, a dictionary mapping quantum channel ids to stim qubit targets and coordinates and a dictionary mapping classical channel ids to stim measurement record indices,

Return type:

StimCircuitAndRegisters

emit_circuit_program(input_circuit, error_models=None, with_ticks=False)[source]

Convert a Circuit to a text in stim format and a mapping of channel labels to stim target or measurement record indices.

Parameters:
  • input_circuit (Circuit) – The EKA circuit to convert.

  • with_ticks (bool, optional) – Whether to include TICK instructions in the stim circuit, by default False.

Returns:

A tuple containing the stim circuit as a string, a dictionary mapping quantum channel ids to stim qubit targets and coordinates and a dictionary mapping classical channel ids to stim measurement record indices,

Return type:

str, dict[Channel, tuple[str, stim.GateTarget]], dict[Channel, int]

static emit_detectors_and_observables(input_istep, rec_dict, measurement_offset=0)[source]

Get the detector instructions for the given interpretation step, and the given measurement offset which corresponds to the number of measurements in the circuit before adding detectors.

Parameters:
  • input_istep (InterpretationStep) – The interpretation step to get detector instructions for.

  • rec_dict (dict[str, int]) – A mapping from channel labels to their corresponding record indices.

  • measurement_offset (int, optional) – An offset to apply to the measurement indices, by default 0

Returns:

A list of stim circuit instructions for the detector measurements.

Return type:

list[str]

Raises:

ValueError – If the detector labels are not in the expected format.

emit_init_instructions(circuit)[source]

Provide the python code (as a string) to initializes the quantum and classical registers, and return the mapping from eka channel to register.

In the beginning of the circuit, the mapping of classical channels to measurement record indices is None, since no classical channels have been measured yet.

Return type:

tuple[str, dict[Channel, Any], dict[Channel, Any]]

emit_leaf_circuit_instruction(circuit, quantum_channel_map, classical_channel_map, measurement_record_counter=0)[source]

Provide the python code (as a string) to emit an Eka instruction in the target language.

Return type:

str

model_computed_fields: ClassVar[Dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, FieldInfo]] = {'ALLOW_ERROR_MODELS': FieldInfo(annotation=bool, required=False, default=True, frozen=True, init=False), 'STIM_CLASSICALLY_CONTROLLED_OPS': FieldInfo(annotation=frozenset[str], required=False, default=frozenset({OpSignature(name='classical_controlled_z', op_type=<OpType.CUSTOM: 'custom'>, quantum_input=1, classical_input=1, is_clifford=True, description=''), OpSignature(name='classical_controlled_x', op_type=<OpType.CUSTOM: 'custom'>, quantum_input=1, classical_input=1, is_clifford=True, description=''), OpSignature(name='classical_controlled_y', op_type=<OpType.CUSTOM: 'custom'>, quantum_input=1, classical_input=1, is_clifford=True, description='')})), 'SUPPORTED_ERROR_TYPES': FieldInfo(annotation=frozenset[ErrorType], required=False, default=frozenset({<ErrorType.PAULI_Y: ('pauli_y', 1)>, <ErrorType.PAULI_CHANNEL: ('pauli_channel', 3)>, <ErrorType.PHASE_FLIP: ('phase-flip', 1)>, <ErrorType.DEPOLARIZING1: ('depolarizing1', 1)>, <ErrorType.PAULI_Z: ('pauli_z', 1)>, <ErrorType.PAULI_X: ('pauli_x', 1)>, <ErrorType.DEPOLARIZING2: ('depolarizing2', 1)>, <ErrorType.BIT_FLIP: ('bit-flip', 1)>})), 'SUPPORTED_OPERATIONS': FieldInfo(annotation=frozenset[OpSignature], required=False, default=frozenset({OpSignature(name='measure_z', op_type=<OpType.MEASUREMENT: 'measurement'>, quantum_input=1, classical_input=1, is_clifford=True, description=''), OpSignature(name='reset_0', op_type=<OpType.RESET: 'reset'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='reset_1', op_type=<OpType.RESET: 'reset'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='measure_y', op_type=<OpType.MEASUREMENT: 'measurement'>, quantum_input=1, classical_input=1, is_clifford=True, description=''), OpSignature(name='z', op_type=<OpType.SINGLE_QUBIT: 'single_qubit'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='phaseinv', op_type=<OpType.SINGLE_QUBIT: 'single_qubit'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='cy', op_type=<OpType.TWO_QUBIT: 'two_qubit'>, quantum_input=2, classical_input=0, is_clifford=True, description=''), OpSignature(name='reset_+', op_type=<OpType.RESET: 'reset'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='cx', op_type=<OpType.TWO_QUBIT: 'two_qubit'>, quantum_input=2, classical_input=0, is_clifford=True, description=''), OpSignature(name='reset_-', op_type=<OpType.RESET: 'reset'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='reset_+i', op_type=<OpType.RESET: 'reset'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='y', op_type=<OpType.SINGLE_QUBIT: 'single_qubit'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='reset_-i', op_type=<OpType.RESET: 'reset'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='cz', op_type=<OpType.TWO_QUBIT: 'two_qubit'>, quantum_input=2, classical_input=0, is_clifford=True, description=''), OpSignature(name='phase', op_type=<OpType.SINGLE_QUBIT: 'single_qubit'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='h', op_type=<OpType.SINGLE_QUBIT: 'single_qubit'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='measurement', op_type=<OpType.MEASUREMENT: 'measurement'>, quantum_input=1, classical_input=1, is_clifford=True, description=''), OpSignature(name='cnot', op_type=<OpType.TWO_QUBIT: 'two_qubit'>, quantum_input=2, classical_input=0, is_clifford=True, description=''), OpSignature(name='measure_x', op_type=<OpType.MEASUREMENT: 'measurement'>, quantum_input=1, classical_input=1, is_clifford=True, description=''), OpSignature(name='i', op_type=<OpType.SINGLE_QUBIT: 'single_qubit'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='reset', op_type=<OpType.RESET: 'reset'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='x', op_type=<OpType.SINGLE_QUBIT: 'single_qubit'>, quantum_input=1, classical_input=0, is_clifford=True, description=''), OpSignature(name='swap', op_type=<OpType.TWO_QUBIT: 'two_qubit'>, quantum_input=2, classical_input=0, is_clifford=True, description='')})), 'separator_for_else_in_condition': FieldInfo(annotation=str, required=False, default=', is_else=', description='The separator string used in the description for else conditions.', frozen=True, init=False)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

property operations_map: dict[str, Callable[[list[list[GateTarget]]], list[str]]]

Map of operation signatures to their corresponding StimCallable, which is a set of stim instructions (as strings) associated with values for the targets and parameters.

static parse_if_operation(if_circuit)[source]

Parse control flow operation, allowing only specific cases supported by Stim. Stim only supports conditional pauli operations, with single bit conditions.

Parameters:

if_circuit (IfElseCircuit) – The IfElseCircuit to parse.

Returns:

A Circuit (gate) representing the parsed if operation in Stim, which is a CX (resp. CY or CZ) operation with a classical channel as control.

Return type:

Circuit

static parse_target_run_outcome(output)[source]

Parse the run output of a stim circuit into a dictionary mapping the Eka classical channels labels to their measurement outcomes.

Parameters:

output (StimOutputRunResult) – The output from stim simulation, which is a tuple of (NDArray of shape (shots, num_qubits), dict mapping classical channel to stim measurement record index).

Returns:

A dictionary mapping the Eka classical channel labels to their measurement outcomes. If multiple shots were run, the outcomes are lists of integers (0 or 1), otherwise a single integer (0 or 1).

Return type:

dict[Channel, int | list[int]]

print_stim_circuit_for_crumble(final_step)[source]

Print the stim circuit along with polygon instructions to be used for crumble

Parameters:

stim_circ (stim.Circuit) – input stim circuit

Return type:

str

classmethod stim_polygons(interpreted_eka)[source]

Define stim polygons using data qubits coordinates involved with each stabilizer on patches passed as argument to the function

DEMO SYNTAX: #!pragma POLYGON(1,0,0,0.25) 5 11 16 23

POLYGON(<X>, <Y>, <Z>, <color intensity>) <data qubits involved>

Since polygon definitions are added as comments in the stim circuit body, and there is no way to add comments programmatically in stim.Circuit This function is only available to print polygon instructions from the patch stabilizers. The user MUST add these comments manually to the stim.Circuit string body to display the polygons in crumble

Parameters:

interpreted_eka (InterpretationStep) – The InterpretationStep object containing information on the code stabilizers

Returns:

stim_polygons – Stim polygon instructions as a string

Return type:

str