Source code for qurry.qurrium.container.waves_dynamic

"""Dynamic Wave Container - A experimental feature of Qurrium
(:mod:`qurry.qurrium.container.waves_dynamic`)

"""

from typing import Literal, Union, Optional, Type, MutableMapping, overload
from collections.abc import Hashable, Callable
import warnings

from qiskit import QuantumCircuit
from qiskit.quantum_info import Operator
from qiskit.circuit import Gate, Instruction

from ...exceptions import QurryUnknownExportOption


[docs] def add( self: MutableMapping[Hashable, QuantumCircuit], wave: QuantumCircuit, key: Optional[Hashable] = None, replace: Literal[True, False, "duplicate"] = True, ) -> Hashable: """Add new wave function to measure. Args: wave (QuantumCircuit): The wave function or circuit to add. key (Optional[Hashable], optional): Given a specific key to add to the wave function or circuit, if `key == None`, then generate a number as key. Defaults to None. replace (Literal[True, False, 'duplicate'], optional): If the key is already in the wave function or circuit, then replace the old wave function or circuit when `True`, or duplicate the wave function or circuit when `'duplicate'`. Defaults to `True`. Returns: Optional[Hashable]: Key of given wave function in `.waves`. """ return _add(self, wave, key, replace)
[docs] def process( self, circuits: list[Union[QuantumCircuit, Hashable]] ) -> list[tuple[Hashable, QuantumCircuit]]: """Process the circuits for Qurrium. Args: circuits (list[Union[QuantumCircuit, Hashable]]): The circuits. Raises: KeyError: If the wave not found in the container. ValueError: If the circuit is invalid. """ return _process(self, circuits)
[docs] def remove(self, key: Hashable): """Remove wave function from measure. Args: key (Hashable): The key of wave function or circuit. """ return _remove(self, key)
@overload def call(self, wave: list[Hashable]) -> list[QuantumCircuit]: ... @overload def call(self, wave: Hashable) -> QuantumCircuit: ...
[docs] def call(self, wave): """Export wave function as :class:`~qiskit.circuit.QuantumCircuit`. Args: wave (Union[list[Hashable], Hashable]): The key of wave in 'dict' `.waves`. Returns: Union[list[QuantumCircuit], QuantumCircuit]: The circuit of wave function. """ return self.get_wave( wave=wave, run_by="call", )
@overload def get_wave( self, wave: list[Hashable], run_by: Optional[Literal["copy", "call"]] ) -> list[QuantumCircuit]: ... @overload def get_wave(self, wave: list[Hashable], run_by: Literal["gate"]) -> list[Gate]: ... @overload def get_wave(self, wave: list[Hashable], run_by: Literal["operator"]) -> list[Operator]: ... @overload def get_wave(self, wave: list[Hashable], run_by: Literal["instruction"]) -> list[Instruction]: ... @overload def get_wave(self, wave: Hashable, run_by: Optional[Literal["copy", "call"]]) -> QuantumCircuit: ... @overload def get_wave(self, wave: Hashable, run_by: Literal["gate"]) -> Gate: ... @overload def get_wave(self, wave: Hashable, run_by: Literal["operator"]) -> Operator: ... @overload def get_wave(self, wave: Hashable, run_by: Literal["instruction"]) -> Instruction: ...
[docs] def get_wave(self, wave=None, run_by=None): """Parse wave Circuit into :class:`~qiskit.circuit.Instruction`, :class:`~qiskit.circuit.Gate`, or `~qiskit.quantum_info.Operator` on :class:`~qiskit.circuit.QuantumCircuit`. Args: wave (Optional[Hashable], optional): The key of wave in `wave`. Defaults to None. run_by (Optional[str], optional): Export as :class:`~qiskit.circuit.Instruction`, :class:`~qiskit.circuit.Gate`, or `~qiskit.quantum_info.Operator` or a copy when input is None. Defaults to None. Raises: ValueError: If `wave is None`. KeyError: If `wave` not in `self`. Returns: The result of the wave. """ if wave is None: raise ValueError("Need to input wave name.") if isinstance(wave, list): return [self.get_wave(w, run_by) for w in wave] if wave not in self: raise KeyError(f"Wave {wave} not found in {self}") actions: dict[ str, Callable[[QuantumCircuit], Union[Gate, Operator, Instruction, QuantumCircuit]] ] = { "operator": Operator, "gate": lambda w: w.to_gate(), "instruction": lambda w: w.to_instruction(), "copy": lambda w: w.copy(), "call": lambda w: w, } run_by = "copy" if run_by is None else run_by if run_by not in actions: warnings.warn( "run_by should be 'gate', 'operator', 'instruction', 'copy' or 'call', " + f"but got {run_by}.", category=QurryUnknownExportOption, ) return actions.get(run_by, lambda w: w.copy())(self[wave])
[docs] def operator(self, wave: Union[list[Hashable], Hashable]) -> Union[list[Operator], Operator]: """Export wave function as `Operator`. Args: wave (Union[list[Hashable], Hashable]): The key of wave in 'dict' `.waves`. Returns: Union[list[Operator], Operator]: The operator of wave function. """ return self.get_wave( wave=wave, run_by="operator", )
[docs] def gate(self, wave: Union[list[Hashable], Hashable]) -> Union[list[Gate], Gate]: """Export wave function as `Gate`. Args: wave (Union[list[Hashable], Hashable]): The key of wave in 'dict' `.waves`. Returns: Union[list[Gate], Gate]: The gate of wave function. """ return self.get_wave( wave=wave, run_by="gate", )
[docs] def copy_circuit( self, wave: Union[list[Hashable], Hashable] ) -> Union[list[QuantumCircuit], QuantumCircuit]: """Export a copy of wave function as :class:`~qiskit.circuit.QuantumCircuit`. Args: wave (Union[list[Hashable], Hashable]): The key of wave in 'dict' `.waves`. Returns: Union[list[QuantumCircuit], QuantumCircuit]: The copy circuit of wave function. """ return self.get_wave( wave=wave, run_by="copy", )
[docs] def instruction( self, wave: Union[list[Hashable], Hashable] ) -> Union[list[Instruction], Instruction]: """Export wave function as `Instruction`. Args: wave (Union[list[Hashable], Hashable]): The key of wave in 'dict' `.waves`. Returns: Union[list[Instruction], Instruction]: The instruction of wave function. """ return self.get_wave( wave=wave, run_by="instruction", )
[docs] def has(self, wavename: Hashable) -> bool: """Is there a wave with specific name. Args: wavename (Hashable): Name of wave which is used in `.waves` Returns: bool: Exist or not. """ return wavename in self
[docs] def wave_container_maker( typename: str = "WaveContainer", base_type: Type[MutableMapping[Hashable, QuantumCircuit]] = dict, ) -> Type[MutableMapping]: """A customized dictionary for storing waves. Args: typename (str, optional): The name of the new type. Defaults to "WaveContainer". base_type (Type[MutableMapping[Hashable, QuantumCircuit]], optional): The base type of the new type. Defaults to dict. Returns: Type[MutableMapping]: The new type of wave container. """ if not isinstance(typename, str): raise TypeError("`typename` should be a string.") if not isinstance(base_type, type): raise TypeError("`base_type` should be a type.") if not isinstance(base_type(), MutableMapping): raise TypeError( "`base_type` should be a dict-like structure or a MutableMapping basically." ) def constructor(self, *args, **kwargs): super(base_type, self).__init__(*args, **kwargs) def __call__( self, wave: Union[list[Hashable], Hashable] ) -> Union[list[QuantumCircuit], QuantumCircuit]: return self.call(wave=wave) def __repr__(self): return f"{type(self).__name__}({super(self).__repr__()})" def _repr_oneline(self): return f"{self.__name__}(" + "{...}" + f", num={len(self)})" def _repr_pretty_(self, p, cycle): if cycle: p.text(f"{type(self).__name__}(" + "{...}" + f", num={len(self)})") else: original_repr = super(self).__repr__() original_repr_split = original_repr[1:-1].split(", ") length = len(original_repr_split) with p.group(2, f"{type(self).__name__}(" + "{", "})"): for i, item in enumerate(original_repr_split): p.breakable() p.text(item) if i < length - 1: p.text(",") def __str__(self): return super(self).__repr__() class_namespace = { "__init__": constructor, "__call__": __call__, "add": add, "remove": remove, "get_wave": get_wave, "call": call, "operator": operator, "gate": gate, "copy_circuit": copy_circuit, "instruction": instruction, "has": has, "process": process, "__repr__": __repr__, "_repr_oneline": _repr_oneline, "_repr_pretty_": _repr_pretty_, "__str__": __str__, } result = type(typename, (base_type,), class_namespace) return result
DyanmicWaveContainerByDict = wave_container_maker("WaveContainer", dict) """ A Qurrium standard wave function container should be something dict-like structure, basically a :class:`~typing.MutableMapping`. """ def _add( _wave_container: MutableMapping[Hashable, QuantumCircuit], wave: QuantumCircuit, key: Optional[Hashable] = None, replace: Literal[True, False, "duplicate"] = True, ) -> Hashable: """Add new wave function to measure. Args: _wave_container (WaveContainer): The container of waves. wave (QuantumCircuit): The wave function or circuit to add. key (Optional[Hashable], optional): Given a specific key to add to the wave function or circuit, if `key == None`, then generate a number as key. Defaults to None. replace (Literal[True, False, "duplicate"], optional): If the key is already in the wave function or circuit, then replace the old wave function or circuit when `True`, or duplicate the wave function or circuit when `'duplicate'`. Defaults to `True`. Returns: Optional[Hashable]: Key of given wave function in `.waves`. """ if not isinstance(wave, QuantumCircuit): raise TypeError(f"waveCircuit should be a QuantumCircuit, not {type(wave)}") if isinstance(key, int): raise ValueError("Number key is only for internal use, not for user.") if key is None: key = len(_wave_container) _wave_container[key] = wave elif isinstance(key, Hashable): if key in _wave_container: if replace is True: pass elif replace == "duplicate": if isinstance(key, tuple): key += (len(_wave_container),) else: key = f"{key}.{len(_wave_container)}" else: key = len(_wave_container) _wave_container[key] = wave else: key = len(_wave_container) warnings.warn(f"'{key}' is '{type(key)}', a unhashable key, skipped being add.") return key def _remove( _wave_container: MutableMapping[Hashable, QuantumCircuit], key: Optional[Hashable] = None, ) -> None: del _wave_container[key] def _process( _wave_container: MutableMapping[Hashable, QuantumCircuit], circuits: list[Union[QuantumCircuit, Hashable]], ) -> list[tuple[Hashable, QuantumCircuit]]: """Process the circuits for Qurrium. Args: _wave_container (WaveContainer): The container of waves. circuits (list[Union[QuantumCircuit, Hashable]]): The circuits. Raises: KeyError: If the wave not found in the container. ValueError: If the circuit is invalid. Returns: dict[Hashable, QuantumCircuit]: The circuits maps. """ circuits_items: list[tuple[Hashable, QuantumCircuit]] = [] for _circuit in circuits: if isinstance(_circuit, QuantumCircuit): key = _add(_wave_container, wave=_circuit) circuits_items.append((key, _wave_container[key])) elif isinstance(_circuit, Hashable): if _circuit in _wave_container: circuits_items.append((_circuit, _wave_container[_circuit])) else: raise KeyError(f"Wave {_circuit} not found in {_wave_container}") else: raise ValueError(f"Invalid type of circuit: {_circuit}, type: {type(_circuit)}") if len(circuits_items) != len(circuits): raise ValueError(f"Lost some circuits: {[k for k, _ in circuits_items]}, {circuits}") return circuits_items