Source code for qblox_scheduler.operations.variables
# Repository: https://gitlab.com/qblox/packages/software/qblox-scheduler
# Licensed according to the LICENSE file on the main branch
#
# Copyright 2025, Qblox B.V.
"""
Variable class and related operations for creating a variable, and dropping a variable
when it goes out of scope.
"""
from __future__ import annotations
import uuid
from typing import Literal
from qblox_scheduler.helpers.importers import export_python_object_to_path_string
from qblox_scheduler.operations.expressions import DType, Expression
[docs]
class Variable(Expression):
"""A variable, representing a location in memory."""
def __init__(self, dtype: DType) -> None:
super().__init__(name="Variable")
self.data["expression_info"] = {"variable": uuid.uuid4(), "dtype": dtype}
if dtype == DType.COMPLEX:
self.__real: Variable = Variable(dtype=DType.AMPLITUDE)
self.__imag: Variable | Literal[0] = Variable(dtype=DType.AMPLITUDE)
else:
self.__real = self
self.__imag = 0
@property
[docs]
def real(self) -> Variable:
"""The real part of this variable."""
return self.__real
@property
[docs]
def imag(self) -> Variable | Literal[0]:
"""The imaginary part of this variable."""
return self.__imag
[docs]
def substitute(
self, substitutions: dict[Expression, Expression | int | float | complex]
) -> Expression | int | float | complex:
"""Substitute matching variable."""
for expr, sub in substitutions.items():
if isinstance(expr, Variable) and self.id_ == expr.id_:
return sub
return self
@property
[docs]
def dtype(self) -> DType:
"""Data type of this variable."""
return self["expression_info"]["dtype"]
[docs]
def _update(self) -> None:
self._dtype = self.data["expression_info"]["dtype"]
if self.dtype == DType.COMPLEX:
cls = self.__class__
self.__real = cls.__new__(cls)
self.__real.__setstate__(self.data["expression_info"]["real"])
self.__imag = cls.__new__(cls)
self.__imag.__setstate__(self.data["expression_info"]["imag"])
else:
self.__real = self
self.__imag = 0
@property
[docs]
def id_(self) -> uuid.UUID:
"""The unique ID of this variable."""
return self.data["expression_info"]["variable"]
def __hash__(self) -> int:
return hash(self.id_)
def __repr__(self) -> str:
return f"Var{self.id_.hex}"
def __contains__(self, item: object) -> bool:
return item in (self.real, self.imag)
def __getstate__(self) -> dict[str, object]:
state = {
"deserialization_type": export_python_object_to_path_string(self.__class__),
"data": {
"name": self.data["name"],
"expression_info": {
"variable": str(self.data["expression_info"]["variable"]),
"dtype": self.dtype,
},
},
}
if self.dtype == DType.COMPLEX:
state["data"]["expression_info"].update(
real=self.real.__getstate__(),
imag=self.imag.__getstate__(), # type: ignore
)
return state
def __setstate__(self, state: dict[str, dict] | Variable) -> None:
if isinstance(state, Variable):
# This is needed because json deserialization does a post-order traversal of nested
# dicts, meaning that for a Variable with DType.COMPLEX, the real and imaginary parts
# will have already been deserialized.
self.data = state.data
return
state["data"]["expression_info"]["variable"] = uuid.UUID(
state["data"]["expression_info"]["variable"]
)
self.data = state["data"]
self._update()