Source code for pyquil.external.rpcq

"""Models and functions that support the use of the `quilc` rpcq server."""

import json
from dataclasses import dataclass, field
from typing import Literal, Optional, Union

from deprecated.sphinx import deprecated
from rpcq.messages import TargetDevice as TargetQuantumProcessor

JsonValue = Union[type(None), bool, int, float, str, list["JsonValue"], dict[str, "JsonValue"]]


[docs] @dataclass class Operator: """Operator class for representing a quantum gate or measurement.""" operator: Optional[str] = None duration: Optional[float] = None fidelity: Optional[float] = None def __post_init__(self): self.duration = float(self.duration) if self.duration is not None else None self.fidelity = float(self.fidelity) if self.fidelity is not None else None def _dict(self) -> dict[str, JsonValue]: if type(self) is Operator: raise ValueError("Should be a subclass") return dict( operator=self.operator, duration=self.duration, fidelity=self.fidelity, )
[docs] @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def dict(self): """Return a dictionary representation of the Operator.""" return self._dict()
@classmethod def _parse_obj(cls, dictionary: dict): return Operator(**dictionary)
[docs] @classmethod @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def parse_obj(cls, dictionary: dict): """Parse a dictionary into an Operator object.""" return cls._parse_obj(dictionary)
[docs] @dataclass class MeasureInfo(Operator): """MeasureInfo class for representing a measurement operation.""" qubit: Optional[Union[int, str]] = None target: Optional[Union[int, str]] = None operator_type: Literal["measure"] = "measure" def __post_init__(self): """Post initialization method for MeasureInfo.""" self.qubit = str(self.qubit) def _dict(self) -> dict[str, JsonValue]: return dict( operator_type=self.operator_type, operator=self.operator, duration=self.duration, fidelity=self.fidelity, qubit=self.qubit, target=self.target, )
[docs] @deprecated( version="4.7", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def dict(self): """Return a dictionary representation of the MeasureInfo.""" return self._dict()
@classmethod def _parse_obj(cls, dictionary: dict) -> "MeasureInfo": return MeasureInfo( operator=dictionary.get("operator"), duration=dictionary.get("duration"), fidelity=dictionary.get("fidelity"), qubit=dictionary.get("qubit"), target=dictionary.get("target"), )
[docs] @classmethod @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def parse_obj(cls, dictionary: dict): """Parse a dictionary into a MeasureInfo object.""" return cls._parse_obj(dictionary)
[docs] @dataclass class GateInfo(Operator): """GateInfo class for representing a quantum gate operation.""" parameters: list[Union[float, str]] = field(default_factory=list) arguments: list[Union[int, str]] = field(default_factory=list) operator_type: Literal["gate"] = "gate" def _dict(self) -> dict[str, JsonValue]: return dict( operator_type=self.operator_type, operator=self.operator, duration=self.duration, fidelity=self.fidelity, parameters=self.parameters, arguments=self.arguments, )
[docs] @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def dict(self): """Return a dictionary representation of the GateInfo.""" return self._dict()
@classmethod def _parse_obj(cls, dictionary: dict) -> "GateInfo": return GateInfo( operator=dictionary.get("operator"), duration=dictionary.get("duration"), fidelity=dictionary.get("fidelity"), parameters=dictionary["parameters"], arguments=dictionary["arguments"], )
[docs] @classmethod @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def parse_obj(cls, dictionary: dict): """Parse a dictionary into a GateInfo object.""" return cls._parse_obj(dictionary)
def _parse_operator(dictionary: dict) -> Union[GateInfo, MeasureInfo]: operator_type = dictionary["operator_type"] if operator_type == "measure": return MeasureInfo._parse_obj(dictionary) if operator_type == "gate": return GateInfo._parse_obj(dictionary) raise ValueError("Should be a subclass of Operator")
[docs] @dataclass class Qubit: """Qubit class for representing a qubit in a quantum processor.""" id: int dead: Optional[bool] = False gates: list[Union[GateInfo, MeasureInfo]] = field(default_factory=list) def _dict(self) -> dict[str, JsonValue]: encoding = dict(id=self.id, gates=[g._dict() for g in self.gates]) if self.dead: encoding["dead"] = self.dead return encoding
[docs] @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def dict(self): """Return a dictionary representation of the Qubit.""" return self._dict()
@classmethod def _parse_obj(cls, dictionary: dict) -> "Qubit": return Qubit( id=dictionary["id"], dead=bool(dictionary.get("dead")), gates=[_parse_operator(v) for v in dictionary.get("gates", [])], )
[docs] @classmethod @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def parse_obj(cls, dictionary: dict): """Parse a dictionary into a Qubit object.""" return cls._parse_obj(dictionary)
[docs] @dataclass class Edge: """Edge class for representing a connection between two qubits.""" ids: list[int] dead: Optional[bool] = False gates: list[GateInfo] = field(default_factory=list) def _dict(self) -> dict[str, JsonValue]: encoding = dict(ids=self.ids, gates=[g._dict() for g in self.gates]) if self.dead: encoding["dead"] = self.dead return encoding
[docs] @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def dict(self): """Return a dictionary representation of the Edge.""" return self._dict()
@classmethod def _parse_obj(cls, dictionary: dict) -> "Edge": return Edge( ids=dictionary["ids"], dead=bool(dictionary.get("dead")), gates=[GateInfo._parse_obj(g) for g in dictionary.get("gates", [])], )
[docs] @classmethod @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def parse_obj(cls, dictionary: dict): """Parse a dictionary into an Edge object.""" return cls._parse_obj(dictionary)
[docs] @dataclass class CompilerISA: """CompilerISA class for representing the instruction set architecture of a quantum processor.""" qubits: dict[str, Qubit] = field(default_factory=dict) edges: dict[str, Edge] = field(default_factory=dict) def _dict(self, by_alias=False) -> dict[str, JsonValue]: return { "1Q" if by_alias else "qubits": {k: q._dict() for k, q in self.qubits.items()}, "2Q" if by_alias else "edges": {k: e._dict() for k, e in self.edges.items()}, }
[docs] @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def dict(self, by_alias=False): """Return a dictionary representation of the CompilerISA.""" return self._dict(by_alias=by_alias)
@classmethod def _parse_obj(cls, dictionary: dict): qubit_dict = dictionary.get("1Q", {}) edge_dict = dictionary.get("2Q", {}) return CompilerISA( qubits={k: Qubit._parse_obj(v) for k, v in qubit_dict.items()}, edges={k: Edge._parse_obj(v) for k, v in edge_dict.items()}, )
[docs] @classmethod @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def parse_obj(cls, dictionary: dict): """Parse a dictionary into a CompilerISA object.""" return cls._parse_obj(dictionary)
[docs] @classmethod @deprecated( version="4.6.2", reason="No longer requires serialization of RPCQ objects and is dropping Pydantic as a dependency.", # noqa: E501 ) def parse_file(cls, filename: str): """Parse a JSON file into a CompilerISA object.""" with open(filename) as file: json_dict = json.load(file) return cls._parse_obj(json_dict)
[docs] def add_qubit(quantum_processor: CompilerISA, node_id: int) -> Qubit: """Add a qubit to the quantum processor ISA.""" if node_id not in quantum_processor.qubits: quantum_processor.qubits[str(node_id)] = Qubit(id=node_id) return quantum_processor.qubits[str(node_id)]
[docs] def get_qubit(quantum_processor: CompilerISA, node_id: int) -> Optional[Qubit]: """Get a qubit from the quantum processor ISA.""" return quantum_processor.qubits.get(str(node_id))
[docs] def make_edge_id(qubit1: int, qubit2: int) -> str: """Make an edge ID from two qubit IDs.""" return "-".join([str(qubit) for qubit in sorted([qubit1, qubit2])])
[docs] def add_edge(quantum_processor: CompilerISA, qubit1: int, qubit2: int) -> Edge: """Add an Edge between two qubit IDs.""" edge_id = make_edge_id(qubit1, qubit2) if edge_id not in quantum_processor.edges: quantum_processor.edges[edge_id] = Edge(ids=sorted([qubit1, qubit2])) return quantum_processor.edges[edge_id]
[docs] def get_edge(quantum_processor: CompilerISA, qubit1: int, qubit2: int) -> Optional[Edge]: """Get an Edge between two qubit IDs.""" edge_id = make_edge_id(qubit1, qubit2) return quantum_processor.edges.get(edge_id)
[docs] def compiler_isa_to_target_quantum_processor(compiler_isa: CompilerISA) -> TargetQuantumProcessor: """Convert a CompilerISA object to a TargetQuantumProcessor object.""" return TargetQuantumProcessor(isa=compiler_isa.dict(by_alias=True), specs={})
[docs] class Supported1QGate: """Supported 1Q gates.""" I = "I" # noqa: E741 - I is not an ambiguous gate name. RX = "RX" RZ = "RZ" MEASURE = "MEASURE" WILDCARD = "WILDCARD"
[docs] class Supported2QGate: """Supported 2Q gates.""" WILDCARD = "WILDCARD" CZ = "CZ" ISWAP = "ISWAP" CPHASE = "CPHASE" XY = "XY"