loom.executor.eka_to_pennylane_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_pennylane_converter.EkaToPennylaneConverter(**data)[source]

Bases: Converter[str, dict[str, Any]]

Convert Eka InterpretationStep to PennyLane.

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

from loom.executor import EkaToPennylaneConverter

pl_converter = EkaToPennylaneConverter(is_catalyst=False)
pl_program, q_register, c_register = pl_converter.convert(interpreted_eka)
n_qubits = len(q_register)

# Indent the program body so it fits inside a function
indented_program = "\n    ".join(pl_program.splitlines())

# Construct the Python program string
s_prog = f"""
import pennylane as qml

def circuit():
    {indented_program}
    return {{k:qml.sample(measurements[k]) for k in measurements.keys()}}

# Use a PennyLane device
dev = qml.device("lightning.qubit", wires=n_qubits, shots=5)
circ = qml.QNode(circuit, dev)

results = circ()
"""
local_ns = {}
exec(s_prog, globals(), local_ns)

results = local_ns["results"]

parsed_outcome = pl_converter.parse_target_run_outcome(results)
Parameters:
  • is_catalyst (bool) – Whether the PennyLane program is intended to run on Catalyst.

  • import_prefix (str) – The import alias for PennyLane defaults to “qml.”.

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
SUPPORTED_OPERATIONS: frozenset[OpSignature]
convert_circuit(input_circuit)[source]

Convert an Eka Circuit to PennyLane code string along with quantum and classical channel maps.

Return type:

tuple[str, dict[str, int], dict[str, str]]

Returns:

A tuple containing:

  • The PennyLane code string representing the circuit.

  • A dictionary mapping Eka quantum channel IDs to PennyLane wire indices.

  • A dictionary mapping Eka classical channel IDs to PennyLane measurement keys.

emit_init_instructions(circuit)[source]

Provide a mapping from eka channel label to initialized PennyLane wire index.

Return type:

tuple[str, dict[str, int], dict[str, str]]

emit_leaf_circuit_instruction(circuit, quantum_channel_map, classical_channel_map)[source]

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

Return type:

str

import_prefix: str
is_catalyst: bool
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=False, frozen=True, init=False), '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='')})), 'import_prefix': FieldInfo(annotation=str, required=False, default='qml.', description='The import alias for PennyLane.', frozen=True, init=True), 'is_catalyst': FieldInfo(annotation=bool, required=False, default=False, frozen=True, init=True), '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[str], list[str], str | None], str]]

Map Eka operations to PennyLane instructions. The map return a sequence of PennyLane instructions, which are specified by name, wires, classical register label, and whether it is a measurement or classically controlled operation.

static parse_target_run_outcome(outcome)[source]

Parse the PennyLane/catalyst run outcome into a dictionary mapping the Eka classical channels labels to the measurement outcomes.

Return type:

dict[str, list[int]]

classmethod validate_import_prefix(v)[source]

Ensure the import prefix ends with a dot if not empty.

Return type:

str