# Copyright (C) 2024 qBraid
#
# This file is part of the qBraid-SDK
#
# The qBraid-SDK is free software released under the GNU General Public License v3
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
#
# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.
"""
Module defining base program type specification
"""
from typing import Any, Callable, Optional, Type
from .experiment import ExperimentType
from .registry import (
derive_program_type_alias,
get_native_experiment_type,
is_registered_alias_native,
register_program_type,
)
[docs]
class ProgramSpec:
"""Base class used to register program type and type alias."""
[docs]
def __init__( # pylint: disable=too-many-arguments
self,
program_type: Type[Any],
alias: Optional[str] = None,
overwrite: bool = False,
to_ir: Optional[Callable[[Any], Any]] = None,
validate: Optional[Callable[[Any], None]] = None,
experiment_type: Optional[ExperimentType] = None,
):
self._program_type = program_type
self._to_ir = to_ir or (lambda program: program)
self._validate = validate or (lambda program: None)
register_program_type(program_type, alias=alias, overwrite=overwrite)
self._alias = alias or derive_program_type_alias(program_type)
self._native = is_registered_alias_native(self._alias)
self._experiment_type: Optional[ExperimentType] = None
self.experiment_type = experiment_type
@property
def program_type(self) -> Type[Any]:
"""Return the registered program type."""
return self._program_type
@property
def alias(self) -> str:
"""Return the alias of the registered program type."""
return self._alias
@property
def native(self) -> bool:
"""True if program is natively supported by qBraid, False otherwise."""
return self._native
@property
def experiment_type(self) -> Optional[ExperimentType]:
"""Getter for experiment type."""
return self._experiment_type
@experiment_type.setter
def experiment_type(self, value: Optional[ExperimentType]):
"""Setter for experiment type with logic for native aliases."""
if value is not None:
self._experiment_type = value
elif self._native:
self._experiment_type = get_native_experiment_type(self._alias)
else:
self._experiment_type = None
def to_ir(self, program: Any) -> Any:
"""
Convert the given program to an intermediate representation (IR) using the to_ir lambda.
Args:
program (Any): The program to convert.
Returns:
Any: The program converted to an IR, or the program itself if to_ir is None.
"""
return self._to_ir(program)
def validate(self, program: Any) -> None:
"""
Validate the given program using the validate lambda.
Args:
program (Any): The program to validate.
Raises:
ValueError: If the program is invalid.
"""
self._validate(program)
def __str__(self) -> str:
return f"ProgramSpec({self._program_type.__name__}, {self.alias})"
def __repr__(self) -> str:
return (
f"<ProgramSpec('{self._program_type.__module__}.{self._program_type.__name__}', "
f"'{self.alias}')>"
)
def __eq__(self, other: object) -> bool:
"""
Compare this ProgramSpec object with another object for equality based on type and alias.
Args:
other (object): Another object to compare against.
Returns:
bool: True if both objects are instances of ProgramSpec and have the
same type and alias, False otherwise.
"""
if not isinstance(other, ProgramSpec):
return False
this_spec = (self._program_type, self._alias, self._native)
other_spec = (other._program_type, other._alias, other._native)
return this_spec == other_spec