Python sdk
analyzer
special
analysis_params
AnalysisOptionalDevicesParams (HardwareListParams)
pydantic-model
Source code in classiq/interface/analyzer/analysis_params.py
class AnalysisOptionalDevicesParams(HardwareListParams):
qubit_count: int = pydantic.Field(
default=...,
description="number of qubits in the data",
ge=MIN_QUBITS,
le=MAX_QUBITS,
)
qubit_count: ConstrainedIntValue
pydantic-field
required
number of qubits in the data
ChemistryGenerationParams (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/analysis_params.py
class ChemistryGenerationParams(pydantic.BaseModel):
class Config:
title = "Chemistry"
molecule: MoleculeProblem = pydantic.Field(
title="Molecule",
default=...,
description="The molecule to generate the VQE ansatz for",
)
optimizer_preferences: OptimizerPreferences = pydantic.Field(
default=..., description="Execution options for the classical Optimizer"
)
def initial_point(self) -> Optional[numpy.ndarray]:
if self.optimizer_preferences.initial_point is not None:
return numpy.ndarray(
self.optimizer_preferences.initial_point # type: ignore[arg-type]
)
else:
return None
molecule: MoleculeProblem
pydantic-field
required
The molecule to generate the VQE ansatz for
optimizer_preferences: OptimizerPreferences
pydantic-field
required
Execution options for the classical Optimizer
HardwareListParams (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/analysis_params.py
class HardwareListParams(pydantic.BaseModel):
devices: Optional[List[PydanticNonEmptyString]] = pydantic.Field(
default=None, description="Devices"
)
providers: List[Provider]
from_ide: bool = Field(default=False)
@pydantic.validator("providers", always=True)
def set_default_providers(
cls, providers: Optional[List[AnalyzerProviderVendor]]
) -> List[AnalyzerProviderVendor]:
if providers is None:
providers = list(AnalyzerProviderVendor)
return providers
devices: List[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue]
pydantic-field
Devices
HardwareParams (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/analysis_params.py
class HardwareParams(pydantic.BaseModel):
device: PydanticNonEmptyString = pydantic.Field(default=None, description="Devices")
provider: AnalyzerProviderVendor
device: ConstrainedStrValue
pydantic-field
Devices
LatexParams (AnalysisParams)
pydantic-model
Source code in classiq/interface/analyzer/analysis_params.py
class LatexParams(AnalysisParams):
gate_names: List[GateNamsMapping] = pydantic.Field(
default=..., description="List of gate names as apper in the qasm"
)
gate_names: List[classiq.interface.analyzer.analysis_params.GateNamsMapping]
pydantic-field
required
List of gate names as apper in the qasm
cytoscape_graph
CytoScapeEdge (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/cytoscape_graph.py
class CytoScapeEdge(pydantic.BaseModel):
data: CytoScapeEdgeData = pydantic.Field(
default=..., description="Edge's Data, mainly the source and target of the Edge"
)
data: CytoScapeEdgeData
pydantic-field
required
Edge's Data, mainly the source and target of the Edge
CytoScapeEdgeData (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/cytoscape_graph.py
class CytoScapeEdgeData(pydantic.BaseModel):
source: str = pydantic.Field(
default=..., description="the Id of the Node that is the Source of the edge"
)
target: str = pydantic.Field(
default=..., description="the Id of the Node that is the Target the edge"
)
source: str
pydantic-field
required
the Id of the Node that is the Source of the edge
target: str
pydantic-field
required
the Id of the Node that is the Target the edge
CytoScapeGraph (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/cytoscape_graph.py
class CytoScapeGraph(pydantic.BaseModel):
nodes: List[CytoScapeNode] = pydantic.Field(
default_factory=list,
description="Nodes of the Graph",
)
edges: List[CytoScapeEdge] = pydantic.Field(
default_factory=list,
description="Edges of the Graph",
)
edges: List[classiq.interface.analyzer.cytoscape_graph.CytoScapeEdge]
pydantic-field
Edges of the Graph
nodes: List[classiq.interface.analyzer.cytoscape_graph.CytoScapeNode]
pydantic-field
Nodes of the Graph
CytoScapeNode (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/cytoscape_graph.py
class CytoScapeNode(pydantic.BaseModel):
data: Dict[str, Any] = pydantic.Field(
default=...,
description="Data of the Node, such as label, and color, can be of free form",
)
position: Optional[CytoScapePosition] = pydantic.Field(
default=..., description="Position of the Node to be rendered in Cytocape"
)
data: Dict[str, Any]
pydantic-field
required
Data of the Node, such as label, and color, can be of free form
position: CytoScapePosition
pydantic-field
required
Position of the Node to be rendered in Cytocape
CytoScapePosition (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/cytoscape_graph.py
class CytoScapePosition(pydantic.BaseModel):
x: int = pydantic.Field(
default=..., description="X coordinate in the Cytoscape View"
)
y: int = pydantic.Field(
default=..., description="Y coordinate in the Cytoscape View"
)
x: int
pydantic-field
required
X coordinate in the Cytoscape View
y: int
pydantic-field
required
Y coordinate in the Cytoscape View
HardwareConnectivityGraphResult (VersionedModel)
pydantic-model
Source code in classiq/interface/analyzer/cytoscape_graph.py
class HardwareConnectivityGraphResult(VersionedModel):
graph: Optional[CytoScapeGraph] = pydantic.Field(
default=...,
description="The Cytoscape graph in the desired Structure for the FE",
)
error: ConnectivityErrors = pydantic.Field(
default=ConnectivityErrors.EMPTY,
description="Any errors encountered while generating the graph",
)
error: ConnectivityErrors
pydantic-field
Any errors encountered while generating the graph
graph: CytoScapeGraph
pydantic-field
required
The Cytoscape graph in the desired Structure for the FE
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
result
Analysis (VersionedModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class Analysis(VersionedModel):
input_properties: QuantumCircuitProperties = pydantic.Field(
default=..., description="Input circuit properties"
)
native_properties: NativeQuantumCircuitProperties = pydantic.Field(
default=..., description="Transpiled circuit properties"
)
input_properties: QuantumCircuitProperties
pydantic-field
required
Input circuit properties
native_properties: NativeQuantumCircuitProperties
pydantic-field
required
Transpiled circuit properties
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
AvailableHardware (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class AvailableHardware(pydantic.BaseModel):
ibm_quantum: Optional[Dict[PydanticNonEmptyString, bool]] = pydantic.Field(
default=None,
description="available IBM Quantum devices with boolean indicates if a given device has enough qubits.",
)
azure_quantum: Optional[Dict[PydanticNonEmptyString, bool]] = pydantic.Field(
default=None,
description="available Azure Quantum devices with boolean indicates if a given device has enough qubits.",
)
amazon_braket: Optional[Dict[PydanticNonEmptyString, bool]] = pydantic.Field(
default=None,
description="available Amazon Braket devices with boolean indicates if a given device has enough qubits.",
)
amazon_braket: Dict[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, bool]
pydantic-field
available Amazon Braket devices with boolean indicates if a given device has enough qubits.
azure_quantum: Dict[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, bool]
pydantic-field
available Azure Quantum devices with boolean indicates if a given device has enough qubits.
ibm_quantum: Dict[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, bool]
pydantic-field
available IBM Quantum devices with boolean indicates if a given device has enough qubits.
DevicesResult (VersionedModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class DevicesResult(VersionedModel):
devices: AvailableHardware
status: GraphStatus
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
GraphResult (VersionedModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class GraphResult(VersionedModel):
kind: Literal["graph"] = Field(default="graph")
details: str
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
HardwareComparisonData (VersionedModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class HardwareComparisonData(VersionedModel):
kind: Literal["hardware_comparison"] = Field(default="hardware_comparison")
data: List[SingleHardwareInformation]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
HardwareComparisonInformation (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class HardwareComparisonInformation(pydantic.BaseModel):
devices: List[str] = pydantic.Field(
default=..., description="Device which is used for the transpilation."
)
providers: List[str] = pydantic.Field(
default=..., description="Provider cloud of the device."
)
depth: List[pydantic.NonNegativeInt] = pydantic.Field(
default=..., description="Circuit depth."
)
multi_qubit_gate_count: List[pydantic.NonNegativeInt] = pydantic.Field(
default=..., description="Number of multi qubit gates."
)
total_gate_count: List[pydantic.NonNegativeInt] = pydantic.Field(
default=..., description="Number of total gates."
)
@pydantic.root_validator
def validate_equal_length(cls, values: Dict[str, list]) -> Dict[str, list]:
lengths = list(map(len, values.values()))
if len(set(lengths)) != 1:
raise ClassiqValueError("All lists should have the same length")
return values
depth: List[pydantic.types.NonNegativeInt]
pydantic-field
required
Circuit depth.
devices: List[str]
pydantic-field
required
Device which is used for the transpilation.
multi_qubit_gate_count: List[pydantic.types.NonNegativeInt]
pydantic-field
required
Number of multi qubit gates.
providers: List[str]
pydantic-field
required
Provider cloud of the device.
total_gate_count: List[pydantic.types.NonNegativeInt]
pydantic-field
required
Number of total gates.
NativeQuantumCircuitProperties (QuantumCircuitProperties)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class NativeQuantumCircuitProperties(QuantumCircuitProperties):
native_gates: Set[BasisGates] = pydantic.Field(
default=..., description="Native gates used for decomposition"
)
native_gates: Set[classiq.interface.analyzer.result.BasisGates]
pydantic-field
required
Native gates used for decomposition
QuantumCircuitProperties (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class QuantumCircuitProperties(pydantic.BaseModel):
depth: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Circuit depth"
)
auxiliary_qubits: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Number of Auxiliary qubits"
)
classical_bits: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Number of classical bits"
)
gates_count: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Total number of gates in the circuit"
)
multi_qubit_gates_count: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Number of multi-qubit gates in circuit"
)
non_entangled_subcircuits_count: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Number of non-entangled sub-circuit "
)
auxiliary_qubits: NonNegativeInt
pydantic-field
required
Number of Auxiliary qubits
classical_bits: NonNegativeInt
pydantic-field
required
Number of classical bits
depth: NonNegativeInt
pydantic-field
required
Circuit depth
gates_count: NonNegativeInt
pydantic-field
required
Total number of gates in the circuit
multi_qubit_gates_count: NonNegativeInt
pydantic-field
required
Number of multi-qubit gates in circuit
non_entangled_subcircuits_count: NonNegativeInt
pydantic-field
required
Number of non-entangled sub-circuit
RbResults (VersionedModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class RbResults(VersionedModel):
mean_fidelity: float
average_error: float
A: float
B: float
success_probability: List[float]
parameters_error: Tuple[float, ...]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
SingleHardwareInformation (BaseModel)
pydantic-model
Source code in classiq/interface/analyzer/result.py
class SingleHardwareInformation(pydantic.BaseModel):
devices: str = pydantic.Field(
default=..., description="Device which is used for the transpilation."
)
providers: str = pydantic.Field(
default=..., description="Provider cloud of the device."
)
depth: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Circuit depth."
)
multi_qubit_gate_count: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Number of multi qubit gates."
)
total_gate_count: pydantic.NonNegativeInt = pydantic.Field(
default=..., description="Number of total gates."
)
depth: NonNegativeInt
pydantic-field
required
Circuit depth.
devices: str
pydantic-field
required
Device which is used for the transpilation.
multi_qubit_gate_count: NonNegativeInt
pydantic-field
required
Number of multi qubit gates.
providers: str
pydantic-field
required
Provider cloud of the device.
total_gate_count: NonNegativeInt
pydantic-field
required
Number of total gates.
applications
special
qsvm
QSVMData (VersionedModel)
pydantic-model
Source code in classiq/interface/applications/qsvm.py
class QSVMData(VersionedModel):
data: DataList
labels: Optional[LabelsInt] = None
internal_state: Optional[QSVMInternalState] = None
class Config:
smart_union = True
extra = "forbid"
@pydantic.validator("data", pre=True)
def set_data(cls, data: Union[IterableType, ArrayLike]) -> list:
return listify(data)
@pydantic.validator("labels", pre=True)
def set_labels(
cls, labels: Optional[Union[IterableType, ArrayLike]]
) -> Optional[list]:
if labels is None:
return None
else:
return listify(labels)
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
QSVMInternalState (VersionedModel)
pydantic-model
Source code in classiq/interface/applications/qsvm.py
class QSVMInternalState(VersionedModel):
underscore_sparse: bool
class_weight: list
classes: list
underscore_gamma: float
underscore_base_fit: list
support: list
support_vectors: list
underscore_n_support: list
dual_coef_2: list
intercept: list
underscore_p_a: list
underscore_p_b: list
fit_status: int
shape_fit: Shape
underscore_intercept: list
dual_coef: list
class_weight__shape: Shape
classes__shape: Shape
underscore_base_fit__shape: Shape
support__shape: Shape
support_vectors__shape: Shape
underscore_n_support__shape: Shape
dual_coef_2__shape: Shape
intercept__shape: Shape
underscore_p_a__shape: Shape
underscore_p_b__shape: Shape
underscore_intercept__shape: Shape
dual_coef__shape: Shape
set_class_weight = validate_array_to_list("class_weight")
set_classes = validate_array_to_list("classes")
set_underscore_base_fit = validate_array_to_list("underscore_base_fit")
set_support = validate_array_to_list("support")
set_support_vectors = validate_array_to_list("support_vectors")
set_underscore_n_support = validate_array_to_list("underscore_n_support")
set_dual_coef_2 = validate_array_to_list("dual_coef_2")
set_intercept = validate_array_to_list("intercept")
set_underscore_p_a = validate_array_to_list("underscore_p_a")
set_underscore_p_b = validate_array_to_list("underscore_p_b")
set_underscore_intercept = validate_array_to_list("underscore_intercept")
set_dual_coef = validate_array_to_list("dual_coef")
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
QSVMPredictResult (VersionedModel)
pydantic-model
Source code in classiq/interface/applications/qsvm.py
class QSVMPredictResult(VersionedModel):
data: list # serialized np.array
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
QSVMTestResult (VersionedModel)
pydantic-model
Source code in classiq/interface/applications/qsvm.py
class QSVMTestResult(VersionedModel):
data: float # between 0 to 1
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
ast_node
SourceReference (HashablePydanticBaseModel)
pydantic-model
Source code in classiq/interface/ast_node.py
class SourceReference(HashablePydanticBaseModel):
start_line: int
start_column: int
end_line: int
end_column: int
file_name: Optional[str] = pydantic.Field(default=None)
def __str__(self) -> str:
file_string = (
f"file {os.path.basename(self.file_name)} " if self.file_name else ""
)
start_character_string = (
f" character {self.start_column + 1}" if self.start_column > 0 else ""
)
return f"{file_string}line {self.start_line + 1}{start_character_string}"
__str__(self)
special
Return str(self).
Source code in classiq/interface/ast_node.py
def __str__(self) -> str:
file_string = (
f"file {os.path.basename(self.file_name)} " if self.file_name else ""
)
start_character_string = (
f" character {self.start_column + 1}" if self.start_column > 0 else ""
)
return f"{file_string}line {self.start_line + 1}{start_character_string}"
backend
special
backend_preferences
AliceBobBackendPreferences (BackendPreferences)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class AliceBobBackendPreferences(BackendPreferences):
backend_service_provider: ProviderTypeVendor.ALICE_BOB
distance: Optional[int] = pydantic.Field(
default=None, description="Repetition code distance"
)
kappa_1: Optional[float] = pydantic.Field(
default=None, description="One-photon dissipation rate (Hz)"
)
kappa_2: Optional[float] = pydantic.Field(
default=None, description="Two-photon dissipation rate (Hz)"
)
average_nb_photons: Optional[float] = pydantic.Field(
default=None, description="Average number of photons"
)
api_key: pydantic_backend.PydanticAliceBobApiKeyType = pydantic.Field(
..., description="AliceBob API key"
)
_parameters: Dict[str, Any] = PrivateAttr(default_factory=dict)
@pydantic.root_validator(pre=True)
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(
values, "backend_service_provider", ProviderVendor.ALICE_AND_BOB
)
@property
def parameters(self) -> Dict[str, Any]:
self._parameters = {
"distance": self.distance,
"kappa_1": self.kappa_1,
"kappa_2": self.kappa_2,
"average_nb_photons": self.average_nb_photons,
}
self._parameters = {k: v for k, v in self._parameters.items() if v is not None}
return self._parameters
api_key: ConstrainedStrValue
pydantic-field
required
AliceBob API key
average_nb_photons: float
pydantic-field
Average number of photons
distance: int
pydantic-field
Repetition code distance
kappa_1: float
pydantic-field
One-photon dissipation rate (Hz)
kappa_2: float
pydantic-field
Two-photon dissipation rate (Hz)
AwsBackendPreferences (BackendPreferences)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class AwsBackendPreferences(BackendPreferences):
backend_service_provider: ProviderTypeVendor.AMAZON_BRAKET
aws_role_arn: pydantic_backend.PydanticAwsRoleArn = pydantic.Field(
description="ARN of the role to be assumed for execution on your Braket account."
)
s3_bucket_name: str = pydantic.Field(description="S3 Bucket Name")
s3_folder: pydantic_backend.PydanticS3BucketKey = pydantic.Field(
description="S3 Folder Path Within The S3 Bucket"
)
job_timeout: pydantic_backend.PydanticExecutionTimeout = pydantic.Field(
description="Timeout for Jobs sent for execution in seconds.",
default=AWS_DEFAULT_JOB_TIMEOUT_SECONDS,
)
@validator("s3_bucket_name")
def _validate_s3_bucket_name(
cls, s3_bucket_name: str, values: Dict[str, Any]
) -> str:
s3_bucket_name = s3_bucket_name.strip()
if not s3_bucket_name.startswith("amazon-braket-"):
raise ClassiqValueError('S3 bucket name should start with "amazon-braket-"')
return s3_bucket_name
@pydantic.root_validator(pre=True)
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(
values, "backend_service_provider", ProviderVendor.AMAZON_BRAKET
)
aws_role_arn: ConstrainedStrValue
pydantic-field
required
ARN of the role to be assumed for execution on your Braket account.
job_timeout: ConstrainedIntValue
pydantic-field
Timeout for Jobs sent for execution in seconds.
s3_bucket_name: str
pydantic-field
required
S3 Bucket Name
s3_folder: ConstrainedStrValue
pydantic-field
required
S3 Folder Path Within The S3 Bucket
AzureBackendPreferences (BackendPreferences)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class AzureBackendPreferences(BackendPreferences):
backend_service_provider: ProviderTypeVendor.AZURE_QUANTUM
location: str = pydantic.Field(
default="East US", description="Azure personal resource region"
)
credentials: Optional[AzureCredential] = pydantic.Field(
default=None,
description="The service principal credential to access personal quantum workspace",
)
ionq_error_mitigation_flag: Optional[bool] = pydantic.Field(
default=False,
description="Error mitigation configuration upon running on IonQ through Azure.",
)
@property
def run_through_classiq(self) -> bool:
return self.credentials is None
@pydantic.root_validator(pre=True)
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(
values, "backend_service_provider", ProviderVendor.AZURE_QUANTUM
)
credentials: AzureCredential
pydantic-field
The service principal credential to access personal quantum workspace
ionq_error_mitigation_flag: bool
pydantic-field
Error mitigation configuration upon running on IonQ through Azure.
location: str
pydantic-field
Azure personal resource region
AzureCredential (BaseSettings)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class AzureCredential(pydantic.BaseSettings):
tenant_id: str = pydantic.Field(description="Azure Tenant ID")
client_id: str = pydantic.Field(description="Azure Client ID")
client_secret: str = pydantic.Field(description="Azure Client Secret")
resource_id: pydantic_backend.PydanticAzureResourceIDType = pydantic.Field(
description="Azure Resource ID (including Azure subscription ID, resource "
"group and workspace), for personal resource",
)
class Config:
title = "Azure Service Principal Credential"
env_prefix = "AZURE_"
case_sensitive = False
client_id: str
pydantic-field
required
Azure Client ID
client_secret: str
pydantic-field
required
Azure Client Secret
resource_id: ConstrainedStrValue
pydantic-field
required
Azure Resource ID (including Azure subscription ID, resource group and workspace), for personal resource
tenant_id: str
pydantic-field
required
Azure Tenant ID
BackendPreferences (BaseModel)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class BackendPreferences(BaseModel):
# Due to the way the field is currently implemented, i.e. it redefined with different types
# in the subclass, it shouldn't be dumped with exclude_unset. This causes this field not to appear.
# For example: don't use obj.dict(exclude_unset=True).
backend_service_provider: str = pydantic.Field(
..., description="Provider company or cloud for the requested backend."
)
backend_name: str = pydantic.Field(
..., description="Name of the requested backend or target."
)
@property
def hw_provider(self) -> Provider:
return Provider(self.backend_service_provider)
@pydantic.validator("backend_service_provider", pre=True)
def validate_backend_service_provider(
cls, backend_service_provider: Any
) -> Provider:
return validate_backend_service_provider(backend_service_provider)
@classmethod
def batch_preferences(
cls, *, backend_names: Iterable[str], **kwargs: Any
) -> List[BackendPreferences]:
return [cls(backend_name=name, **kwargs) for name in backend_names]
def is_nvidia_backend(self) -> bool:
return False
backend_name: str
pydantic-field
required
Name of the requested backend or target.
backend_service_provider: str
pydantic-field
required
Provider company or cloud for the requested backend.
IBMBackendPreferences (BackendPreferences)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class IBMBackendPreferences(BackendPreferences):
backend_service_provider: ProviderTypeVendor.IBM_QUANTUM
access_token: Optional[str] = pydantic.Field(
default=None,
description="IBM Quantum access token to be used"
" with IBM Quantum hosted backends",
)
provider: IBMBackendProvider = pydantic.Field(
default_factory=IBMBackendProvider,
description="Provider specs. for identifying a single IBM Quantum provider.",
)
@pydantic.root_validator(pre=True)
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(
values, "backend_service_provider", ProviderVendor.IBM_QUANTUM
)
access_token: str
pydantic-field
IBM Quantum access token to be used with IBM Quantum hosted backends
provider: IBMBackendProvider
pydantic-field
Provider specs. for identifying a single IBM Quantum provider.
IonqBackendPreferences (BackendPreferences)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class IonqBackendPreferences(BackendPreferences):
backend_service_provider: ProviderTypeVendor.IONQ
api_key: pydantic_backend.PydanticIonQApiKeyType = pydantic.Field(
..., description="IonQ API key"
)
@pydantic.root_validator(pre=True)
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(
values, "backend_service_provider", ProviderVendor.IONQ
)
api_key: ConstrainedStrValue
pydantic-field
required
IonQ API key
OQCBackendPreferences (BackendPreferences)
pydantic-model
Source code in classiq/interface/backend/backend_preferences.py
class OQCBackendPreferences(BackendPreferences):
backend_service_provider: ProviderTypeVendor.OQC
username: str = pydantic.Field(description="OQC username")
password: str = pydantic.Field(description="OQC password")
@pydantic.root_validator(pre=True)
def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(
values, "backend_service_provider", ProviderVendor.OQC
)
password: str
pydantic-field
required
OQC password
username: str
pydantic-field
required
OQC username
chemistry
special
fermionic_operator
FermionicOperator (HashablePydanticBaseModel)
pydantic-model
Specification of a Fermionic operator. Input: List of ladder operators, each ladder operator is described by a tuple of its index and a character indicating if it's a creation ('+') or annihilation operator ('-').
Source code in classiq/interface/chemistry/fermionic_operator.py
class FermionicOperator(HashablePydanticBaseModel):
"""
Specification of a Fermionic operator.
Input:
List of ladder operators, each ladder operator is described by a tuple of its
index and a character indicating if it's a creation ('+') or annihilation operator ('-').
"""
op_list: list = pydantic.Field(
description=(
"A list of tuples each containing an index and a character; for "
"example [('+', 0), ('-', 1)]."
),
)
@staticmethod
def _validate_single_op(op: tuple) -> LadderOperator:
if not isinstance(op, tuple):
try: # type: ignore[unreachable] # it is reachable...
op = tuple(op)
except Exception as exc:
raise ClassiqValueError("Ladder operator should be a tuple.") from exc
if len(op) != 2:
raise ClassiqValueError(
"Ladder operator tuple should be of length two; for example ('+', 1)."
)
op_symbol = op[0]
if op_symbol == "LadderOperator.PLUS":
op_symbol = "+"
elif op_symbol == "LadderOperator.MINUS":
op_symbol = "-"
if op_symbol not in ("+", "-"):
raise ClassiqValueError(
f"The first term in a ladder operator tuple indicates if its a raising "
f"(LadderOperator.PLUS / '+') or lowering (LadderOperator.MINUS / '-') "
f"operator. Received {op_symbol}"
)
op_index = op[1]
if not isinstance(op_index, int):
raise ClassiqValueError(
"The second term in a ladder operator tuple indicates its index and should be of type int"
)
return (op_symbol, op_index)
@pydantic.validator("op_list")
def _validate_op_list(cls, op_list: list) -> list:
return list(map(cls._validate_single_op, op_list))
def __mul__(self, coeff: Union[float, int]) -> SummedFermionicOperator:
if isinstance(coeff, (float, int)):
return SummedFermionicOperator(op_list=[(self, float(coeff))])
raise ClassiqValueError(
"The coefficient multiplying Fermionic Operator should be of type float"
)
__rmul__ = __mul__
def __add__(
self, other: Union[SummedFermionicOperator, FermionicOperator]
) -> SummedFermionicOperator:
if isinstance(other, SummedFermionicOperator):
return SummedFermionicOperator(op_list=[(self, 1.0)] + other.op_list)
elif isinstance(other, FermionicOperator):
return SummedFermionicOperator(op_list=[(self, 1.0)] + [(other, 1.0)])
raise ClassiqValueError(
"FermionicOperator can be summed together only with type FermionicOperator or SummedFermionicOperator"
)
class Config:
frozen = True
@staticmethod
def _to_ladder_op(char: str) -> str:
return "a" + _SUPERSCRIPT_PLUS if char == "+" else "a"
@staticmethod
def _to_subscript(num: int) -> str:
return "".join(_SUBSCRIPT_UNICODE_CHARS[digit] for digit in str(num))
def __str__(self) -> str:
return "".join(
f"{self._to_ladder_op(char)}{self._to_subscript(index)}"
for (char, index) in self.op_list
)
@property
def all_indices(self) -> Set[int]:
return {op[1] for op in self.op_list}
op_list: list
pydantic-field
required
A list of tuples each containing an index and a character; for example [('+', 0), ('-', 1)].
__str__(self)
special
Return str(self).
Source code in classiq/interface/chemistry/fermionic_operator.py
def __str__(self) -> str:
return "".join(
f"{self._to_ladder_op(char)}{self._to_subscript(index)}"
for (char, index) in self.op_list
)
SummedFermionicOperator (HashablePydanticBaseModel)
pydantic-model
Specification of a summed Fermionic operator. Input: List of fermionic operators tuples, The first term in the tuple is the FermionicOperator and the second term is its coefficient. For example: op1 = FermionicOperator(op_list=[('+', 0), ('-', 1)]) op2 = FermionicOperator(op_list=[('-', 0), ('-', 1)]) summed_operator = SummedFermionicOperator(op_list=[(op1, 0.2), (op2, 6.7)])
Source code in classiq/interface/chemistry/fermionic_operator.py
class SummedFermionicOperator(HashablePydanticBaseModel):
"""
Specification of a summed Fermionic operator.
Input:
List of fermionic operators tuples, The first term in the tuple is the FermionicOperator and the second term is its coefficient.
For example:
op1 = FermionicOperator(op_list=[('+', 0), ('-', 1)])
op2 = FermionicOperator(op_list=[('-', 0), ('-', 1)])
summed_operator = SummedFermionicOperator(op_list=[(op1, 0.2), (op2, 6.7)])
"""
op_list: list = pydantic.Field(
description="A list of tuples each containing a FermionicOperator and a coefficient.",
)
class Config:
frozen = True
@staticmethod
def _validate_single_op(op: tuple) -> FermionicOperatorTuple:
# is it tuple - if not, convert to tuple
if not isinstance(op, tuple):
try: # type: ignore[unreachable] # it is reachable...
op = tuple(op)
except Exception as exc:
raise ClassiqValueError("Operator should be a tuple.") from exc
if len(op) != 2:
raise ClassiqValueError("Operator tuple should be of length two.")
# is it FermionicOperator - if not, convert to FermionicOperator
if not isinstance(op[0], FermionicOperator):
try:
op = (FermionicOperator(**op[0]), op[1])
except Exception as exc:
raise ClassiqValueError(
"The first term in the operator tuple should be an instance of the FermionicOperator class"
) from exc
if not isinstance(op[1], float):
raise ClassiqValueError(
"The second term in the operator tuple indicates its coefficient and should be of type float"
)
return op # type: ignore[return-value] # mypy thinks that it is `Tuple[Any, ...]`, though the asserts here tell otherwise..
@pydantic.validator("op_list")
def _validate_op_list(cls, op_list: list) -> list:
return list(map(cls._validate_single_op, op_list))
def __add__(
self, other: Union[SummedFermionicOperator, FermionicOperator]
) -> SummedFermionicOperator:
if isinstance(other, SummedFermionicOperator):
return SummedFermionicOperator(op_list=self.op_list + other.op_list)
elif isinstance(other, FermionicOperator):
return SummedFermionicOperator(op_list=self.op_list + [(other, 1.0)])
raise ClassiqValueError(
"FermionicOperator can be summed together only with type FermionicOperator or SummedFermionicOperator"
)
def is_close(self, other: SummedFermionicOperator) -> bool:
if not isinstance(other, SummedFermionicOperator):
return False # type: ignore[unreachable]
if len(self.op_list) != len(other.op_list):
return False
for (op1, coeff1), (op2, coeff2) in zip(self.op_list, other.op_list):
if op1 != op2 or not np.isclose(coeff1, coeff2):
return False
return True
@property
def _all_indices(self) -> Set[int]:
return set(
itertools.chain.from_iterable(op.all_indices for op, _ in self.op_list)
)
@property
def num_qubits(self) -> int:
return len(self._all_indices)
def __str__(self) -> str:
return " + \n".join(str(op[1]) + " * " + str(op[0]) for op in self.op_list)
op_list: list
pydantic-field
required
A list of tuples each containing a FermionicOperator and a coefficient.
__str__(self)
special
Return str(self).
Source code in classiq/interface/chemistry/fermionic_operator.py
def __str__(self) -> str:
return " + \n".join(str(op[1]) + " * " + str(op[0]) for op in self.op_list)
ground_state_problem
GroundStateProblem (HashablePydanticBaseModel)
pydantic-model
Source code in classiq/interface/chemistry/ground_state_problem.py
class GroundStateProblem(HashablePydanticBaseModel):
kind: str
mapping: FermionMapping = pydantic.Field(
default=FermionMapping.JORDAN_WIGNER,
description="Fermionic mapping type",
title="Fermion Mapping",
)
z2_symmetries: bool = pydantic.Field(
default=False,
description="whether to perform z2 symmetries reduction",
)
num_qubits: Optional[int] = pydantic.Field(default=None)
@pydantic.validator("z2_symmetries")
def _validate_z2_symmetries(
cls, z2_symmetries: bool, values: Dict[str, Any]
) -> bool:
if z2_symmetries and values.get("mapping") == FermionMapping.FAST_BRAVYI_KITAEV:
raise ClassiqValueError(
"z2 symmetries reduction can not be used for fast_bravyi_kitaev mapping"
)
return z2_symmetries
class Config:
frozen = True
mapping: FermionMapping
pydantic-field
Fermionic mapping type
z2_symmetries: bool
pydantic-field
whether to perform z2 symmetries reduction
HamiltonianProblem (GroundStateProblem)
pydantic-model
Source code in classiq/interface/chemistry/ground_state_problem.py
class HamiltonianProblem(GroundStateProblem):
kind: Literal["hamiltonian"] = pydantic.Field(default="hamiltonian")
hamiltonian: SummedFermionicOperator = pydantic.Field(
description="Hamiltonian as a fermionic operator"
)
num_particles: List[pydantic.PositiveInt] = pydantic.Field(
description="Tuple containing the numbers of alpha particles and beta particles"
)
@pydantic.validator("num_particles")
def _validate_num_particles(cls, num_particles: List[int]) -> List[int]:
assert isinstance(num_particles, list)
assert len(num_particles) == 2
# This probably will never happen, since pydantic automatically converts
# floats to ints
assert isinstance(num_particles[0], int)
assert num_particles[0] >= 1
assert isinstance(num_particles[1], int)
assert num_particles[1] >= 1
return num_particles
hamiltonian: SummedFermionicOperator
pydantic-field
required
Hamiltonian as a fermionic operator
num_particles: List[pydantic.types.PositiveInt]
pydantic-field
required
Tuple containing the numbers of alpha particles and beta particles
MoleculeProblem (GroundStateProblem)
pydantic-model
Source code in classiq/interface/chemistry/ground_state_problem.py
class MoleculeProblem(GroundStateProblem):
kind: Literal["molecule"] = pydantic.Field(default="molecule")
molecule: Molecule
basis: str = pydantic.Field(default="sto3g", description="Molecular basis set")
freeze_core: bool = pydantic.Field(default=False)
remove_orbitals: List[int] = pydantic.Field(
default_factory=list, description="list of orbitals to remove"
)
basis: str
pydantic-field
Molecular basis set
remove_orbitals: List[int]
pydantic-field
list of orbitals to remove
molecule
Atom (HashablePydanticBaseModel)
pydantic-model
Source code in classiq/interface/chemistry/molecule.py
class Atom(HashablePydanticBaseModel):
symbol: Literal[tuple(ELEMENTS)] = pydantic.Field(description="The atom symbol") # type: ignore[valid-type]
x: float = pydantic.Field(description="The x coordinate of the atom")
y: float = pydantic.Field(description="The y coordinate of the atom")
z: float = pydantic.Field(description="The z coordinate of the atom")
symbol: Literal['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og']
pydantic-field
required
The atom symbol
x: float
pydantic-field
required
The x coordinate of the atom
y: float
pydantic-field
required
The y coordinate of the atom
z: float
pydantic-field
required
The z coordinate of the atom
Molecule (HashablePydanticBaseModel)
pydantic-model
Source code in classiq/interface/chemistry/molecule.py
class Molecule(HashablePydanticBaseModel):
atoms: List[Atom] = pydantic.Field(
description="A list of atoms each containing the atoms symbol and its (x,y,z) location",
min_items=1,
)
spin: pydantic.NonNegativeInt = pydantic.Field(
default=1, description="spin of the molecule"
)
charge: pydantic.NonNegativeInt = pydantic.Field(
default=0, description="charge of the molecule"
)
@property
def atoms_type(self) -> List[AtomType]:
return [(atom.symbol, [atom.x, atom.y, atom.z]) for atom in self.atoms]
@pydantic.validator("atoms", each_item=True, pre=True)
def _validate_atoms(cls, atom: Union[AtomType, Atom]) -> Atom:
if isinstance(atom, (list, tuple)):
return cls._validate_old_atoms_type(atom)
return atom
@staticmethod
def _validate_old_atoms_type(atom: AtomType) -> Atom:
if len(atom) != 2:
raise ClassiqValueError(
"each atom should be a list of two entries: 1) name pf the elemnt (str) 2) list of its (x,y,z) location"
)
if not isinstance(atom[0], str):
raise ClassiqValueError(
f"atom name should be a string. unknown element: {atom[0]}."
)
if len(atom[1]) != 3:
raise ClassiqValueError(
f"location of the atom is of length three, representing the (x,y,z) coordinates of the atom, error value: {atom[1]}"
)
for idx in atom[1]:
if not isinstance(idx, (float, int)):
raise ClassiqValueError(
f"coordinates of the atom should be of type float. error value: {idx}"
)
symbol, coordinate = atom
return Atom(symbol=symbol, x=coordinate[0], y=coordinate[1], z=coordinate[2])
class Config:
frozen = True
atoms: ConstrainedListValue
pydantic-field
required
A list of atoms each containing the atoms symbol and its (x,y,z) location
charge: NonNegativeInt
pydantic-field
charge of the molecule
spin: NonNegativeInt
pydantic-field
spin of the molecule
operator
PauliOperator (HashablePydanticBaseModel, VersionedModel)
pydantic-model
Specification of a Pauli sum operator.
Source code in classiq/interface/chemistry/operator.py
class PauliOperator(HashablePydanticBaseModel, VersionedModel):
"""
Specification of a Pauli sum operator.
"""
pauli_list: PydanticPauliList = pydantic.Field(
description="A list of tuples each containing a pauli string comprised of I,X,Y,Z characters and a complex coefficient; for example [('IZ', 0.1), ('XY', 0.2)].",
)
is_hermitian: bool = pydantic.Field(default=False)
has_complex_coefficients: bool = pydantic.Field(default=True)
def show(self) -> str:
if self.is_hermitian:
# If the operator is hermitian then the coefficients must be numeric
return "\n".join(
f"{summand[1].real:+.3f} * {summand[0]}" for summand in self.pauli_list # type: ignore[union-attr]
)
return "\n".join(
f"+({summand[1]:+.3f}) * {summand[0]}" for summand in self.pauli_list
)
@pydantic.validator("pauli_list", each_item=True, pre=True)
def _validate_pauli_monomials(
cls, monomial: Tuple[PydanticPauliMonomialStr, ParameterComplexType]
) -> Tuple[PydanticPauliMonomialStr, ParameterComplexType]:
_PauliMonomialLengthValidator( # type: ignore[call-arg]
monomial=monomial
) # Validate the length of the monomial.
coeff = cls._validate_monomial_coefficient(monomial[1])
parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff) # type: ignore[call-arg]
return (parsed_monomial.string, parsed_monomial.coeff)
@staticmethod
def _validate_monomial_coefficient(
coeff: Union[sympy.Expr, ParameterComplexType]
) -> ParameterComplexType:
if isinstance(coeff, str):
validate_expression_str(coeff)
elif isinstance(coeff, sympy.Expr):
coeff = str(coeff)
return coeff
@pydantic.validator("pauli_list")
def _validate_pauli_list(cls, pauli_list: PydanticPauliList) -> PydanticPauliList:
if not all_equal(len(summand[0]) for summand in pauli_list):
raise ClassiqValueError("Pauli strings have incompatible lengths.")
return pauli_list
@pydantic.root_validator
def _validate_hermitianity(cls, values: Dict[str, Any]) -> Dict[str, Any]:
pauli_list = values.get("pauli_list", [])
if all(isinstance(summand[1], complex) for summand in pauli_list):
values["is_hermitian"] = all(
np.isclose(complex(summand[1]).real, summand[1])
for summand in pauli_list
)
if values.get("is_hermitian", False):
values["has_complex_coefficients"] = False
values["pauli_list"] = [
(summand[0], complex(summand[1].real)) for summand in pauli_list
]
else:
values["has_complex_coefficients"] = not all(
np.isclose(complex(summand[1]).real, summand[1])
for summand in pauli_list
if isinstance(summand[1], complex)
)
return values
def __mul__(self, coefficient: complex) -> "PauliOperator":
multiplied_ising = [
(monomial[0], self._multiply_monomial_coefficient(monomial[1], coefficient))
for monomial in self.pauli_list
]
return self.__class__(pauli_list=multiplied_ising)
@staticmethod
def _multiply_monomial_coefficient(
monomial_coefficient: ParameterComplexType, coefficient: complex
) -> ParameterComplexType:
if isinstance(monomial_coefficient, ParameterType):
return str(sympy.sympify(monomial_coefficient) * coefficient)
return monomial_coefficient * coefficient
@property
def is_commutative(self) -> bool:
return all(
self._is_sub_pauli_commutative(
[summand[0][qubit_num] for summand in self.pauli_list]
)
for qubit_num in range(self.num_qubits)
)
@staticmethod
def _is_sub_pauli_commutative(qubit_pauli_string: Union[List[str], str]) -> bool:
unique_paulis = set(qubit_pauli_string) - {"I"}
return len(unique_paulis) <= 1
@property
def num_qubits(self) -> int:
return len(self.pauli_list[0][0])
def to_matrix(self) -> np.ndarray:
if not all(isinstance(summand[1], complex) for summand in self.pauli_list):
raise ClassiqValueError(
"Supporting only Hamiltonian with numeric coefficients."
)
return sum(
cast(complex, summand[1]) * to_pauli_matrix(summand[0])
for summand in self.pauli_list
) # type: ignore[return-value]
@staticmethod
def _extend_pauli_string(
pauli_string: PydanticPauliMonomialStr, num_extra_qubits: int
) -> PydanticPauliMonomialStr:
return "I" * num_extra_qubits + pauli_string
def extend(self, num_extra_qubits: int) -> "PauliOperator":
new_pauli_list = [
(self._extend_pauli_string(pauli_string, num_extra_qubits), coeff)
for (pauli_string, coeff) in self.pauli_list
]
return self.copy(update={"pauli_list": new_pauli_list}, deep=True)
@staticmethod
def _reorder_pauli_string(
pauli_string: PydanticPauliMonomialStr,
order: Collection[int],
new_num_qubits: int,
) -> PydanticPauliMonomialStr:
reversed_pauli_string = pauli_string[::-1]
reversed_new_pauli_string = ["I"] * new_num_qubits
for logical_pos, actual_pos in enumerate(order):
reversed_new_pauli_string[actual_pos] = reversed_pauli_string[logical_pos]
return "".join(reversed(reversed_new_pauli_string))
@staticmethod
def _validate_reorder(
order: Collection[int],
num_qubits: int,
num_extra_qubits: int,
) -> None:
if num_extra_qubits < 0:
raise ClassiqValueError("Number of extra qubits cannot be negative")
if len(order) != num_qubits:
raise ClassiqValueError("The qubits order doesn't match the Pauli operator")
if len(order) != len(set(order)):
raise ClassiqValueError("The qubits order is not one-to-one")
if not all(pos < num_qubits + num_extra_qubits for pos in order):
raise ClassiqValueError(
"The qubits order contains qubits which do no exist"
)
@classmethod
def reorder(
cls,
operator: "PauliOperator",
order: Collection[int],
num_extra_qubits: int = 0,
) -> "PauliOperator":
cls._validate_reorder(order, operator.num_qubits, num_extra_qubits)
new_num_qubits = operator.num_qubits + num_extra_qubits
new_pauli_list = [
(cls._reorder_pauli_string(pauli_string, order, new_num_qubits), coeff)
for pauli_string, coeff in operator.pauli_list
]
return cls(pauli_list=new_pauli_list)
@classmethod
def from_unzipped_lists(
cls,
operators: List[List["Pauli"]],
coefficients: Optional[List[complex]] = None,
) -> "PauliOperator":
if coefficients is None:
coefficients = [1] * len(operators)
if len(operators) != len(coefficients):
raise ClassiqValueError(
f"The number of coefficients ({len(coefficients)}) must be equal to the number of pauli operators ({len(operators)})"
)
return cls(
pauli_list=[
(pauli_integers_to_str(op), coeff)
for op, coeff in zip(operators, coefficients)
]
)
class Config:
frozen = True
pauli_list: ConstrainedListValue
pydantic-field
required
A list of tuples each containing a pauli string comprised of I,X,Y,Z characters and a complex coefficient; for example [('IZ', 0.1), ('XY', 0.2)].
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
PauliOperatorV1 (HashablePydanticBaseModel)
pydantic-model
Specification of a Pauli sum operator.
Source code in classiq/interface/chemistry/operator.py
class PauliOperatorV1(HashablePydanticBaseModel):
"""
Specification of a Pauli sum operator.
"""
pauli_list: PydanticPauliList = pydantic.Field(
description="A list of tuples each containing a pauli string comprised of I,X,Y,Z characters and a complex coefficient; for example [('IZ', 0.1), ('XY', 0.2)].",
)
is_hermitian: bool = pydantic.Field(default=False)
has_complex_coefficients: bool = pydantic.Field(default=True)
def show(self) -> str:
if self.is_hermitian:
# If the operator is hermitian then the coefficients must be numeric
return "\n".join(
f"{summand[1].real:+.3f} * {summand[0]}" for summand in self.pauli_list # type: ignore[union-attr]
)
return "\n".join(
f"+({summand[1]:+.3f}) * {summand[0]}" for summand in self.pauli_list
)
@pydantic.validator("pauli_list", each_item=True, pre=True)
def _validate_pauli_monomials(
cls, monomial: Tuple[PydanticPauliMonomialStr, ParameterComplexType]
) -> Tuple[PydanticPauliMonomialStr, ParameterComplexType]:
_PauliMonomialLengthValidator( # type: ignore[call-arg]
monomial=monomial
) # Validate the length of the monomial.
coeff = cls._validate_monomial_coefficient(monomial[1])
parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff) # type: ignore[call-arg]
return (parsed_monomial.string, parsed_monomial.coeff)
@staticmethod
def _validate_monomial_coefficient(
coeff: Union[sympy.Expr, ParameterComplexType]
) -> ParameterComplexType:
if isinstance(coeff, str):
validate_expression_str(coeff)
elif isinstance(coeff, sympy.Expr):
coeff = str(coeff)
return coeff
@pydantic.validator("pauli_list")
def _validate_pauli_list(cls, pauli_list: PydanticPauliList) -> PydanticPauliList:
if not all_equal(len(summand[0]) for summand in pauli_list):
raise ClassiqValueError("Pauli strings have incompatible lengths.")
return pauli_list
@pydantic.root_validator
def _validate_hermitianity(cls, values: Dict[str, Any]) -> Dict[str, Any]:
pauli_list = values.get("pauli_list", [])
if all(isinstance(summand[1], complex) for summand in pauli_list):
values["is_hermitian"] = all(
np.isclose(complex(summand[1]).real, summand[1])
for summand in pauli_list
)
if values.get("is_hermitian", False):
values["has_complex_coefficients"] = False
values["pauli_list"] = [
(summand[0], complex(summand[1].real)) for summand in pauli_list
]
else:
values["has_complex_coefficients"] = not all(
np.isclose(complex(summand[1]).real, summand[1])
for summand in pauli_list
if isinstance(summand[1], complex)
)
return values
def __mul__(self, coefficient: complex) -> "PauliOperatorV1":
multiplied_ising = [
(monomial[0], self._multiply_monomial_coefficient(monomial[1], coefficient))
for monomial in self.pauli_list
]
return self.__class__(pauli_list=multiplied_ising)
@staticmethod
def _multiply_monomial_coefficient(
monomial_coefficient: ParameterComplexType, coefficient: complex
) -> ParameterComplexType:
if isinstance(monomial_coefficient, ParameterType):
return str(sympy.sympify(monomial_coefficient) * coefficient)
return monomial_coefficient * coefficient
@property
def is_commutative(self) -> bool:
return all(
self._is_sub_pauli_commutative(
[summand[0][qubit_num] for summand in self.pauli_list]
)
for qubit_num in range(self.num_qubits)
)
@staticmethod
def _is_sub_pauli_commutative(qubit_pauli_string: Union[List[str], str]) -> bool:
unique_paulis = set(qubit_pauli_string) - {"I"}
return len(unique_paulis) <= 1
@property
def num_qubits(self) -> int:
return len(self.pauli_list[0][0])
def to_matrix(self) -> np.ndarray:
if not all(isinstance(summand[1], complex) for summand in self.pauli_list):
raise ClassiqValueError(
"Supporting only Hamiltonian with numeric coefficients."
)
return sum(
cast(complex, summand[1]) * to_pauli_matrix(summand[0])
for summand in self.pauli_list
) # type: ignore[return-value]
@staticmethod
def _extend_pauli_string(
pauli_string: PydanticPauliMonomialStr, num_extra_qubits: int
) -> PydanticPauliMonomialStr:
return "I" * num_extra_qubits + pauli_string
def extend(self, num_extra_qubits: int) -> "PauliOperatorV1":
new_pauli_list = [
(self._extend_pauli_string(pauli_string, num_extra_qubits), coeff)
for (pauli_string, coeff) in self.pauli_list
]
return self.copy(update={"pauli_list": new_pauli_list}, deep=True)
@staticmethod
def _reorder_pauli_string(
pauli_string: PydanticPauliMonomialStr,
order: Collection[int],
new_num_qubits: int,
) -> PydanticPauliMonomialStr:
reversed_pauli_string = pauli_string[::-1]
reversed_new_pauli_string = ["I"] * new_num_qubits
for logical_pos, actual_pos in enumerate(order):
reversed_new_pauli_string[actual_pos] = reversed_pauli_string[logical_pos]
return "".join(reversed(reversed_new_pauli_string))
@staticmethod
def _validate_reorder(
order: Collection[int],
num_qubits: int,
num_extra_qubits: int,
) -> None:
if num_extra_qubits < 0:
raise ClassiqValueError("Number of extra qubits cannot be negative")
if len(order) != num_qubits:
raise ClassiqValueError("The qubits order doesn't match the Pauli operator")
if len(order) != len(set(order)):
raise ClassiqValueError("The qubits order is not one-to-one")
if not all(pos < num_qubits + num_extra_qubits for pos in order):
raise ClassiqValueError(
"The qubits order contains qubits which do no exist"
)
@classmethod
def reorder(
cls,
operator: "PauliOperatorV1",
order: Collection[int],
num_extra_qubits: int = 0,
) -> "PauliOperatorV1":
cls._validate_reorder(order, operator.num_qubits, num_extra_qubits)
new_num_qubits = operator.num_qubits + num_extra_qubits
new_pauli_list = [
(cls._reorder_pauli_string(pauli_string, order, new_num_qubits), coeff)
for pauli_string, coeff in operator.pauli_list
]
return cls(pauli_list=new_pauli_list)
@classmethod
def from_unzipped_lists(
cls,
operators: List[List["Pauli"]],
coefficients: Optional[List[complex]] = None,
) -> "PauliOperatorV1":
if coefficients is None:
coefficients = [1] * len(operators)
if len(operators) != len(coefficients):
raise ClassiqValueError(
f"The number of coefficients ({len(coefficients)}) must be equal to the number of pauli operators ({len(operators)})"
)
return cls(
pauli_list=[
(pauli_integers_to_str(op), coeff)
for op, coeff in zip(operators, coefficients)
]
)
class Config:
frozen = True
pauli_list: ConstrainedListValue
pydantic-field
required
A list of tuples each containing a pauli string comprised of I,X,Y,Z characters and a complex coefficient; for example [('IZ', 0.1), ('XY', 0.2)].
PauliOperators (VersionedModel)
pydantic-model
Source code in classiq/interface/chemistry/operator.py
class PauliOperators(VersionedModel):
operators: List[PauliOperator]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
combinatorial_optimization
special
mht_qaoa_input
MhtQaoaInput (BaseModel)
pydantic-model
Source code in classiq/interface/combinatorial_optimization/mht_qaoa_input.py
class MhtQaoaInput(BaseModel):
reps: pydantic.PositiveInt = pydantic.Field(
default=3, description="Number of QAOA layers."
)
plot_list: List[PlotData] = pydantic.Field(
description="The list of (x,y,t) plots of the MHT problem."
)
misdetection_maximum_time_steps: pydantic.NonNegativeInt = pydantic.Field(
default=0,
description="The maximum number of time steps a target might be misdetected.",
)
penalty_energy: float = pydantic.Field(
default=2,
description="Penalty energy for invalid solutions. The value affects "
"the converges rate. Small positive values are preferred",
)
three_local_coeff: float = pydantic.Field(
default=0,
description="Coefficient for the 3-local terms in the Hamiltonian. It is related to the angular acceleration.",
)
one_local_coeff: float = pydantic.Field(
default=0, description="Coefficient for the 1-local terms in the Hamiltonian."
)
is_penalty: bool = pydantic.Field(
default=True, description="Build Pubo using penalty terms"
)
max_velocity: float = pydantic.Field(
default=0, description="Max allowed velocity for a segment"
)
def is_valid_cost(self, cost: float) -> bool:
return True
@pydantic.validator("plot_list")
def round_plot_list_times_and_validate(
cls, plot_list: List[PlotData]
) -> List[PlotData]:
MhtQaoaInput._check_all_ids_are_distinct(plot_list)
MhtQaoaInput._round_to_tolerance_decimals(plot_list)
time_stamps = sorted({plot.t for plot in plot_list})
time_diff_set = {
np.round(time_stamps[i] - time_stamps[i - 1], decimals=_TOLERANCE_DECIMALS)
for i in range(1, len(time_stamps))
}
if len(time_diff_set) != 1:
raise ClassiqValueError(
"The time difference between each time stamp is not equal"
)
return plot_list
@staticmethod
def _round_to_tolerance_decimals(plot_list: List[PlotData]) -> None:
for plot in plot_list:
plot.t = np.round(plot.t, decimals=_TOLERANCE_DECIMALS)
@staticmethod
def _check_all_ids_are_distinct(plot_list: List[PlotData]) -> None:
if not more_itertools.all_unique(plot.plot_id for plot in plot_list):
raise ClassiqValueError("Plot IDs should be unique.")
is_penalty: bool
pydantic-field
Build Pubo using penalty terms
max_velocity: float
pydantic-field
Max allowed velocity for a segment
misdetection_maximum_time_steps: NonNegativeInt
pydantic-field
The maximum number of time steps a target might be misdetected.
one_local_coeff: float
pydantic-field
Coefficient for the 1-local terms in the Hamiltonian.
penalty_energy: float
pydantic-field
Penalty energy for invalid solutions. The value affects the converges rate. Small positive values are preferred
plot_list: List[classiq.interface.combinatorial_optimization.mht_qaoa_input.PlotData]
pydantic-field
required
The list of (x,y,t) plots of the MHT problem.
reps: PositiveInt
pydantic-field
Number of QAOA layers.
three_local_coeff: float
pydantic-field
Coefficient for the 3-local terms in the Hamiltonian. It is related to the angular acceleration.
PlotData (BaseModel)
pydantic-model
Source code in classiq/interface/combinatorial_optimization/mht_qaoa_input.py
class PlotData(BaseModel):
# We are currently ignoring units. This might need to be handled in the future
x: float = pydantic.Field(description="The X coordinate of this plot")
y: float = pydantic.Field(description="The Y coordinate of this plot")
t: float = pydantic.Field(description="The time stamp of this plot")
plot_id: pydantic.NonNegativeInt = pydantic.Field(
description="The plot ID of this plot"
)
plot_id: NonNegativeInt
pydantic-field
required
The plot ID of this plot
t: float
pydantic-field
required
The time stamp of this plot
x: float
pydantic-field
required
The X coordinate of this plot
y: float
pydantic-field
required
The Y coordinate of this plot
optimization_problem
MaxCutProblem (BaseModel)
pydantic-model
Source code in classiq/interface/combinatorial_optimization/optimization_problem.py
class MaxCutProblem(BaseModel):
qaoa_reps: pydantic.PositiveInt = pydantic.Field(
default=1, description="Number of layers in qaoa ansatz."
)
optimizer_preferences: CombinatorialOptimizer = pydantic.Field(
default_factory=CombinatorialOptimizer,
description="preferences for the VQE execution",
)
serialized_graph: Dict[str, Any]
optimizer_preferences: CombinatorialOptimizer
pydantic-field
preferences for the VQE execution
qaoa_reps: PositiveInt
pydantic-field
Number of layers in qaoa ansatz.
result
AnglesResult (VersionedModel)
pydantic-model
Source code in classiq/interface/combinatorial_optimization/result.py
class AnglesResult(VersionedModel):
initial_point: List[float]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
PyomoObjectResult (VersionedModel)
pydantic-model
Source code in classiq/interface/combinatorial_optimization/result.py
class PyomoObjectResult(VersionedModel):
details: str
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
execution
special
result
ResourceEstimatorResult (VersionedModel)
pydantic-model
Source code in classiq/interface/execution/result.py
class ResourceEstimatorResult(VersionedModel):
report_json: str
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
executor
special
aws_execution_cost
ExecutionCostForTimePeriod (BaseModel)
pydantic-model
Source code in classiq/interface/executor/aws_execution_cost.py
class ExecutionCostForTimePeriod(pydantic.BaseModel):
start: date = pydantic.Field(
description="The beginning of the time period for tasks usage and cost ("
"inclusive).",
)
end: date = pydantic.Field(
description="The end of the time period for tasks usage and cost (exclusive).",
)
granularity: Granularity = pydantic.Field(
description="Either MONTHLY or DAILY, or HOURLY.", default=Granularity.daily
)
cost_scope: CostScope = pydantic.Field(
description="Either user or organization", default=CostScope.user
)
class Config:
json_encoders = {date: lambda v: v.strftime("%Y-%m-%d")}
@validator("end")
def date_order(cls, v: date, values: Dict[str, Any], **kwargs: Any) -> date:
if "start" in values and v <= values["start"]:
raise ClassiqValueError('"end" date should be after "start" date')
return v
cost_scope: CostScope
pydantic-field
Either user or organization
end: date
pydantic-field
required
The end of the time period for tasks usage and cost (exclusive).
granularity: Granularity
pydantic-field
Either MONTHLY or DAILY, or HOURLY.
start: date
pydantic-field
required
The beginning of the time period for tasks usage and cost (inclusive).
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
estimation
OperatorsEstimation (BaseModel)
pydantic-model
Estimate the expectation value of a list of Pauli operators on a quantum state given by a quantum program.
Source code in classiq/interface/executor/estimation.py
class OperatorsEstimation(pydantic.BaseModel):
"""
Estimate the expectation value of a list of Pauli operators on a quantum state given
by a quantum program.
"""
quantum_program: QuantumCode
operators: PauliOperators
execution_preferences
ExecutionPreferences (BaseModel)
pydantic-model
Source code in classiq/interface/executor/execution_preferences.py
class ExecutionPreferences(pydantic.BaseModel):
timeout_sec: Optional[pydantic.PositiveInt] = pydantic.Field(
default=None,
description="If set, limits the execution runtime. Value is in seconds. "
"Not supported on all platforms.",
)
noise_properties: Optional[NoiseProperties] = pydantic.Field(
default=None, description="Properties of the noise in the circuit"
)
random_seed: int = pydantic.Field(
default_factory=create_random_seed,
description="The random seed used for the execution",
)
backend_preferences: BackendPreferencesTypes = backend_preferences_field(
backend_name=ClassiqSimulatorBackendNames.SIMULATOR
)
num_shots: Optional[pydantic.PositiveInt] = pydantic.Field(default=None)
transpile_to_hardware: TranspilationOption = pydantic.Field(
default=TranspilationOption.DECOMPOSE,
description="Transpile the circuit to the hardware basis gates before execution",
title="Transpilation Option",
)
job_name: Optional[str] = pydantic.Field(
min_length=1,
description="The job name",
)
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
@pydantic.validator("backend_preferences", always=True)
def validate_timeout_for_aws(
cls, backend_preferences: BackendPreferencesTypes, values: Dict[str, Any]
) -> BackendPreferencesTypes:
timeout = values.get("timeout_sec", None)
if (
not isinstance(backend_preferences, AwsBackendPreferences)
or timeout is None
):
return backend_preferences
if (
timeout != backend_preferences.job_timeout
and backend_preferences.job_timeout != AWS_DEFAULT_JOB_TIMEOUT_SECONDS
):
raise ClassiqValueError(DIFFERENT_TIMEOUT_MSG)
if timeout > MAX_EXECUTION_TIMEOUT_SECONDS:
raise ClassiqValueError(TIMEOUT_LARGE_FOR_AWS_MSG)
backend_preferences.job_timeout = timeout
return backend_preferences
backend_preferences: Union[classiq.interface.backend.backend_preferences.AzureBackendPreferences, classiq.interface.backend.backend_preferences.ClassiqBackendPreferences, classiq.interface.backend.backend_preferences.IBMBackendPreferences, classiq.interface.backend.backend_preferences.AwsBackendPreferences, classiq.interface.backend.backend_preferences.IonqBackendPreferences, classiq.interface.backend.backend_preferences.GCPBackendPreferences, classiq.interface.backend.backend_preferences.AliceBobBackendPreferences, classiq.interface.backend.backend_preferences.OQCBackendPreferences]
pydantic-field
Preferences for the requested backend to run the quantum circuit.
job_name: ConstrainedStrValue
pydantic-field
The job name
noise_properties: NoiseProperties
pydantic-field
Properties of the noise in the circuit
random_seed: int
pydantic-field
The random seed used for the execution
timeout_sec: PositiveInt
pydantic-field
If set, limits the execution runtime. Value is in seconds. Not supported on all platforms.
transpile_to_hardware: TranspilationOption
pydantic-field
Transpile the circuit to the hardware basis gates before execution
execution_request
ExecutionJobDetails (VersionedModel)
pydantic-model
Source code in classiq/interface/executor/execution_request.py
class ExecutionJobDetails(VersionedModel):
id: str
name: Optional[str]
start_time: datetime
end_time: Optional[datetime]
provider: Optional[str]
backend_name: Optional[str]
status: JobStatus
num_shots: Optional[int]
program_id: Optional[str]
error: Optional[str]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
ExecutionJobsQueryResults (VersionedModel)
pydantic-model
Source code in classiq/interface/executor/execution_request.py
class ExecutionJobsQueryResults(VersionedModel):
results: List[ExecutionJobDetails]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
ExecutionRequest (BaseModel)
pydantic-model
Source code in classiq/interface/executor/execution_request.py
class ExecutionRequest(BaseModel, json_encoders=CUSTOM_ENCODERS):
execution_payload: ExecutionPayloads
preferences: ExecutionPreferences = pydantic.Field(
default_factory=ExecutionPreferences,
description="preferences for the execution",
)
preferences: ExecutionPreferences
pydantic-field
preferences for the execution
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
QuantumProgramExecution (QuantumProgram)
pydantic-model
Source code in classiq/interface/executor/execution_request.py
class QuantumProgramExecution(QuantumProgram):
execution_type: Literal["quantum_program2"] = "quantum_program2"
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
QuantumProgramExecutionRequest (ExecutionRequest)
pydantic-model
Source code in classiq/interface/executor/execution_request.py
class QuantumProgramExecutionRequest(ExecutionRequest):
execution_payload: QuantumCodeExecution
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
execution_result
ExecuteGeneratedCircuitResults (VersionedModel)
pydantic-model
Source code in classiq/interface/executor/execution_result.py
class ExecuteGeneratedCircuitResults(VersionedModel):
results: ResultsCollection
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
iqae_result
IQAEResult (VersionedModel, QmodPyObject)
pydantic-model
Source code in classiq/interface/executor/iqae_result.py
class IQAEResult(VersionedModel, QmodPyObject):
estimation: float
confidence_interval: Tuple[float, float]
iterations_data: List[IQAEIterationData]
warnings: List[str]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
optimizer_preferences
CombinatorialOptimizer (OptimizerPreferences)
pydantic-model
Source code in classiq/interface/executor/optimizer_preferences.py
class CombinatorialOptimizer(OptimizerPreferences):
cost_type: CostType = pydantic.Field(
default=CostType.CVAR,
description="Summarizing method of the measured bit strings",
)
alpha_cvar: Optional[PydanticAlphaParamCVAR] = pydantic.Field(
default=None, description="Parameter for the CVAR summarizing method"
)
is_maximization: bool = pydantic.Field(
default=False,
description="Whether the optimization goal is to maximize",
)
should_check_valid_solutions: bool = pydantic.Field(
default=False,
description="Whether to check if all the solutions satisfy the constraints",
)
@pydantic.validator("alpha_cvar", pre=True, always=True)
def check_alpha_cvar(
cls, alpha_cvar: Optional[PydanticAlphaParamCVAR], values: Dict[str, Any]
) -> Optional[PydanticAlphaParamCVAR]:
cost_type = values.get("cost_type")
if alpha_cvar is not None and cost_type != CostType.CVAR:
raise ClassiqValueError("Use CVAR params only for CostType.CVAR.")
if alpha_cvar is None and cost_type == CostType.CVAR:
alpha_cvar = PydanticAlphaParamCVAR(0.2)
return alpha_cvar
alpha_cvar: PydanticAlphaParamCVAR
pydantic-field
Parameter for the CVAR summarizing method
cost_type: CostType
pydantic-field
Summarizing method of the measured bit strings
is_maximization: bool
pydantic-field
Whether the optimization goal is to maximize
should_check_valid_solutions: bool
pydantic-field
Whether to check if all the solutions satisfy the constraints
OptimizerPreferences (BaseModel)
pydantic-model
Source code in classiq/interface/executor/optimizer_preferences.py
class OptimizerPreferences(BaseModel):
name: OptimizerType = pydantic.Field(
default=OptimizerType.COBYLA, description="Classical optimization algorithm."
)
num_shots: Optional[pydantic.PositiveInt] = pydantic.Field(
default=None,
description="Number of repetitions of the quantum ansatz.",
)
max_iteration: pydantic.PositiveInt = pydantic.Field(
default=100, description="Maximal number of optimizer iterations"
)
tolerance: Optional[pydantic.PositiveFloat] = pydantic.Field(
default=None, description="Final accuracy in the optimization"
)
step_size: Optional[pydantic.PositiveFloat] = pydantic.Field(
default=None,
description="step size for numerically " "calculating the gradient",
)
random_seed: Optional[int] = pydantic.Field(
default=None,
description="The random seed used for the generation",
)
initial_point: Optional[List[float]] = pydantic.Field(
default=None,
description="Initial values for the ansatz parameters",
)
skip_compute_variance: bool = pydantic.Field(
default=False,
description="If True, the optimizer will not compute the variance of the ansatz.",
)
@pydantic.validator("tolerance", pre=True, always=True)
def check_tolerance(
cls, tolerance: Optional[pydantic.PositiveFloat], values: Dict[str, Any]
) -> Optional[pydantic.PositiveFloat]:
optimizer_type = values.get("type")
if tolerance is not None and optimizer_type == OptimizerType.SPSA:
raise ClassiqValueError("No tolerance param for SPSA optimizer")
if tolerance is None and optimizer_type != OptimizerType.SPSA:
tolerance = pydantic.PositiveFloat(0.001)
return tolerance
@pydantic.validator("step_size", pre=True, always=True)
def check_step_size(
cls, step_size: Optional[pydantic.PositiveFloat], values: Dict[str, Any]
) -> Optional[pydantic.PositiveFloat]:
optimizer_type = values.get("name")
if step_size is not None and optimizer_type not in (
OptimizerType.L_BFGS_B,
OptimizerType.ADAM,
):
raise ClassiqValueError(
"Use step_size only for L_BFGS_B or ADAM optimizers."
)
if step_size is None and optimizer_type in (
OptimizerType.L_BFGS_B,
OptimizerType.ADAM,
):
step_size = pydantic.PositiveFloat(0.05)
return step_size
initial_point: List[float]
pydantic-field
Initial values for the ansatz parameters
max_iteration: PositiveInt
pydantic-field
Maximal number of optimizer iterations
name: OptimizerType
pydantic-field
Classical optimization algorithm.
num_shots: PositiveInt
pydantic-field
Number of repetitions of the quantum ansatz.
random_seed: int
pydantic-field
The random seed used for the generation
skip_compute_variance: bool
pydantic-field
If True, the optimizer will not compute the variance of the ansatz.
step_size: PositiveFloat
pydantic-field
step size for numerically calculating the gradient
tolerance: PositiveFloat
pydantic-field
Final accuracy in the optimization
quantum_code
QuantumBaseCode (BaseModel)
pydantic-model
Source code in classiq/interface/executor/quantum_code.py
class QuantumBaseCode(BaseModel):
syntax: QuantumInstructionSet = pydantic.Field(
default=QuantumInstructionSet.QASM, description="The syntax of the program."
)
code: CodeType = pydantic.Field(
..., description="The textual representation of the program"
)
@pydantic.validator("code")
def load_quantum_program(
cls, code: Union[CodeType, IonqQuantumCircuit], values: Dict[str, Any]
) -> CodeType:
syntax = values.get("syntax")
if isinstance(code, IonqQuantumCircuit):
if syntax != QuantumInstructionSet.IONQ:
raise ClassiqValueError(
f"Invalid code type {type(code)} for syntax: {syntax}"
)
return code.json()
return code
code: str
pydantic-field
required
The textual representation of the program
syntax: QuantumInstructionSet
pydantic-field
The syntax of the program.
QuantumCode (QuantumBaseCode)
pydantic-model
Source code in classiq/interface/executor/quantum_code.py
class QuantumCode(QuantumBaseCode):
arguments: MultipleArguments = pydantic.Field(
default=(),
description="The parameters dictionary for a parametrized quantum program.",
)
output_qubits_map: OutputQubitsMap = pydantic.Field(
default_factory=dict,
description="The map of outputs to their qubits in the circuit.",
)
registers_initialization: Optional[RegistersInitialization] = pydantic.Field(
default_factory=None,
description="Initial conditions for the different registers in the circuit.",
)
synthesis_execution_data: Optional[ExecutionData] = pydantic.Field(default=None)
synthesis_execution_arguments: Arguments = pydantic.Field(default_factory=dict)
class Config:
validate_assignment = True
@pydantic.validator("arguments")
def validate_arguments(
cls, arguments: MultipleArguments, values: Dict[str, Any]
) -> MultipleArguments:
if arguments and values.get("syntax") not in (
QuantumInstructionSet.QSHARP,
QuantumInstructionSet.QASM,
):
raise ClassiqValueError("Only QASM or Q# programs support arguments")
if values.get("syntax") == QuantumInstructionSet.QSHARP and len(arguments) > 1:
raise ClassiqValueError(
f"Q# programs supports only one group of arguments. {len(arguments)} given"
)
return arguments
@pydantic.validator("synthesis_execution_data")
def validate_synthesis_execution_data(
cls,
synthesis_execution_data: Optional[ExecutionData],
values: Dict[str, Any],
) -> Optional[ExecutionData]:
if (
synthesis_execution_data is not None
and values.get("syntax") is not QuantumInstructionSet.QASM
):
raise ClassiqValueError("Only QASM supports the requested configuration")
return synthesis_execution_data
@staticmethod
def from_file(
file_path: Union[str, Path],
syntax: Optional[Union[str, QuantumInstructionSet]] = None,
arguments: MultipleArguments = (),
) -> QuantumCode:
path = Path(file_path)
code = path.read_text()
if syntax is None:
syntax = QuantumInstructionSet.from_suffix(path.suffix.lstrip("."))
return QuantumCode(syntax=syntax, code=code, arguments=arguments)
arguments: Tuple[Dict[classiq.interface.backend.pydantic_backend.ConstrainedStrValue, Any], ...]
pydantic-field
The parameters dictionary for a parametrized quantum program.
output_qubits_map: Dict[str, Tuple[int, ...]]
pydantic-field
The map of outputs to their qubits in the circuit.
registers_initialization: Dict[str, classiq.interface.executor.register_initialization.RegisterInitialization]
pydantic-field
Initial conditions for the different registers in the circuit.
result
EstimationResult (BaseModel, QmodPyObject)
pydantic-model
Source code in classiq/interface/executor/result.py
class EstimationResult(BaseModel, QmodPyObject):
value: Complex = pydantic.Field(..., description="Estimation for the operator")
variance: Complex = pydantic.Field(..., description="Variance of the estimation")
metadata: EstimationMetadata = pydantic.Field(
..., description="Metadata for the estimation"
)
metadata: EstimationMetadata
pydantic-field
required
Metadata for the estimation
value: Complex
pydantic-field
required
Estimation for the operator
variance: Complex
pydantic-field
required
Variance of the estimation
EstimationResults (VersionedModel)
pydantic-model
Source code in classiq/interface/executor/result.py
class EstimationResults(VersionedModel):
results: List[EstimationResult]
def __len__(self) -> int:
return len(self.results)
def __iter__(self) -> Iterator[EstimationResult]: # type: ignore[override]
# TODO This is a bug waiting to happen. We change the meaning of
# __iter__ in a derived class.
return iter(self.results)
def __getitem__(self, index: int) -> EstimationResult:
return self.results[index]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
ExecutionDetails (BaseModel, QmodPyObject)
pydantic-model
Source code in classiq/interface/executor/result.py
class ExecutionDetails(BaseModel, QmodPyObject):
vendor_format_result: Dict[str, Any] = pydantic.Field(
..., description="Result in proprietary vendor format"
)
counts: Counts = pydantic.Field(
default_factory=dict, description="Number of counts per state"
)
counts_lsb_right: bool = pydantic.Field(
True,
description="Is the qubit order of counts field such that the LSB is right?",
)
parsed_states: ParsedStates = pydantic.Field(
default_factory=dict,
description="A mapping between the raw states of counts (bitstrings) to their parsed states (registers' values)",
)
histogram: Optional[Dict[State, pydantic.NonNegativeFloat]] = pydantic.Field(
None,
description="Histogram of probability per state (an alternative to counts)",
)
output_qubits_map: OutputQubitsMap = pydantic.Field(
default_factory=dict,
description="The map of outputs (measured registers) to their qubits in the circuit.",
)
state_vector: StateVector = pydantic.Field(
default=None,
description="The state vector when executed on a simulator, with LSB right qubit order",
)
parsed_state_vector_states: ParsedStates = pydantic.Field(
default=None,
description="A mapping between the raw states of the state vector (bitstrings) to their parsed states (registers' values)",
)
physical_qubits_map: Optional[OutputQubitsMap] = pydantic.Field(
default=None,
description="The map of all registers (also non measured) to their qubits in the circuit. Used for state_vector which represent also the non-measured qubits.",
)
num_shots: Optional[pydantic.NonNegativeInt] = pydantic.Field(
default=None, description="The total number of shots the circuit was executed"
)
@pydantic.validator("counts", pre=True)
def _clean_spaces_from_counts_keys(cls, v: Counts) -> Counts:
if not v or " " not in list(v.keys())[0]:
return v
return {state.replace(" ", ""): v[state] for state in v}
@pydantic.validator("num_shots", always=True)
def _validate_num_shots(
cls, num_shots: Optional[int], values: Dict[str, Any]
) -> Optional[int]:
if num_shots is not None:
return num_shots
counts = values.get("counts")
if not counts:
return None
return sum(shots for _, shots in counts.items())
@property
def parsed_counts(self) -> ParsedCounts:
return get_parsed_counts(self.counts, self.parsed_states)
@property
def parsed_state_vector(self) -> Optional[ParsedStateVector]:
if not self.state_vector:
return None
parsed_state_vector = [
SimulatedState(
state=self.parsed_state_vector_states[bitstring],
bitstring=bitstring,
amplitude=complex(amplitude_str),
)
for bitstring, amplitude_str in self.state_vector.items()
]
return sorted(parsed_state_vector, key=lambda k: abs(k.amplitude), reverse=True)
def flip_execution_counts_bitstring(self) -> None:
"""Backends should return result count bitstring in right to left form"""
self.counts = flip_counts_qubit_order(self.counts)
self.counts_lsb_right = not self.counts_lsb_right
def counts_by_qubit_order(self, lsb_right: bool) -> Counts:
if self.counts_lsb_right != lsb_right:
return flip_counts_qubit_order(self.counts)
else:
return self.counts
def counts_of_qubits(self, *qubits: int) -> Counts:
_validate_qubit_indices(self.counts, qubits)
reduced_counts: DefaultDict[State, int] = defaultdict(int)
for state_str, state_count in self.counts_by_qubit_order(
lsb_right=False
).items():
reduced_counts[_slice_str(state_str, qubits)] += state_count
return dict(reduced_counts)
def counts_of_output(self, output_name: Name) -> Counts:
if output_name not in self.output_qubits_map:
raise ClassiqError(_UNAVAILABLE_OUTPUT_ERROR_MSG)
return self.counts_of_qubits(*self.output_qubits_map[output_name])
def counts_of_multiple_outputs(
self, output_names: Tuple[Name, ...]
) -> Dict[Tuple[State, ...], pydantic.NonNegativeInt]:
if any(name not in self.output_qubits_map for name in output_names):
raise ClassiqError(_UNAVAILABLE_OUTPUT_ERROR_MSG)
output_regs: Tuple[Qubits, ...] = tuple(
self.output_qubits_map[name] for name in output_names
)
reduced_counts: DefaultDict[Tuple[State, ...], int] = defaultdict(int)
for state_str, state_count in self.counts_by_qubit_order(
lsb_right=False
).items():
reduced_strs = tuple(_slice_str(state_str, reg) for reg in output_regs)
reduced_counts[reduced_strs] += state_count
return dict(reduced_counts)
def parsed_counts_of_outputs(
self, output_names: Union[Name, Tuple[Name, ...]]
) -> ParsedCounts:
if isinstance(output_names, Name):
output_names = (output_names,)
if any(name not in self.output_qubits_map for name in output_names):
raise ClassiqError(_UNAVAILABLE_OUTPUT_ERROR_MSG)
reduced_parsed_states = reduce_parsed_states(self.parsed_states, output_names)
return get_parsed_counts(self.counts, reduced_parsed_states)
def register_output_from_qubits(self, qubits: Tuple[int, ...]) -> Dict[float, int]:
register_output: Dict[float, int] = {}
value_from_str_bin = functools.partial(
self._get_register_value_from_binary_string_results, register_qubits=qubits
)
for results_binary_key, counts in self.counts_by_qubit_order(
lsb_right=False
).items():
value = value_from_str_bin(binary_string=results_binary_key)
register_output[value] = register_output.get(value, 0) + counts
return register_output
@staticmethod
def _get_register_value_from_binary_string_results(
binary_string: str, register_qubits: List[int]
) -> RegisterValue:
register_binary_string = "".join(
operator.itemgetter(*register_qubits)(binary_string)
)[::-1]
return number_utils.binary_to_float_or_int(bin_rep=register_binary_string)
counts: Dict[str, pydantic.types.NonNegativeInt]
pydantic-field
Number of counts per state
counts_lsb_right: bool
pydantic-field
Is the qubit order of counts field such that the LSB is right?
histogram: Dict[str, pydantic.types.NonNegativeFloat]
pydantic-field
Histogram of probability per state (an alternative to counts)
num_shots: NonNegativeInt
pydantic-field
The total number of shots the circuit was executed
output_qubits_map: Dict[str, Tuple[int, ...]]
pydantic-field
The map of outputs (measured registers) to their qubits in the circuit.
parsed_state_vector_states: Mapping[str, Mapping[str, Union[float, int]]]
pydantic-field
A mapping between the raw states of the state vector (bitstrings) to their parsed states (registers' values)
parsed_states: Mapping[str, Mapping[str, Union[float, int]]]
pydantic-field
A mapping between the raw states of counts (bitstrings) to their parsed states (registers' values)
physical_qubits_map: Dict[str, Tuple[int, ...]]
pydantic-field
The map of all registers (also non measured) to their qubits in the circuit. Used for state_vector which represent also the non-measured qubits.
state_vector: Dict[str, Any]
pydantic-field
The state vector when executed on a simulator, with LSB right qubit order
vendor_format_result: Dict[str, Any]
pydantic-field
required
Result in proprietary vendor format
flip_execution_counts_bitstring(self)
Backends should return result count bitstring in right to left form
Source code in classiq/interface/executor/result.py
def flip_execution_counts_bitstring(self) -> None:
"""Backends should return result count bitstring in right to left form"""
self.counts = flip_counts_qubit_order(self.counts)
self.counts_lsb_right = not self.counts_lsb_right
GroverSimulationResults (VersionedModel)
pydantic-model
Source code in classiq/interface/executor/result.py
class GroverSimulationResults(VersionedModel):
result: Dict[str, Any]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
MultipleExecutionDetails (VersionedModel)
pydantic-model
Source code in classiq/interface/executor/result.py
class MultipleExecutionDetails(VersionedModel):
details: List[ExecutionDetails]
def __getitem__(self, index: int) -> ExecutionDetails:
return self.details[index]
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
SampledState (BaseModel)
pydantic-model
Source code in classiq/interface/executor/result.py
class SampledState(BaseModel):
state: ParsedState
shots: MeasuredShots
def __repr__(self) -> str:
return f"{self.state}: {self.shots}"
__repr__(self)
special
Return repr(self).
Source code in classiq/interface/executor/result.py
def __repr__(self) -> str:
return f"{self.state}: {self.shots}"
vqe_result
VQEIntermediateData (BaseModel)
pydantic-model
Source code in classiq/interface/executor/vqe_result.py
class VQEIntermediateData(BaseModel):
utc_time: datetime = pydantic.Field(description="Time when the iteration finished")
iteration_number: pydantic.PositiveInt = pydantic.Field(
description="The iteration's number (evaluation count)"
)
parameters: List[float] = pydantic.Field(
description="The optimizer parameters for the variational form"
)
mean_all_solutions: Optional[float] = pydantic.Field(
default=None, description="The mean score of all solutions in this iteration"
)
solutions: List[SolutionData] = pydantic.Field(
description="Solutions found in this iteration, their score and"
"number of repetitions"
)
standard_deviation: float = pydantic.Field(
description="The evaluated standard deviation"
)
iteration_number: PositiveInt
pydantic-field
required
The iteration's number (evaluation count)
mean_all_solutions: float
pydantic-field
The mean score of all solutions in this iteration
parameters: List[float]
pydantic-field
required
The optimizer parameters for the variational form
solutions: List[classiq.interface.executor.vqe_result.SolutionData]
pydantic-field
required
Solutions found in this iteration, their score andnumber of repetitions
standard_deviation: float
pydantic-field
required
The evaluated standard deviation
utc_time: datetime
pydantic-field
required
Time when the iteration finished
finance
special
finance_modelling_params
FinanceModellingParams (BaseModel)
pydantic-model
Source code in classiq/interface/finance/finance_modelling_params.py
class FinanceModellingParams(BaseModel):
finance_model: Finance = pydantic.Field(
description="The model parameter for the finance problem."
)
phase_port_size: int = pydantic.Field(description="Width of the phase port.")
finance_model: Finance
pydantic-field
required
The model parameter for the finance problem.
phase_port_size: int
pydantic-field
required
Width of the phase port.
function_input
FinanceFunctionInput (BaseModel)
pydantic-model
Source code in classiq/interface/finance/function_input.py
class FinanceFunctionInput(pydantic.BaseModel):
f: "FinanceFunctionType" = pydantic.Field(
description="An enumeration of the wanted financial function: VaR, expected "
"shortfall, European call options or x^2"
)
variable: str = pydantic.Field(
default="x", description="Variable/s of the function"
)
condition: FunctionCondition = pydantic.Field(
description="The condition for the function"
)
polynomial_degree: Optional[int] = pydantic.Field(
default=None,
description="The polynomial degree of approximation, uses linear approximation by default",
)
use_chebyshev_polynomial_approximation: bool = pydantic.Field(
default=False,
description="Flag if to use chebyshev polynomial approximation for target function",
)
tail_probability: Optional[PydanticNonZeroProbabilityFloat] = pydantic.Field(
default=None,
description="The required probability on the tail of the distribution (1 - percentile)",
)
@pydantic.validator("f", pre=True)
def _convert_f_if_str(cls, f: Any) -> "FinanceFunctionType":
# Keep this for backwards-compatible string support
if f in get_finance_function_dict():
return get_finance_function_dict()[f]
return f
@pydantic.validator("use_chebyshev_polynomial_approximation")
def _validate_polynomial_flag(
cls, use_chebyshev_flag: bool, values: Dict[str, Any]
) -> bool:
if use_chebyshev_flag ^ (values.get("polynomial_degree") is None):
return use_chebyshev_flag
raise ClassiqValueError(
"Degree must be positive and use_chebyshev_polynomial_approximation set to True"
)
@pydantic.validator("f")
def _validate_finance_function(
cls, f: Union[int, str, "FinanceFunctionType"]
) -> "FinanceFunctionType":
from classiq.qmod.builtins.enums import FinanceFunctionType
if isinstance(f, FinanceFunctionType):
return f
if isinstance(f, int):
return FinanceFunctionType(f)
return get_finance_function_dict()[f]
@pydantic.validator("tail_probability", always=True)
def _validate_tail_probability_assignment_for_shortfall(
cls,
tail_probability: Optional[PydanticNonZeroProbabilityFloat],
values: Dict[str, Any],
) -> Optional[PydanticNonZeroProbabilityFloat]:
from classiq.qmod.builtins.enums import FinanceFunctionType
if values.get("f") == FinanceFunctionType.SHORTFALL and not tail_probability:
raise ClassiqValueError(
"Tail probability must be set for expected shortfall"
)
return tail_probability
class Config:
frozen = True
condition: FunctionCondition
pydantic-field
required
The condition for the function
f: FinanceFunctionType
pydantic-field
required
An enumeration of the wanted financial function: VaR, expected shortfall, European call options or x^2
polynomial_degree: int
pydantic-field
The polynomial degree of approximation, uses linear approximation by default
tail_probability: PydanticNonZeroProbabilityFloat
pydantic-field
The required probability on the tail of the distribution (1 - percentile)
use_chebyshev_polynomial_approximation: bool
pydantic-field
Flag if to use chebyshev polynomial approximation for target function
variable: str
pydantic-field
Variable/s of the function
FunctionCondition (BaseModel)
pydantic-model
Source code in classiq/interface/finance/function_input.py
class FunctionCondition(pydantic.BaseModel):
threshold: float
larger: bool = pydantic.Field(
default=False,
description="When true, function is set when input is larger to threshold and otherwise 0. Default is False.",
)
class Config:
frozen = True
larger: bool
pydantic-field
When true, function is set when input is larger to threshold and otherwise 0. Default is False.
gaussian_model_input
GaussianModelInput (FinanceModelInput)
pydantic-model
Source code in classiq/interface/finance/gaussian_model_input.py
class GaussianModelInput(FinanceModelInput):
kind: Literal["gaussian"] = pydantic.Field(default="gaussian")
num_qubits: pydantic.PositiveInt = pydantic.Field(
description="The number of qubits represent"
"the latent normal random variable Z (Resolution of "
"the random variable Z)."
)
normal_max_value: float = pydantic.Field(
description="Min/max value to truncate the " "latent normal random variable Z"
)
default_probabilities: List[PydanticProbabilityFloat] = pydantic.Field(
description="default probabilities for each asset"
)
rhos: List[pydantic.PositiveFloat] = pydantic.Field(
description="Sensitivities of default probability of assets "
"with respect to Z (1/sigma(Z))"
)
loss: List[int] = pydantic.Field(
description="List of ints signifying loss per asset"
)
min_loss: Optional[int] = pydantic.Field(
description="Minimum possible loss for the model "
)
@property
def num_model_qubits(self) -> int:
return len(self.rhos)
@property
def distribution_range(self) -> Tuple[float, float]:
return 0, sum(self.loss)
@property
def num_output_qubits(self) -> int:
return int(math.log2(sum(self.loss))) + 1
@property
def num_bernoulli_qubits(self) -> int:
return self.num_qubits + self.num_model_qubits
default_probabilities: List[classiq.interface.helpers.custom_pydantic_types.PydanticProbabilityFloat]
pydantic-field
required
default probabilities for each asset
loss: List[int]
pydantic-field
required
List of ints signifying loss per asset
min_loss: int
pydantic-field
Minimum possible loss for the model
normal_max_value: float
pydantic-field
required
Min/max value to truncate the latent normal random variable Z
num_qubits: PositiveInt
pydantic-field
required
The number of qubits representthe latent normal random variable Z (Resolution of the random variable Z).
rhos: List[pydantic.types.PositiveFloat]
pydantic-field
required
Sensitivities of default probability of assets with respect to Z (1/sigma(Z))
log_normal_model_input
LogNormalModelInput (FinanceModelInput)
pydantic-model
Source code in classiq/interface/finance/log_normal_model_input.py
class LogNormalModelInput(FinanceModelInput):
kind: Literal["log_normal"] = pydantic.Field(default="log_normal")
num_qubits: pydantic.PositiveInt = pydantic.Field(
description="Number of qubits to represent the probability."
)
mu: pydantic.NonNegativeFloat = pydantic.Field(
description="Mean of the Normal distribution variable X s.t. ln(X) ~ log-normal."
)
sigma: pydantic.PositiveFloat = pydantic.Field(
description="Std of the Normal distribution variable X s.t. ln(X) ~ log-normal."
)
@property
def distribution_range(self) -> Tuple[float, float]:
mean = np.exp(self.mu + self.sigma**2 / 2)
variance = (np.exp(self.sigma**2) - 1) * np.exp(2 * self.mu + self.sigma**2)
stddev = np.sqrt(variance)
low = np.maximum(0, mean - 3 * stddev)
high = mean + 3 * stddev
return low, high
@property
def num_model_qubits(self) -> int:
return self.num_qubits
@property
def num_output_qubits(self) -> int:
return self.num_qubits
class Config:
frozen = True
mu: NonNegativeFloat
pydantic-field
required
Mean of the Normal distribution variable X s.t. ln(X) ~ log-normal.
num_qubits: PositiveInt
pydantic-field
required
Number of qubits to represent the probability.
sigma: PositiveFloat
pydantic-field
required
Std of the Normal distribution variable X s.t. ln(X) ~ log-normal.
generator
special
amplitude_estimation
AmplitudeEstimation (FunctionParams)
pydantic-model
Creates a quantum circuit for amplitude estimation Provide the state preparation and oracle within the GroverOperator parameter Choose estimation accuracy with the estimation_register_size parameter
Source code in classiq/interface/generator/amplitude_estimation.py
class AmplitudeEstimation(FunctionParams):
"""
Creates a quantum circuit for amplitude estimation
Provide the state preparation and oracle within the GroverOperator parameter
Choose estimation accuracy with the estimation_register_size parameter
"""
grover_operator: GroverOperator = pydantic.Field(
description="The Grover Operator used in the algorithm. "
"Composed of the oracle and the state preparation operator."
)
estimation_register_size: pydantic.PositiveInt = pydantic.Field(
description="The number of qubits used to estimate the amplitude. "
"Bigger register provides a better estimate of the good states' amplitude."
)
def _create_ios(self) -> None:
self._inputs = dict()
self._outputs = {
ESTIMATED_AMPLITUDE_OUTPUT_NAME: RegisterArithmeticInfo(
size=self.estimation_register_size
),
**self.grover_operator.outputs,
}
estimation_register_size: PositiveInt
pydantic-field
required
The number of qubits used to estimate the amplitude. Bigger register provides a better estimate of the good states' amplitude.
grover_operator: GroverOperator
pydantic-field
required
The Grover Operator used in the algorithm. Composed of the oracle and the state preparation operator.
amplitude_loading
AmplitudeLoading (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/amplitude_loading.py
class AmplitudeLoading(FunctionParams):
size: pydantic.PositiveInt = pydantic.Field(
description="The number of qubits of the amplitude input."
)
fraction_digits: pydantic.NonNegativeInt = pydantic.Field(
description="The number of fraction digits of the amplitude input."
)
is_signed: bool = pydantic.Field(
description="Whether the amplitude input has a sign qubit."
)
expression: PydanticExpressionStr = pydantic.Field(
description="The mathematical expression of the amplitude loading function."
)
implementation: AmplitudeLoadingImplementation = pydantic.Field(
default=AmplitudeLoadingImplementation.EXPERIMENTAL,
description="Implementation options.",
)
@pydantic.validator("expression", pre=True)
def validate_coefficient(cls, expression: str) -> str:
if isinstance(expression, str):
# We validate the given value is legal and does not contain code that will be executed in our BE.
validate_expression(
expression,
supported_nodes=get_args(GenerationExpressionSupportedNodeTypes),
)
# We only check that this method does not raise any exception to see that it can be converted to sympy
sympy.parse_expr(expression)
if isinstance(expression, sympy.Expr):
return str(expression)
return expression
@pydantic.root_validator()
def check_all_variable_are_defined(cls, values: Dict[str, Any]) -> Dict[str, Any]:
expression = values.get("expression", "")
literals = set(re.findall(SUPPORTED_VAR_NAMES_REG, expression))
not_allowed = literals.intersection(FORBIDDEN_LITERALS) - BOOLEAN_LITERALS
variables = literals.difference(SUPPORTED_FUNC_NAMES) - BOOLEAN_LITERALS
if not_allowed:
raise ClassiqValueError(
f"The following names: {not_allowed} are not allowed"
)
if len(variables) != 1:
raise ClassiqValueError(f"{variables} must contain exactly single variable")
return values
def _create_ios(self) -> None:
self._inputs = {
TARGET_OUTPUT_NAME: RegisterUserInput(name=TARGET_OUTPUT_NAME, size=1),
AMPLITUDE_IO_NAME: RegisterUserInput(
name=AMPLITUDE_IO_NAME, size=self.size
),
}
self._outputs = {
TARGET_OUTPUT_NAME: RegisterUserInput(name=TARGET_OUTPUT_NAME, size=1),
**self._inputs,
}
@property
def variable(self) -> str:
literals = (
set(re.findall(SUPPORTED_VAR_NAMES_REG, self.expression)) - BOOLEAN_LITERALS
)
return list(literals.difference(SUPPORTED_FUNC_NAMES))[0]
expression: ConstrainedStrValue
pydantic-field
required
The mathematical expression of the amplitude loading function.
fraction_digits: NonNegativeInt
pydantic-field
required
The number of fraction digits of the amplitude input.
implementation: AmplitudeLoadingImplementation
pydantic-field
Implementation options.
is_signed: bool
pydantic-field
required
Whether the amplitude input has a sign qubit.
size: PositiveInt
pydantic-field
required
The number of qubits of the amplitude input.
arith
special
arithmetic_expression_validator
ExpressionValidator (NodeVisitor)
Source code in classiq/interface/generator/arith/arithmetic_expression_validator.py
class ExpressionValidator(ast.NodeVisitor):
def __init__(
self,
supported_nodes: Tuple[Type[AST], ...],
expression_type: str = DEFAULT_EXPRESSION_TYPE,
supported_functions: Optional[Set[str]] = None,
mode: str = "eval",
) -> None:
super().__init__()
self.supported_nodes = supported_nodes
self._expression_type = expression_type
self._supported_functions = supported_functions or DEFAULT_SUPPORTED_FUNC_NAMES
self._mode = mode
self._ast_obj: Optional[ast.AST] = None
def validate(self, expression: str) -> None:
try:
adjusted_expression = self._get_adjusted_expression(expression)
ast_expr = ast.parse(adjusted_expression, filename="", mode=self._mode)
except SyntaxError as e:
raise ClassiqValueError(f"Failed to parse expression {expression!r}") from e
try:
self._ast_obj = self.rewrite_ast(ast_expr)
self.visit(self._ast_obj)
except RecursionError as e:
raise ClassiqValueError(
f"Failed to parse expression since it is too long: {expression}"
) from e
@staticmethod
def _get_adjusted_expression(expression: str) -> str:
# This works around the simplification of the trivial expressions such as a + 0, 1 * a, etc.
if IDENITIFIER_REGEX.fullmatch(expression):
return f"0 + {expression}"
return expression
@property
def ast_obj(self) -> ast.AST:
if not self._ast_obj:
raise ClassiqArithmeticError("Must call `validate` before getting ast_obj")
return self._ast_obj
@staticmethod
def _check_repeated_variables(variables: Tuple[Any, Any]) -> None:
if (
all(isinstance(var, ast.Name) for var in variables)
and variables[0].id == variables[1].id
):
raise ClassiqValueError(_REPEATED_VARIABLES_ERROR_MESSAGE)
@staticmethod
def _check_multiple_comparators(node: ast.Compare) -> None:
if len(node.comparators) > 1:
raise ClassiqValueError(
"Arithmetic expression with more than 1 comparator is not supported"
)
def generic_visit(self, node: ast.AST) -> None:
self._validate_node_type(node)
return super().generic_visit(node)
def _validate_node_type(self, node: ast.AST) -> None:
if isinstance(node, self.supported_nodes):
return
raise ClassiqValueError(
f"Invalid {self._expression_type} expression: "
f"{type(node).__name__} is not supported"
)
def validate_Compare(self, node: ast.Compare) -> None: # noqa: N802
self._check_repeated_variables((node.left, node.comparators[0]))
self._check_multiple_comparators(node)
def visit_Compare(self, node: ast.Compare) -> None:
self.validate_Compare(node)
self.generic_visit(node)
def validate_BinOp(self, node: ast.BinOp) -> None: # noqa: N802
self._check_repeated_variables((node.left, node.right))
def visit_BinOp(self, node: ast.BinOp) -> None:
self.validate_BinOp(node)
self.generic_visit(node)
def validate_Call(self, node: ast.Call) -> None: # noqa: N802
if len(node.args) >= 2:
self._check_repeated_variables((node.args[0], node.args[1]))
node_id = AstNodeRewrite().extract_node_id(node)
if node_id not in self._supported_functions:
raise ClassiqValueError(f"{node_id} not in supported functions")
if node_id in ("CLShift", "CRShift") and (
len(node.args) != 2 or not isinstance(node.args[1], ast.Constant)
):
raise ClassiqValueError("Cyclic Shift expects 2 arguments (exp, int)")
def visit_Call(self, node: ast.Call) -> None:
self.validate_Call(node)
self.generic_visit(node)
def validate_Constant(self, node: ast.Constant) -> None: # noqa: N802
if not isinstance(node.value, (int, float, complex, str)):
raise ClassiqValueError(
f"{type(node.value).__name__} literals are not valid in {self._expression_type} expressions"
)
def visit_Constant(self, node: ast.Constant) -> None:
self.validate_Constant(node)
self.generic_visit(node)
def visit_Attribute(self, node: ast.Attribute) -> None:
self.generic_visit(node)
@classmethod
def rewrite_ast(cls, expression_ast: AST) -> AST:
return expression_ast
generic_visit(self, node)
Called if no explicit visitor function exists for a node.
Source code in classiq/interface/generator/arith/arithmetic_expression_validator.py
def generic_visit(self, node: ast.AST) -> None:
self._validate_node_type(node)
return super().generic_visit(node)
ast_node_rewrite
AstNodeRewrite (NodeTransformer)
Source code in classiq/interface/generator/arith/ast_node_rewrite.py
class AstNodeRewrite(ast.NodeTransformer):
def __init__(self) -> None:
super().__init__()
self.count_str_gen = _count_str_gen()
def visit(self, node: ast.AST) -> ast.AST:
new_node = ast.NodeTransformer.visit(self, node=node)
new_node.id = self.extract_node_id(new_node)
return new_node
def extract_node_id(self, node: ast.AST) -> Optional[Union[str, float]]:
if hasattr(node, "id"):
return node.id
elif hasattr(node, "op"):
return type(node.op).__name__ + next(self.count_str_gen)
elif hasattr(node, "func"):
return self.extract_node_id(node.func)
elif hasattr(node, "value"):
return node.value
elif hasattr(node, "ops"):
return type(node.ops[0]).__name__ + next(self.count_str_gen)
return None
def visit_UnaryOp(self, node: ast.UnaryOp) -> Any:
if hasattr(node, OUTPUT_SIZE):
node.operand.output_size = node.output_size # type: ignore[attr-defined]
node = cast(ast.UnaryOp, self.generic_visit(node))
if isinstance(node.op, ast.UAdd):
return node.operand
elif isinstance(node.op, ast.USub) and isinstance(node.operand, ast.Constant):
return self.visit(ast.Constant(value=-node.operand.value))
return node
def visit_BinOp(self, node: ast.BinOp) -> Any:
if hasattr(node, OUTPUT_SIZE):
node.left.output_size = node.output_size # type: ignore[attr-defined]
node.right.output_size = node.output_size # type: ignore[attr-defined]
node = cast(ast.BinOp, self.generic_visit(node))
if isinstance(node.op, ast.Mod):
if not isinstance(node.right, ast.Constant) or isinstance(
node.left, ast.Constant
):
raise ClassiqArithmeticError(
"Modulo must be between a variable and a constant"
)
value = node.right.value
is_power_2 = value > 0 and (value & (value - 1) == 0)
if not is_power_2:
raise ClassiqArithmeticError(NOT_POWER_OF_TWO_ERROR_MSG)
if not isinstance(node.left, ast.Name):
node.left.output_size = node.right.value.bit_length() - 1 # type: ignore[attr-defined]
return node.left
return node
visit(self, node)
Visit a node.
Source code in classiq/interface/generator/arith/ast_node_rewrite.py
def visit(self, node: ast.AST) -> ast.AST:
new_node = ast.NodeTransformer.visit(self, node=node)
new_node.id = self.extract_node_id(new_node)
return new_node
number_utils
signed_int_to_unsigned(number)
Return the integer value of a signed int if it would we read as un-signed in binary representation
Source code in classiq/interface/generator/arith/number_utils.py
def signed_int_to_unsigned(number: int) -> int:
"""Return the integer value of a signed int if it would we read as un-signed in binary representation"""
if number >= 0:
return number
not_power2 = abs(number) & (abs(number) - 1) != 0
return number + 2 ** (number.bit_length() + 1 * not_power2)
chemistry_function_params
ChemistryFunctionParams (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/chemistry_function_params.py
class ChemistryFunctionParams(FunctionParams):
gs_problem: CHEMISTRY_PROBLEMS_TYPE
@pydantic.validator("gs_problem")
def validate_gs_problem_contains_num_qubits(
cls, gs_problem: CHEMISTRY_PROBLEMS_TYPE
) -> CHEMISTRY_PROBLEMS_TYPE:
if not gs_problem.num_qubits:
raise ClassiqValueError(
"Ground state problem doesn't contain num_qubits. "
"Use update_problem method."
)
return gs_problem
@property
def num_qubits(self) -> int:
assert isinstance(
self.gs_problem, GroundStateProblem
), "self.gs_problem is not from GroundStateProblem class"
assert isinstance(self.gs_problem.num_qubits, int)
return self.gs_problem.num_qubits
def _create_ios(self) -> None:
self._inputs = {
DEFAULT_INPUT_NAME: RegisterUserInput(
name=DEFAULT_INPUT_NAME, size=self.num_qubits
)
}
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterUserInput(
name=DEFAULT_OUTPUT_NAME, size=self.num_qubits
)
}
gs_problem: Annotated[Union[classiq.interface.chemistry.ground_state_problem.MoleculeProblem, classiq.interface.chemistry.ground_state_problem.HamiltonianProblem], FieldInfo(default=PydanticUndefined, description='Ground state problem object describing the system.', discriminator='kind', extra={})]
pydantic-field
required
Ground state problem object describing the system.
commuting_pauli_exponentiation
CommutingPauliExponentiation (FunctionParams)
pydantic-model
Exponentiation of a Hermitian Pauli sum operator with commuting pauli strings.
Source code in classiq/interface/generator/commuting_pauli_exponentiation.py
class CommutingPauliExponentiation(FunctionParams):
"""
Exponentiation of a Hermitian Pauli sum operator with commuting pauli strings.
"""
pauli_operator: PauliOperator = pydantic.Field(
description="A weighted sum of Pauli strings."
)
evolution_coefficient: ParameterFloatType = pydantic.Field(
default=1.0,
description="A global coefficient multiplying the operator.",
is_exec_param=True,
)
@pydantic.validator("pauli_operator")
def _validate_is_hermitian(cls, pauli_operator: PauliOperator) -> PauliOperator:
return operator.validate_operator_is_hermitian(pauli_operator)
@pydantic.validator("pauli_operator")
def _validate_paulis_commute(cls, pauli_operator: PauliOperator) -> PauliOperator:
if not pauli_operator.is_commutative:
raise ClassiqValueError("Pauli strings are not commutative")
return pauli_operator
def _create_ios(self) -> None:
size = self.pauli_operator.num_qubits
self._inputs = {
DEFAULT_INPUT_NAME: RegisterUserInput(name=DEFAULT_INPUT_NAME, size=size)
}
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterUserInput(name=DEFAULT_OUTPUT_NAME, size=size)
}
evolution_coefficient: Union[float, str]
pydantic-field
A global coefficient multiplying the operator.
pauli_operator: PauliOperator
pydantic-field
required
A weighted sum of Pauli strings.
control_state
ControlState (BaseModel)
pydantic-model
Source code in classiq/interface/generator/control_state.py
class ControlState(BaseModel):
num_ctrl_qubits: pydantic.PositiveInt = pydantic.Field(
default=_DEFAULT_NUM_CONTROL_QUBITS, description="Number of control qubits"
)
ctrl_state: str = pydantic.Field(
default=_INVALID_CONTROL_STATE, description="Control state string"
)
name: str = pydantic.Field(default=None, description="Control name")
@pydantic.root_validator()
def _validate_control(cls, values: Dict[str, Any]) -> Dict[str, Any]:
num_ctrl_qubits: int = values.get(
"num_ctrl_qubits", _DEFAULT_NUM_CONTROL_QUBITS
)
ctrl_state: str = values.get("ctrl_state", _INVALID_CONTROL_STATE)
if ctrl_state == _INVALID_CONTROL_STATE:
ctrl_state = "1" * num_ctrl_qubits
values["ctrl_state"] = ctrl_state
cls.validate_control_string(ctrl_state)
if num_ctrl_qubits == _DEFAULT_NUM_CONTROL_QUBITS:
num_ctrl_qubits = len(ctrl_state)
values["num_ctrl_qubits"] = num_ctrl_qubits
if len(ctrl_state) != num_ctrl_qubits:
raise ClassiqValueError(
"Control state length should be equal to the number of control qubits"
)
if values.get("name") is None:
values["name"] = f"{_DEFAULT_CONTROL_NAME}_{ctrl_state}"
return values
@staticmethod
def validate_control_string(ctrl_state: str) -> None:
if not set(ctrl_state) <= {"1", "0"}:
raise ClassiqValueError(
f"Control state can only be constructed from 0 and 1, received: {ctrl_state}"
)
if not ctrl_state:
raise ClassiqValueError("Control state cannot be empty")
def __str__(self) -> str:
return self.ctrl_state
def __len__(self) -> int:
return self.num_ctrl_qubits
@property
def control_register(self) -> RegisterUserInput:
return RegisterUserInput(name=self.name, size=self.num_ctrl_qubits)
def rename(self, name: str) -> ControlState:
return ControlState(ctrl_state=self.ctrl_state, name=name)
class Config:
frozen = True
ctrl_state: str
pydantic-field
Control state string
name: str
pydantic-field
Control name
num_ctrl_qubits: PositiveInt
pydantic-field
Number of control qubits
__str__(self)
special
Return str(self).
Source code in classiq/interface/generator/control_state.py
def __str__(self) -> str:
return self.ctrl_state
entangler_params
Entangler (FunctionParams)
pydantic-model
A Father class for all entangler classes
Source code in classiq/interface/generator/entangler_params.py
class Entangler(FunctionParams):
"""
A Father class for all entangler classes
"""
qubit_count: pydantic.PositiveInt = pydantic.Field(
description="The number of qubits for the entangler."
)
schmidt_rank: pydantic.NonNegativeInt = pydantic.Field(
default=0, description="The required schmidt rank (log of schmidt number)."
)
def _create_ios(self) -> None:
self._inputs = {IN_NAME: RegisterUserInput(name=IN_NAME, size=self.qubit_count)}
self._outputs = {
OUT_NAME: RegisterUserInput(name=OUT_NAME, size=self.qubit_count)
}
qubit_count: PositiveInt
pydantic-field
required
The number of qubits for the entangler.
schmidt_rank: NonNegativeInt
pydantic-field
The required schmidt rank (log of schmidt number).
GridEntangler (Entangler)
pydantic-model
creates a graph state in the form of multi-dimensional grid according to the specified number of qubits and Schmidt rank. If possible the grid will include the exact Schmidt rank if not a smaller grid with a lower schmidt rank is constructed - as close as possible to the specified parameters. if the specified Schmidt rank is too high a 'long' grid with the maximal possible Schmidt rank width is constructed (that still obeys the condition that the largest dimension minus 1 is larger then the sum of the (d_i - 1) -- d_i including all other dimensions)
Source code in classiq/interface/generator/entangler_params.py
class GridEntangler(Entangler):
"""
creates a graph state in the form of multi-dimensional grid according to the specified number of qubits and Schmidt
rank. If possible the grid will include the exact Schmidt rank if not a smaller grid with a lower schmidt rank is
constructed - as close as possible to the specified parameters. if the specified Schmidt rank is too high a 'long'
grid with the maximal possible Schmidt rank width is constructed (that still obeys the condition that the largest
dimension minus 1 is larger then the sum of the (d_i - 1) -- d_i including all other dimensions)
"""
grid_randomization: bool = pydantic.Field(
default=True,
description="Boolean determining whether the grid structure is randomly selected out of all grids which provide"
"the same Schmidt rank width. If False the grid with maximal number of dimensions is selected.",
)
filling_factor: PydanticProbabilityFloat = pydantic.Field(
default=1,
description="float determining the fraction of cz gates that are included in a circuit for a given grid "
"structure. For example, for filling_factor=0.5 half of the cz gates required for the full grid structure are "
"included in the output circuit. The cz gates included in the circuit are chosen randomaly.",
)
filling_factor: PydanticProbabilityFloat
pydantic-field
float determining the fraction of cz gates that are included in a circuit for a given grid structure. For example, for filling_factor=0.5 half of the cz gates required for the full grid structure are included in the output circuit. The cz gates included in the circuit are chosen randomaly.
grid_randomization: bool
pydantic-field
Boolean determining whether the grid structure is randomly selected out of all grids which providethe same Schmidt rank width. If False the grid with maximal number of dimensions is selected.
HypercubeEntangler (Entangler)
pydantic-model
Creates a cluster/graph state in the form of a hypercube with the specified number of qubits. The hypercube is constructed by building cubes of growing dimension therefore if the number of qubits is not a a power of 2 (n=2^k) the last cube will not be completed. for example if n = 11 = 2^3 + 3 a three dimensional cube is constructed connected to additional 3 qubits in the natural order (that is, these qubits will be: 1000, 1001, 1010)
Source code in classiq/interface/generator/entangler_params.py
class HypercubeEntangler(Entangler):
"""
Creates a cluster/graph state in the form of a hypercube with the specified number of qubits. The hypercube is
constructed by building cubes of growing dimension therefore if the number of qubits is not a a power of 2 (n=2^k)
the last cube will not be completed. for example if n = 11 = 2^3 + 3 a three dimensional cube is constructed
connected to additional 3 qubits in the natural order
(that is, these qubits will be: 1000, 1001, 1010)
"""
pass
TwoDimensionalEntangler (Entangler)
pydantic-model
Creates a two dimensional cluster state with the specified number of qubits and schmidt rank (log of schmidt number). When the desired schmidt rank is too high, a rectangular grid with schmidt rank floor(sqrt(qubit_count))-1 is generated.
Source code in classiq/interface/generator/entangler_params.py
class TwoDimensionalEntangler(Entangler):
"""
Creates a two dimensional cluster state with the specified number of qubits and schmidt rank
(log of schmidt number). When the desired schmidt rank is too high, a rectangular grid with schmidt rank
floor(sqrt(qubit_count))-1 is generated.
"""
pass
expressions
special
evaluated_expression
EvaluatedExpression
dataclass
EvaluatedExpression(value: Union[int, float, list, bool, classiq.interface.generator.expressions.qmod_struct_instance.QmodStructInstance, classiq.interface.generator.expressions.qmod_sized_proxy.QmodSizedProxy, classiq.interface.generator.expressions.type_proxy.TypeProxy, classiq.interface.generator.expressions.handle_identifier.HandleIdentifier, sympy.core.expr.Expr, sympy.logic.boolalg.Boolean])
Source code in classiq/interface/generator/expressions/evaluated_expression.py
@dataclass(frozen=True)
class EvaluatedExpression:
value: ExpressionValue
def is_constant(self, constant_type: Optional[Type] = None) -> bool:
if self.value is None:
return False
return isinstance(
self.value,
get_args(RuntimeConstant) if constant_type is None else constant_type,
)
def as_constant_type(self, constant_type: Type) -> Any:
if not self.is_constant():
raise ClassiqValueError(
f"Invalid access to expression {self.value!r} as {constant_type}"
)
return constant_type(self.value)
def to_int_value(self) -> int:
return self.as_constant_type(int)
def to_bool_value(self) -> bool:
return self.as_constant_type(bool)
def to_float_value(self) -> float:
return self.as_constant_type(float)
def to_list(self) -> list:
return self.as_constant_type(list)
def to_handle(self) -> HandleIdentifier:
if not isinstance(self.value, HandleIdentifier):
raise ClassiqValueError(
f"Invalid access to expression {self.value} as HandleIdentifier"
)
return self.value
def to_struct_dict(self) -> Mapping[str, Any]:
if not isinstance(self.value, QmodStructInstance):
raise ClassiqValueError(
f"Invalid access to expression {self.value} as SympyStructInstance"
)
return self.value.fields
def as_expression(self) -> str:
if self.value is None:
raise ClassiqValueError("Invalid access to unevaluated expression")
return str(self.value)
def is_identifier(self) -> bool:
return (
isinstance(self.value, Expr)
and re.fullmatch(EXECUTION_PARAMETER_PATTERN, str(self.value)) is not None
)
expression
Expression (HashableASTNode)
pydantic-model
Source code in classiq/interface/generator/expressions/expression.py
class Expression(HashableASTNode):
expr: str
_evaluated_expr: Optional[EvaluatedExpression] = PrivateAttr(default=None)
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._try_to_immediate_evaluate()
@pydantic.validator("expr")
def validate_expression(cls, expr: str) -> str:
supported_functions = (
SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS_QMOD
| set(SYMPY_SUPPORTED_EXPRESSIONS)
| set(DEFAULT_SUPPORTED_FUNC_NAMES)
)
validate_expression_str(expr, supported_functions=supported_functions)
return expr
@pydantic.validator("expr")
def format_expression(cls, expr: str) -> str:
if sys.version_info >= (3, 9):
expr = ast.unparse(ast.parse(expr))
return expr
def is_evaluated(self) -> bool:
return self._evaluated_expr is not None
def as_constant(self, constant_type: Type) -> Any:
return self.value.as_constant_type(constant_type)
def to_int_value(self) -> int:
return self.as_constant(int)
def to_bool_value(self) -> bool:
return self.as_constant(bool)
def to_float_value(self) -> float:
return self.as_constant(float)
def to_struct_dict(self) -> Mapping[str, Any]:
return self.value.to_struct_dict()
def to_list(self) -> list:
return self.as_constant(list)
def _try_to_immediate_evaluate(self) -> None:
try:
result = ast.literal_eval(self.expr)
if isinstance(result, (int, float, bool)):
self._evaluated_expr = EvaluatedExpression(value=result)
except Exception: # noqa: S110
pass
@property
def value(self) -> EvaluatedExpression:
if self._evaluated_expr is None:
raise ClassiqError(f"Trying to access unevaluated value {self.expr}")
return self._evaluated_expr
def as_expression(self) -> str:
return self.value.as_expression()
def is_constant(self) -> bool:
return self.value.is_constant()
class Config:
frozen = True
def __str__(self) -> str:
return self.expr
__str__(self)
special
Return str(self).
Source code in classiq/interface/generator/expressions/expression.py
def __str__(self) -> str:
return self.expr
handle_identifier
HandleIdentifier
dataclass
HandleIdentifier(id: int)
Source code in classiq/interface/generator/expressions/handle_identifier.py
@dataclasses.dataclass(frozen=True)
class HandleIdentifier:
id: int
qmod_qscalar_proxy
QmodQScalarProxy (Symbol, QmodSizedProxy)
Source code in classiq/interface/generator/expressions/qmod_qscalar_proxy.py
class QmodQScalarProxy(Symbol, QmodSizedProxy):
def __new__(cls, handle: HandleBinding, **assumptions: bool) -> "QmodQScalarProxy":
return super().__new__(cls, str(handle), **assumptions)
def __init__(self, handle: HandleBinding, size: int) -> None:
super().__init__(handle, size)
__new__(cls, handle, **assumptions)
special
staticmethod
Symbols are identified by name and assumptions::
from sympy import Symbol Symbol("x") == Symbol("x") True Symbol("x", real=True) == Symbol("x", real=False) False
Source code in classiq/interface/generator/expressions/qmod_qscalar_proxy.py
def __new__(cls, handle: HandleBinding, **assumptions: bool) -> "QmodQScalarProxy":
return super().__new__(cls, str(handle), **assumptions)
finance
Finance (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/finance.py
class Finance(function_params.FunctionParams):
model: Union[GaussianModelInput, LogNormalModelInput] = pydantic.Field(
description="Load a financial model", discriminator="kind"
)
finance_function: FinanceFunctionInput = pydantic.Field(
description="The finance function to solve the model"
)
def _create_ios(self) -> None:
finance_model = FinanceModels(model=self.model)
# 1 for the objective qubit
function_size = sum(
reg.size for reg in finance_model._outputs.values() if reg is not None
)
self._inputs = {
FUNCTION_INPUT_NAME: RegisterUserInput(
name=FUNCTION_INPUT_NAME, size=function_size
),
OBJECTIVE_INPUT_NAME: RegisterUserInput(name=OBJECTIVE_INPUT_NAME, size=1),
}
self._outputs = {
FUNCTION_OUTPUT_NAME: RegisterUserInput(
name=FUNCTION_OUTPUT_NAME, size=function_size
),
OBJECTIVE_OUTPUT_NAME: RegisterUserInput(
name=OBJECTIVE_OUTPUT_NAME, size=1
),
}
finance_function: FinanceFunctionInput
pydantic-field
required
The finance function to solve the model
model: Union[classiq.interface.finance.gaussian_model_input.GaussianModelInput, classiq.interface.finance.log_normal_model_input.LogNormalModelInput]
pydantic-field
required
Load a financial model
FinanceModels (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/finance.py
class FinanceModels(function_params.FunctionParams):
model: Union[GaussianModelInput, LogNormalModelInput] = pydantic.Field(
description="Load a financial model"
)
def _create_ios(self) -> None:
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterUserInput(
name=DEFAULT_OUTPUT_NAME, size=self.model.num_output_qubits
)
}
if isinstance(self.model, GaussianModelInput):
self._inputs = {
DEFAULT_INPUT_NAME: RegisterUserInput(
name=DEFAULT_INPUT_NAME, size=self.model.num_bernoulli_qubits
)
}
self._create_zero_input_registers(
{DEFAULT_ZERO_NAME: self.model.num_output_qubits}
)
self._outputs[DEFAULT_BERNOULLI_OUTPUT_NAME] = RegisterUserInput(
name=DEFAULT_BERNOULLI_OUTPUT_NAME,
size=self.model.num_bernoulli_qubits,
)
elif isinstance(self.model, LogNormalModelInput):
self._inputs = {
DEFAULT_INPUT_NAME: RegisterUserInput(
name=DEFAULT_INPUT_NAME, size=self.model.num_model_qubits
)
}
model: Union[classiq.interface.finance.gaussian_model_input.GaussianModelInput, classiq.interface.finance.log_normal_model_input.LogNormalModelInput]
pydantic-field
required
Load a financial model
FinancePayoff (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/finance.py
class FinancePayoff(function_params.FunctionParams):
finance_function: FinanceFunctionInput = pydantic.Field(
description="The finance function to solve the model"
)
num_qubits: pydantic.PositiveInt
distribution_range: Tuple[float, float]
def _create_ios(self) -> None:
self._inputs = {
DEFAULT_INPUT_NAME: RegisterUserInput(
name=DEFAULT_INPUT_NAME, size=self.num_qubits
)
}
self._create_zero_input_registers({DEFAULT_ZERO_NAME: 1})
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterUserInput(name=DEFAULT_OUTPUT_NAME, size=1),
DEFAULT_POST_INPUT_NAME: RegisterUserInput(
name=DEFAULT_INPUT_NAME, size=self.num_qubits
),
}
finance_function: FinanceFunctionInput
pydantic-field
required
The finance function to solve the model
functions
special
classical_function_declaration
ClassicalFunctionDeclaration (FunctionDeclaration)
pydantic-model
Facilitates the creation of a common classical function interface object.
Source code in classiq/interface/generator/functions/classical_function_declaration.py
class ClassicalFunctionDeclaration(FunctionDeclaration):
"""
Facilitates the creation of a common classical function interface object.
"""
return_type: Optional[ConcreteClassicalType] = pydantic.Field(
description="The type of the classical value that is returned by the function (for classical functions)",
default=None,
)
BUILTIN_FUNCTION_DECLARATIONS: ClassVar[
Dict[str, "ClassicalFunctionDeclaration"]
] = {}
FOREIGN_FUNCTION_DECLARATIONS: ClassVar[
Dict[str, "ClassicalFunctionDeclaration"]
] = {}
def update_logic_flow(
self, function_dict: Mapping[str, "ClassicalFunctionDeclaration"]
) -> None:
pass
return_type: Annotated[Union[classiq.interface.generator.functions.classical_type.Integer, classiq.interface.generator.functions.classical_type.Real, classiq.interface.generator.functions.classical_type.Bool, classiq.interface.generator.functions.classical_type.ClassicalList, classiq.interface.generator.functions.classical_type.Pauli, classiq.interface.generator.functions.classical_type.StructMetaType, classiq.interface.generator.functions.classical_type.TypeName, classiq.interface.generator.functions.classical_type.ClassicalArray, classiq.interface.generator.functions.classical_type.VQEResult, classiq.interface.generator.functions.classical_type.Histogram, classiq.interface.generator.functions.classical_type.Estimation, classiq.interface.generator.functions.classical_type.LadderOperator, classiq.interface.generator.functions.classical_type.IQAERes], FieldInfo(default=PydanticUndefined, discriminator='kind', extra={})]
pydantic-field
The type of the classical value that is returned by the function (for classical functions)
classical_type
ClassicalType (HashableASTNode)
pydantic-model
Source code in classiq/interface/generator/functions/classical_type.py
class ClassicalType(HashableASTNode):
def as_symbolic(self, name: str) -> Union[NamedSymbol, List[NamedSymbol]]:
return Symbol(name)
@property
def qmod_type(self) -> type:
raise NotImplementedError(
f"{self.__class__.__name__!r} has no QMOD SDK equivalent"
)
class Config:
extra = Extra.forbid
def __str__(self) -> str:
return str(type(self).__name__)
__str__(self)
special
Return str(self).
Source code in classiq/interface/generator/functions/classical_type.py
def __str__(self) -> str:
return str(type(self).__name__)
TypeName (ClassicalType)
pydantic-model
Source code in classiq/interface/generator/functions/classical_type.py
class TypeName(ClassicalType):
kind: Literal["struct_instance"]
name: str = pydantic.Field(description="The type name of the instance")
@pydantic.root_validator(pre=True)
def _set_kind(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(values, "kind", "struct_instance")
@property
def qmod_type(self) -> type:
return type(self.name, (TypeName,), dict())
name: str
pydantic-field
required
The type name of the instance
function_declaration
FunctionDeclaration (ASTNode, ABC)
pydantic-model
Facilitates the creation of a common function interface object.
Source code in classiq/interface/generator/functions/function_declaration.py
class FunctionDeclaration(ASTNode, abc.ABC):
"""
Facilitates the creation of a common function interface object.
"""
name: str = pydantic.Field(description="The name of the function")
param_decls: Dict[str, ConcreteClassicalType] = pydantic.Field(
description="The expected interface of the functions parameters",
default_factory=dict,
)
class Config:
extra = pydantic.Extra.forbid
name: str
pydantic-field
required
The name of the function
param_decls: Dict[str, Annotated[Union[classiq.interface.generator.functions.classical_type.Integer, classiq.interface.generator.functions.classical_type.Real, classiq.interface.generator.functions.classical_type.Bool, classiq.interface.generator.functions.classical_type.ClassicalList, classiq.interface.generator.functions.classical_type.Pauli, classiq.interface.generator.functions.classical_type.StructMetaType, classiq.interface.generator.functions.classical_type.TypeName, classiq.interface.generator.functions.classical_type.ClassicalArray, classiq.interface.generator.functions.classical_type.VQEResult, classiq.interface.generator.functions.classical_type.Histogram, classiq.interface.generator.functions.classical_type.Estimation, classiq.interface.generator.functions.classical_type.LadderOperator, classiq.interface.generator.functions.classical_type.IQAERes], FieldInfo(default=PydanticUndefined, discriminator='kind', extra={})]]
pydantic-field
The expected interface of the functions parameters
grover_diffuser
GroverDiffuser (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/grover_diffuser.py
class GroverDiffuser(FunctionParams):
variables: List[RegisterUserInput]
state_preparation: str = pydantic.Field(
default="", description="State preparation function"
)
state_preparation_params: GroverStatePreparation = pydantic.Field(
description="State preparation function parameters",
default_factory=CustomFunction,
)
def _create_ios(self) -> None:
self._inputs = {reg.name: reg for reg in self.variables}
self._outputs = {reg.name: reg for reg in self.variables}
@pydantic.root_validator(pre=True)
def _validate_state_preparation_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
if isinstance(
values.get("state_preparation_params"), CustomFunction
) and not values.get("state_preparation"):
raise ClassiqValueError(
"Must receive the function name from the `state_preparation` field for user defined functions"
)
return values
@pydantic.root_validator(pre=True)
def _parse_state_preparation(cls, values: Dict[str, Any]) -> Dict[str, Any]:
parse_function_params_values(
values=values,
params_key="state_preparation_params",
discriminator_key="state_preparation",
param_classes={StatePreparation, CustomFunction},
default_parser_class=CustomFunction,
)
return values
@pydantic.validator("variables")
def _validate_variables(
cls, variables: List[RegisterUserInput]
) -> List[RegisterUserInput]:
names = {reg.name for reg in variables}
assert len(variables) == len(names), "Repeating names not allowed"
return variables
@pydantic.validator("state_preparation_params")
def _validate_state_preparation(
cls, state_preparation_params: GroverStatePreparation, values: Dict[str, Any]
) -> GroverStatePreparation:
variables = values.get("variables", list())
sp_inputs = state_preparation_params.inputs_full(strict_zero_ios=False)
sp_outputs = state_preparation_params.outputs
if len(sp_inputs) == 1 and len(sp_outputs) == 1:
var_size = sum(reg.size for reg in variables)
assert (
state_preparation_params.num_input_qubits(strict_zero_ios=False)
== var_size
)
assert state_preparation_params.num_output_qubits == var_size
else:
variable_names_and_sizes = cls._names_and_sizes(
{var.name: var for var in variables}
)
assert cls._names_and_sizes(sp_inputs) == variable_names_and_sizes
assert cls._names_and_sizes(sp_outputs) == variable_names_and_sizes
return state_preparation_params
@staticmethod
def _names_and_sizes(transputs: ArithmeticIODict) -> Set[Tuple[str, int]]:
return {(name, reg.size) for name, reg in transputs.items()}
state_preparation: str
pydantic-field
State preparation function
state_preparation_params: Union[classiq.interface.generator.state_preparation.state_preparation.StatePreparation, classiq.interface.generator.user_defined_function_params.CustomFunction]
pydantic-field
State preparation function parameters
grover_operator
GroverOperator (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/grover_operator.py
class GroverOperator(FunctionParams):
oracle: str = pydantic.Field(
default=_DEFAULT_ORACLE_DISCRIMINATOR, description="Oracle function"
)
oracle_params: OracleABC = pydantic.Field(description="Oracle function parameters")
state_preparation: str = pydantic.Field(
default="", description="State preparation function"
)
state_preparation_params: GroverStatePreparation = pydantic.Field(
default=None, description="State preparation function parameters"
)
def _create_ios(self) -> None:
self._inputs = {**self.oracle_params.inputs}
self._outputs = {**self.oracle_params.outputs}
@pydantic.root_validator(pre=True)
def _parse_oracle(cls, values: Dict[str, Any]) -> Dict[str, Any]:
oracle_params = values.get("oracle_params")
if isinstance(oracle_params, dict):
values["oracle_params"] = parse_function_params(
params=oracle_params,
discriminator=values.get("oracle", _DEFAULT_ORACLE_DISCRIMINATOR),
param_classes=oracle_function_param_library.param_list,
no_discriminator_error=ClassiqValueError("Invalid oracle name"),
bad_function_error=ClassiqValueError("Invalid oracle params"),
)
elif isinstance(oracle_params, FunctionParams):
values["oracle"] = oracle_params.discriminator()
else:
raise ClassiqValueError("Invalid oracle params")
return values
@pydantic.validator("state_preparation_params", always=True)
def _validate_state_preparation(
cls,
state_preparation_params: Optional[GroverStatePreparation],
values: Dict[str, Any],
) -> GroverStatePreparation:
oracle = values.get("oracle_params")
assert oracle is not None, "Must receive an oracle"
state_preparation_params = (
state_preparation_params
or cls._default_state_preparation_params(
num_qubits=oracle.num_input_qubits(strict_zero_ios=True)
)
)
assert GroverDiffuser(
state_preparation_params=state_preparation_params,
state_preparation=values.get("state_preparation", ""),
variables=oracle.variables(),
), "Cannot construct a GroverDiffuser"
return state_preparation_params
@staticmethod
def _default_state_preparation_params(num_qubits: int) -> StatePreparation:
num_states: int = 2**num_qubits
return StatePreparation(
probabilities=[1.0 / float(num_states)] * num_states,
error_metric={
Metrics.L2: NonNegativeFloatRange(lower_bound=0.0, upper_bound=0.0)
},
)
def get_diffuser(self) -> GroverDiffuser:
return GroverDiffuser(
variables=self.oracle_params.variables(),
state_preparation=self.state_preparation,
state_preparation_params=self.state_preparation_params,
)
oracle: str
pydantic-field
Oracle function
oracle_params: OracleABC
pydantic-field
required
Oracle function parameters
state_preparation: str
pydantic-field
State preparation function
state_preparation_params: Union[classiq.interface.generator.state_preparation.state_preparation.StatePreparation, classiq.interface.generator.user_defined_function_params.CustomFunction]
pydantic-field
State preparation function parameters
hamiltonian_evolution
special
exponentiation
Exponentiation (HamiltonianEvolution)
pydantic-model
Exponentiation of a Hermitian Pauli sum operator.
Source code in classiq/interface/generator/hamiltonian_evolution/exponentiation.py
class Exponentiation(HamiltonianEvolution):
"""
Exponentiation of a Hermitian Pauli sum operator.
"""
evolution_coefficient: float = pydantic.Field(
default=1.0, description="A global coefficient multiplying the operator."
)
constraints: ExponentiationConstraints = pydantic.Field(
default_factory=ExponentiationConstraints,
description="Constraints for the exponentiation.",
)
optimization: ExponentiationOptimization = pydantic.Field(
default=ExponentiationOptimization.MINIMIZE_DEPTH,
description="What attribute to optimize.",
)
@pydantic.validator("pauli_operator")
def _validate_is_hermitian(cls, pauli_operator: PauliOperator) -> PauliOperator:
return operator.validate_operator_is_hermitian(pauli_operator)
constraints: ExponentiationConstraints
pydantic-field
Constraints for the exponentiation.
evolution_coefficient: float
pydantic-field
A global coefficient multiplying the operator.
optimization: ExponentiationOptimization
pydantic-field
What attribute to optimize.
ExponentiationConstraints (BaseModel)
pydantic-model
Source code in classiq/interface/generator/hamiltonian_evolution/exponentiation.py
class ExponentiationConstraints(pydantic.BaseModel):
max_depth: Optional[pydantic.PositiveInt] = pydantic.Field(
default=None, description="Maximum depth of the exponentiation circuit."
)
max_error: Optional[pydantic.PositiveFloat] = pydantic.Field(
default=None,
description="Maximum approximation error of the exponentiation circuit.",
)
class Config:
frozen = True
max_depth: PositiveInt
pydantic-field
Maximum depth of the exponentiation circuit.
max_error: PositiveFloat
pydantic-field
Maximum approximation error of the exponentiation circuit.
hamiltonian_evolution
HamiltonianEvolution (FunctionParams, ABC)
pydantic-model
Suzuki trotterization of a Hermitian operator
Source code in classiq/interface/generator/hamiltonian_evolution/hamiltonian_evolution.py
class HamiltonianEvolution(FunctionParams, ABC):
"""
Suzuki trotterization of a Hermitian operator
"""
pauli_operator: PauliOperator = pydantic.Field(
description="A weighted sum of Pauli strings."
)
use_naive_evolution: bool = pydantic.Field(
default=False, description="Whether to evolve the operator naively."
)
def _create_ios(self) -> None:
self._inputs = {
DEFAULT_INPUT_NAME: RegisterArithmeticInfo(
size=self.pauli_operator.num_qubits
)
}
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterArithmeticInfo(
size=self.pauli_operator.num_qubits
)
}
pauli_operator: PauliOperator
pydantic-field
required
A weighted sum of Pauli strings.
use_naive_evolution: bool
pydantic-field
Whether to evolve the operator naively.
qdrift
QDrift (HamiltonianEvolution)
pydantic-model
qDrift trotterization of a Hermitian operator; see https://arxiv.org/abs/1811.08017
Source code in classiq/interface/generator/hamiltonian_evolution/qdrift.py
class QDrift(HamiltonianEvolution):
"""
qDrift trotterization of a Hermitian operator; see https://arxiv.org/abs/1811.08017
"""
evolution_coefficient: ParameterFloatType = pydantic.Field(
default=1.0,
description="A global coefficient multiplying the operator.",
is_exec_param=True,
)
num_qdrift: pydantic.PositiveInt = pydantic.Field(
description="The number of elements in the qDrift product.",
)
@pydantic.validator("pauli_operator")
def _validate_is_hermitian(cls, pauli_operator: PauliOperator) -> PauliOperator:
return operator.validate_operator_is_hermitian(pauli_operator)
evolution_coefficient: Union[float, str]
pydantic-field
A global coefficient multiplying the operator.
num_qdrift: PositiveInt
pydantic-field
required
The number of elements in the qDrift product.
suzuki_trotter
SuzukiParameters (BaseModel)
pydantic-model
Source code in classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py
class SuzukiParameters(pydantic.BaseModel):
order: pydantic.PositiveInt = pydantic.Field(
default=1,
description="The order of the Suzuki-Trotter. Supports only order equals to 1 or an even number",
)
repetitions: pydantic.NonNegativeInt = pydantic.Field(
default=1, description="The number of repetitions in the Suzuki-Trotter"
)
@pydantic.validator("order")
def _validate_order(cls, order: int) -> int:
if order != 1 and order % 2:
raise ClassiqValueError(
f"Odd order greater than 1 is not supported. Got {order}"
)
return order
class Config:
frozen = True
order: PositiveInt
pydantic-field
The order of the Suzuki-Trotter. Supports only order equals to 1 or an even number
repetitions: NonNegativeInt
pydantic-field
The number of repetitions in the Suzuki-Trotter
SuzukiTrotter (HamiltonianEvolution)
pydantic-model
Suzuki trotterization of a Hermitian operator
Source code in classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py
class SuzukiTrotter(HamiltonianEvolution):
"""
Suzuki trotterization of a Hermitian operator
"""
evolution_coefficient: ParameterFloatType = pydantic.Field(
default=1.0,
description="A global coefficient multiplying the operator.",
is_exec_param=True,
)
suzuki_parameters: SuzukiParameters = pydantic.Field(
default_factory=SuzukiParameters, description="The Suziki parameters."
)
disable_scheduling: bool = pydantic.Field(
default=False, description="Whether to disable the reordering of Pauli terms."
)
@pydantic.validator("pauli_operator")
def _validate_no_complex_coefficients(
cls, pauli_operator: PauliOperator
) -> PauliOperator:
return operator.validate_operator_has_no_complex_coefficients(pauli_operator)
disable_scheduling: bool
pydantic-field
Whether to disable the reordering of Pauli terms.
evolution_coefficient: Union[float, str]
pydantic-field
A global coefficient multiplying the operator.
suzuki_parameters: SuzukiParameters
pydantic-field
The Suziki parameters.
hardware
special
hardware_data
HardwareData (BaseModel)
pydantic-model
Source code in classiq/interface/generator/hardware/hardware_data.py
class HardwareData(pydantic.BaseModel):
basis_gates: List[str] = pydantic.Field(
default=list(),
description="The basis gates of the hardware. "
"This set will be used during the model optimization. "
"If none given, use default values: "
f"If no connectivity map is given or the connectivity map is symmetric - {sorted(DEFAULT_BASIS_GATES)}. "
f"If a non-symmetric connectivity map is given - {sorted(DEFAULT_ROUTING_BASIS_GATES)}. ",
)
connectivity_map: Optional[ConnectivityMap] = pydantic.Field(
default=None,
description="Qubit connectivity map, in the form [ [q0, q1], [q1, q2],...]. "
"If none given, assume the hardware is fully connected",
)
is_symmetric_connectivity: bool = pydantic.Field(
default=True,
description="Assumes that the coupling map forms an undirected graph, "
"so for every qubit pair [q0, q1], both qubits can act as control and target. "
"If false, the first / second qubit denotes the control / target, respectively",
)
@pydantic.validator("connectivity_map")
def _validate_connectivity_map(
cls, connectivity_map: Optional[ConnectivityMap]
) -> Optional[ConnectivityMap]:
if connectivity_map is None:
return connectivity_map
if not connectivity_map:
raise ClassiqValueError("Connectivity map cannot be empty")
connectivity_map = _reindex_qubits(connectivity_map)
return connectivity_map
@pydantic.root_validator()
def _symmetrize_connectivity_map(cls, values: Dict[str, Any]) -> Dict[str, Any]:
connectivity_map = values.get("connectivity_map")
if connectivity_map is None:
return values
is_symmetric = values.get("is_symmetric_connectivity")
if is_symmetric:
connectivity_map = _symmetrize_connectivity_map(connectivity_map)
values["connectivity_map"] = connectivity_map
if not _is_connected_map(connectivity_map):
raise ClassiqValueError(
f"Connectivity map must be connected: {connectivity_map} is not connected."
)
return values
@pydantic.root_validator()
def _validate_basis_gates(cls, values: Dict[str, Any]) -> Dict[str, Any]:
connectivity_map = values.get("connectivity_map")
specified_basis_gates = values.get("basis_gates", [])
if connectivity_map is None:
values["basis_gates"] = specified_basis_gates or list(DEFAULT_BASIS_GATES)
return values
is_symmetric_connectivity = values.get("is_symmetric")
if is_symmetric_connectivity or _check_symmetry(connectivity_map):
values["basis_gates"] = specified_basis_gates or list(DEFAULT_BASIS_GATES)
return values
values["basis_gates"] = specified_basis_gates or list(
DEFAULT_ROUTING_BASIS_GATES
)
invalid_gates = [
gate
for gate in specified_basis_gates
if gate in TWO_QUBIT_GATES and gate not in ROUTING_TWO_QUBIT_BASIS_GATES
]
if invalid_gates:
raise ClassiqValueError(
"Connectivity-aware synthesis with non-symmetric coupling map "
"is currently supported for the following two-qubit gates only: cx, ecr, rzx."
)
return values
basis_gates: List[str]
pydantic-field
The basis gates of the hardware. This set will be used during the model optimization. If none given, use default values: If no connectivity map is given or the connectivity map is symmetric - ['cx', 'cy', 'cz', 'h', 'id', 'p', 'r', 'rx', 'ry', 'rz', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'u', 'u1', 'u2', 'x', 'y', 'z']. If a non-symmetric connectivity map is given - ['cx', 'h', 'id', 'p', 'r', 'rx', 'ry', 'rz', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'u', 'u1', 'u2', 'x', 'y', 'z'].
connectivity_map: List[types.ConstrainedListValue]
pydantic-field
Qubit connectivity map, in the form [ [q0, q1], [q1, q2],...]. If none given, assume the hardware is fully connected
is_symmetric_connectivity: bool
pydantic-field
Assumes that the coupling map forms an undirected graph, so for every qubit pair [q0, q1], both qubits can act as control and target. If false, the first / second qubit denotes the control / target, respectively
hardware_efficient_ansatz
HardwareEfficientAnsatz (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/hardware_efficient_ansatz.py
class HardwareEfficientAnsatz(function_params.FunctionParams):
connectivity_map: ConnectivityMapType = pydantic.Field(
default=None,
description="Hardware's connectivity map, in the form [ [x0, x1], [x1, x2],...]. "
"If none specified - use connectivity map from the model hardware settings. "
"If none specified as well, all qubit pairs will be connected.",
)
num_qubits: pydantic.PositiveInt = pydantic.Field(
default=None,
description="Number of qubits in the ansatz.",
)
reps: pydantic.PositiveInt = pydantic.Field(
default=1, description="Number of layers in the Ansatz"
)
one_qubit_gates: Union[str, List[str]] = pydantic.Field(
default=["x", "ry"],
description='List of gates for the one qubit gates layer, e.g. ["x", "ry"]',
)
two_qubit_gates: Union[str, List[str]] = pydantic.Field(
default=["cx"],
description='List of gates for the two qubit gates entangling layer, e.g. ["cx", "cry"]',
)
parameter_prefix: str = pydantic.Field(
default="param_",
description="Prefix for the generated parameters",
)
@pydantic.validator("num_qubits", pre=True, always=True)
def validate_num_qubits(
cls, num_qubits: Optional[pydantic.PositiveInt], values: Dict[str, Any]
) -> pydantic.PositiveInt:
connectivity_map = values.get("connectivity_map")
conn_map_is_not_list = (
isinstance(connectivity_map, SupportedConnectivityMaps)
or connectivity_map is None
)
if num_qubits is None and conn_map_is_not_list:
raise ClassiqValueError(_NUM_QUBITS_NOT_PROVIDED_ERROR)
if num_qubits is None:
if conn_map_is_not_list:
raise ValueError(_NUM_QUBITS_NOT_PROVIDED_ERROR)
if TYPE_CHECKING:
assert connectivity_map is not None
return len(set(itertools.chain.from_iterable(connectivity_map)))
if conn_map_is_not_list:
return num_qubits
if TYPE_CHECKING:
assert connectivity_map is not None
invalid_qubits = {
qubit
for qubit in itertools.chain.from_iterable(connectivity_map)
if qubit >= num_qubits
}
if invalid_qubits:
raise ClassiqValueError(
f"Invalid qubits: {invalid_qubits} "
f"out of range specified by num_qubits: [0, {num_qubits - 1}]"
)
return num_qubits
@pydantic.validator("one_qubit_gates")
def validate_one_qubit_gates(
cls, one_qubit_gates: Union[str, List[str]]
) -> Union[str, List[str]]:
one_qubit_gates_list = (
[one_qubit_gates] if isinstance(one_qubit_gates, str) else one_qubit_gates
)
for one_qubit_gate in one_qubit_gates_list:
if one_qubit_gate not in SINGLE_QUBIT_GATES:
raise ClassiqValueError(f"Invalid one qubit gate: {one_qubit_gate}")
return one_qubit_gates
@pydantic.validator("two_qubit_gates")
def validate_two_qubit_gates(
cls, two_qubit_gates: Union[str, List[str]]
) -> Union[str, List[str]]:
two_qubit_gates_list = (
[two_qubit_gates] if isinstance(two_qubit_gates, str) else two_qubit_gates
)
for two_qubit_gate in two_qubit_gates_list:
if two_qubit_gate not in TWO_QUBIT_GATES:
raise ClassiqValueError(f"Invalid two qubit gate: {two_qubit_gate}")
return two_qubit_gates
def _create_ios(self) -> None:
self._inputs = {
DEFAULT_INPUT_NAME: RegisterUserInput(
name=DEFAULT_INPUT_NAME, size=self.num_qubits
)
}
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterUserInput(
name=DEFAULT_OUTPUT_NAME, size=self.num_qubits
)
}
connectivity_map: Union[List[types.ConstrainedListValue], classiq.interface.generator.hardware_efficient_ansatz.SupportedConnectivityMaps]
pydantic-field
Hardware's connectivity map, in the form [ [x0, x1], [x1, x2],...]. If none specified - use connectivity map from the model hardware settings. If none specified as well, all qubit pairs will be connected.
num_qubits: PositiveInt
pydantic-field
Number of qubits in the ansatz.
one_qubit_gates: Union[str, List[str]]
pydantic-field
List of gates for the one qubit gates layer, e.g. ["x", "ry"]
parameter_prefix: str
pydantic-field
Prefix for the generated parameters
reps: PositiveInt
pydantic-field
Number of layers in the Ansatz
two_qubit_gates: Union[str, List[str]]
pydantic-field
List of gates for the two qubit gates entangling layer, e.g. ["cx", "cry"]
hva
HVA (ChemistryFunctionParams)
pydantic-model
Hamiltonian Variational Ansatz
Source code in classiq/interface/generator/hva.py
class HVA(ChemistryFunctionParams):
"""
Hamiltonian Variational Ansatz
"""
reps: pydantic.PositiveInt = pydantic.Field(
default=1, description="Number of layers in the Ansatz"
)
use_naive_evolution: bool = pydantic.Field(
default=False, description="Whether to evolve the operator naively"
)
parameter_prefix: str = pydantic.Field(
default="param_",
description="Prefix for the generated parameters",
)
parameter_prefix: str
pydantic-field
Prefix for the generated parameters
reps: PositiveInt
pydantic-field
Number of layers in the Ansatz
use_naive_evolution: bool
pydantic-field
Whether to evolve the operator naively
identity
Identity (FunctionParams)
pydantic-model
Source code in classiq/interface/generator/identity.py
class Identity(FunctionParams):
arguments: NonEmptyRegisterUserInputList = pydantic.Field(
description="registers describing the state (ordered)"
)
@pydantic.validator("arguments")
def _validate_argument_names(
cls, arguments: List[RegisterUserInput]
) -> List[RegisterUserInput]:
return [
arg if arg.name else arg.revalued(name=cls._get_default_arg_name(index))
for index, arg in enumerate(arguments)
]
def _create_ios(self) -> None:
self._inputs = {arg.name: arg for arg in self.arguments}
self._outputs = {arg.name: arg for arg in self.arguments}
@staticmethod
def _get_default_arg_name(index: int) -> str:
return f"arg_{index}"
def get_power_order(self) -> int:
return 1
arguments: ConstrainedListValue
pydantic-field
required
registers describing the state (ordered)
linear_pauli_rotations
LinearPauliRotations (FunctionParams)
pydantic-model
Perform independent linear rotations on target qubits, each controlled by an identical n-qubit state register |x>.
Each target qubit, indexed with k and denoted by q_k, undergoes the following transformation: |x>|q_k> -> |x> * [cos(theta(x,k)/2) + isin(theta(x,k)/2)sigma]|q_k> with sigma being 'X', 'Y' or 'Z' Pauli matrix, and the angle is a linear function of the state, theta(x,k)/2 = (slope(k)*x + offset(k))/2.
For example, a 'Y' rotation on one target qubit will result in a circuit implementing the following logic: |x>|0> -> cos((slopex + offset)/2)|x>|0> + sin((slopex + offset)/2)|x>|1>
!!! q_0 "─────────────────────────■───────── ... ──────────────────────"
│
.
│
q_(n-1): ─────────────────────────┼───────── ... ───────────■──────────
┌────────────┐ ┌───────┴───────┐ ┌─────────┴─────────┐
!!! target "─┤ RY(offset) ├──┤ RY(2^0 slope) ├ ... ┤ RY(2^(n-1) slope) ├"
└────────────┘ └───────────────┘ └───────────────────┘
Source code in classiq/interface/generator/linear_pauli_rotations.py
class LinearPauliRotations(function_params.FunctionParams):
"""
Perform independent linear rotations on target qubits, each controlled by an identical
n-qubit state register |x>.
Each target qubit, indexed with k and denoted by q_k, undergoes the following transformation:
|x>|q_k> -> |x> * [cos(theta(x,k)/2) + i*sin(theta(x,k)/2)*sigma]|q_k>
with sigma being 'X', 'Y' or 'Z' Pauli matrix, and the angle is a linear function of the state,
theta(x,k)/2 = (slope(k)*x + offset(k))/2.
For example, a 'Y' rotation on one target qubit will result in a circuit implementing the following logic:
|x>|0> -> cos((slope*x + offset)/2)|x>|0> + sin((slope*x + offset)/2)|x>|1>
q_0: ─────────────────────────■───────── ... ──────────────────────
│
.
│
q_(n-1): ─────────────────────────┼───────── ... ───────────■──────────
┌────────────┐ ┌───────┴───────┐ ┌─────────┴─────────┐
target: ─┤ RY(offset) ├──┤ RY(2^0 slope) ├ ... ┤ RY(2^(n-1) slope) ├
└────────────┘ └───────────────┘ └───────────────────┘
"""
num_state_qubits: pydantic.PositiveInt = pydantic.Field(
description="The number of input qubits"
)
bases: List[PydanticPauliBasisStr] = pydantic.Field(
description="The types of Pauli rotations ('X', 'Y', 'Z')."
)
slopes: List[float] = pydantic.Field(
description="The slopes of the controlled rotations."
)
offsets: List[float] = pydantic.Field(
description="The offsets of the controlled rotations."
)
@pydantic.validator("bases", "slopes", "offsets", pre=True, always=True)
def as_list(cls, v: Any) -> List[Any]:
if not isinstance(v, list):
v = [v]
return v
@pydantic.root_validator()
def validate_lists(cls, values: Dict[str, Any]) -> Dict[str, Any]:
offsets = values.get("offsets", list())
bases = values.get("bases", list())
slopes = values.get("slopes", list())
if len(slopes) == len(offsets) and len(offsets) == len(bases):
return values
raise ClassiqValueError(LENGTH_ERROR_MESSAGE)
def _create_ios(self) -> None:
self._inputs = {
STATE: RegisterArithmeticInfo(size=self.num_state_qubits),
TARGET: RegisterArithmeticInfo(size=len(self.bases)),
}
self._outputs = {**self.inputs}
bases: List[classiq.interface.generator.linear_pauli_rotations.ConstrainedStrValue]
pydantic-field
required
The types of Pauli rotations ('X', 'Y', 'Z').
num_state_qubits: PositiveInt
pydantic-field
required
The number of input qubits
offsets: List[float]
pydantic-field
required
The offsets of the controlled rotations.
slopes: List[float]
pydantic-field
required
The slopes of the controlled rotations.
mcu
Mcu (FunctionParams)
pydantic-model
Multi-controlled u-gate. Based on U(theta, phi, lam, gam) = e^(i*(gam + (phi + lam)/2)) * RZ(phi) * RY(theta) * RZ(lam) For a general gate U, four angles are required - theta, phi, lambda and gam.
U(gam, phi,theta, lam) = e^(igam) * cos(theta/2) & -e^(ilam)sin(theta/2) \ e^(iphi)sin(theta/2) & e^(i(phi+lam))*cos(theta/2) \
U(gam, phi,theta, lam) = e^(igam) * cos(theta/2) & -e^(ilam)sin(theta/2) \ e^(iphi)sin(theta/2) & e^(i(phi+lam))*cos(theta/2) \
Source code in classiq/interface/generator/mcu.py
class Mcu(FunctionParams):
"""
Multi-controlled u-gate.
Based on U(theta, phi, lam, gam) = e^(i*(gam + (phi + lam)/2)) * RZ(phi) * RY(theta) * RZ(lam)
For a general gate U, four angles are required - theta, phi, lambda and gam.
U(gam, phi,theta, lam) =
e^(i*gam) *
cos(theta/2) & -e^(i*lam)*sin(theta/2) \\
e^(i*phi)*sin(theta/2) & e^(i*(phi+lam))*cos(theta/2) \\
U(gam, phi,theta, lam) =
e^(i*gam) *
cos(theta/2) & -e^(i*lam)*sin(theta/2) \\
e^(i*phi)*sin(theta/2) & e^(i*(phi+lam))*cos(theta/2) \\
"""
theta: ParameterFloatType = pydantic.Field(
default=0, description="Theta radian angle.", is_exec_param=True
)
phi: ParameterFloatType = pydantic.Field(
default=0, description="Phi radian angle.", is_exec_param=True
)
lam: ParameterFloatType = pydantic.Field(
default=0, description="Lambda radian angle.", is_exec_param=True
)
gam: ParameterFloatType = pydantic.Field(
default=0, description="gam radian angle.", is_exec_param=True
)
num_ctrl_qubits: Optional[pydantic.PositiveInt] = pydantic.Field(
default=None, description="The number of control qubits."
)
ctrl_state: Optional[str] = pydantic.Field(
default=None, description="string of the control state"
)
@pydantic.root_validator()
def _validate_control(cls, values: Dict[str, Any]) -> Dict[str, Any]:
num_ctrl_qubits = values.get("num_ctrl_qubits")
ctrl_state = values.get("ctrl_state")
if ctrl_state is not None:
ctrl_state = cast(str, ctrl_state)
ControlState.validate_control_string(ctrl_state)
if ctrl_state is None and num_ctrl_qubits is None:
raise ClassiqValueError("num_ctrl_qubits or ctrl_state must exist.")
if ctrl_state is None and num_ctrl_qubits is not None:
values["ctrl_state"] = "1" * num_ctrl_qubits
ctrl_state = values["ctrl_state"]
if num_ctrl_qubits is None and ctrl_state is not None:
num_ctrl_qubits = len(ctrl_state)
values["num_ctrl_qubits"] = num_ctrl_qubits
if len(ctrl_state) != num_ctrl_qubits:
raise ClassiqValueError(
"control state length should be equal to the number of control qubits"
)
return values
def _create_ios(self) -> None:
if self.num_ctrl_qubits is None:
raise ClassiqValueError("num_ctrl_qubits must have a valid value.")
ctrl_register = RegisterUserInput(size=self.num_ctrl_qubits, name=CTRL)
target_register = RegisterUserInput(size=1, name=TARGET)
self._inputs = {reg.name: reg for reg in (ctrl_register, target_register)}
self._outputs = {reg.name: reg for reg in (ctrl_register, target_register)}
ctrl_state: str
pydantic-field
string of the control state
gam: Union[float, str]
pydantic-field
gam radian angle.
lam: Union[float, str]
pydantic-field
Lambda radian angle.
num_ctrl_qubits: PositiveInt
pydantic-field
The number of control qubits.
phi: Union[float, str]
pydantic-field
Phi radian angle.
theta: Union[float, str]
pydantic-field
Theta radian angle.
mcx
Mcx (FunctionParams)
pydantic-model
multi-controlled x-gate
Source code in classiq/interface/generator/mcx.py
class Mcx(FunctionParams):
"""
multi-controlled x-gate
"""
arguments: List[RegisterUserInput] = pydantic.Field(
default_factory=list, description="registers describing the state (ordered)"
)
num_ctrl_qubits: Optional[pydantic.PositiveInt] = pydantic.Field(
description="number of control qubits"
)
ctrl_state: str = pydantic.Field(default="", description="control state string")
@pydantic.validator("arguments", always=True)
def _validate_argument_names(
cls, arguments: List[RegisterUserInput]
) -> List[RegisterUserInput]:
register_name_list: List[Optional[str]] = [arg.name for arg in arguments]
if None in register_name_list:
raise ClassiqValueError("All registers must be named")
if len(set(register_name_list)) != len(register_name_list):
raise ClassiqValueError("Registers must have distinct names")
return arguments
@pydantic.validator("num_ctrl_qubits", always=True)
def _validate_sizes(cls, num_ctrl_qubits: int, values: Dict[str, Any]) -> int:
arguments_size = sum(arg.size for arg in values.get("arguments", list()))
if not num_ctrl_qubits:
num_ctrl_qubits = arguments_size - 1
if num_ctrl_qubits < 1:
raise ClassiqValueError("Must have control qubits")
if arguments_size == 0:
ctrl_register = RegisterUserInput(size=num_ctrl_qubits, name=CTRL)
target_register = RegisterUserInput(size=1, name=TARGET_QUBIT)
values["arguments"] = [ctrl_register, target_register]
elif num_ctrl_qubits != arguments_size - 1:
raise ClassiqValueError("Given sizes do not match")
return num_ctrl_qubits
@pydantic.validator("ctrl_state", always=True)
def _validate_ctrl_state(cls, ctrl_state: str, values: Dict[str, Any]) -> str:
num_ctrl_qubits = values.get("num_ctrl_qubits", -1)
if not ctrl_state:
return "1" * num_ctrl_qubits
if len(ctrl_state) != num_ctrl_qubits:
raise ClassiqValueError(
"control state length should be equal to the number of control qubits"
)
ControlState.validate_control_string(ctrl_state)
return ctrl_state
def _create_ios(self) -> None:
self._inputs = {arg.name: arg for arg in self.arguments}
self._outputs = {arg.name: arg for arg in self.arguments}
def get_power_order(self) -> int:
return 2
arguments: List[classiq.interface.generator.arith.register_user_input.RegisterUserInput]
pydantic-field
registers describing the state (ordered)
ctrl_state: str
pydantic-field
control state string
num_ctrl_qubits: PositiveInt
pydantic-field
number of control qubits
model
special
constraints
Constraints (BaseModel)
pydantic-model
Input constraints for the generated quantum circuit.
Source code in classiq/interface/generator/model/constraints.py
class Constraints(BaseModel, extra=Extra.forbid):
"""
Input constraints for the generated quantum circuit.
"""
max_width: Optional[pydantic.PositiveInt] = pydantic.Field(
default=None,
description="Maximum number of qubits in generated quantum circuit",
)
max_depth: Optional[pydantic.PositiveInt] = None
max_gate_count: Dict[TranspilerBasisGates, pydantic.NonNegativeInt] = (
pydantic.Field(default_factory=lambda: defaultdict(int))
)
optimization_parameter: OptimizationParameterType = pydantic.Field(
default=OptimizationParameter.NO_OPTIMIZATION,
description="If set, the synthesis engine optimizes the solution"
" according to that chosen parameter",
)
max_width: PositiveInt
pydantic-field
Maximum number of qubits in generated quantum circuit
optimization_parameter: Union[classiq.interface.generator.model.constraints.OptimizationParameter, classiq.interface.generator.transpiler_basis_gates.TranspilerBasisGates]
pydantic-field
If set, the synthesis engine optimizes the solution according to that chosen parameter
model
ClassiqBaseModel (VersionedModel, ABC)
pydantic-model
All the relevant data for evaluating execution in one place.
Source code in classiq/interface/generator/model/model.py
class ClassiqBaseModel(VersionedModel, ABC):
"""
All the relevant data for evaluating execution in one place.
"""
enums: List[EnumDeclaration] = pydantic.Field(
default_factory=list,
description="user-defined enums",
)
types: List[StructDeclaration] = pydantic.Field(
default_factory=list,
description="user-defined structs",
)
constants: List[Constant] = pydantic.Field(
default_factory=list,
)
classical_execution_code: str = pydantic.Field(
description="The classical execution code of the model", default=""
)
execution_preferences: ExecutionPreferences = pydantic.Field(
default_factory=ExecutionPreferences
)
@pydantic.validator("types")
def types_validator(cls, types: List[StructDeclaration]) -> List[StructDeclaration]:
if not is_list_unique([struct_type.name for struct_type in types]):
raise ClassiqValueError(TYPE_LIBRARY_DUPLICATED_TYPE_NAMES)
return types
classical_execution_code: str
pydantic-field
The classical execution code of the model
enums: List[classiq.interface.generator.types.enum_declaration.EnumDeclaration]
pydantic-field
user-defined enums
types: List[classiq.interface.generator.types.struct_declaration.StructDeclaration]
pydantic-field
user-defined structs
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
ExecutionModel (ClassiqBaseModel)
pydantic-model
Source code in classiq/interface/generator/model/model.py
class ExecutionModel(ClassiqBaseModel):
circuit_outputs: ArithmeticIODict = pydantic.Field(
description="Mapping between a measured register name and its arithmetic type",
default_factory=dict,
)
circuit_outputs: Dict[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, classiq.interface.generator.arith.register_user_input.RegisterArithmeticInfo]
pydantic-field
Mapping between a measured register name and its arithmetic type
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
preferences
special
preferences
Preferences (BaseModel)
pydantic-model
Source code in classiq/interface/generator/model/preferences/preferences.py
class Preferences(pydantic.BaseModel, extra=pydantic.Extra.forbid):
_backend_preferences: Optional[BackendPreferences] = pydantic.PrivateAttr(
default=None
)
machine_precision: PydanticMachinePrecision = DEFAULT_MACHINE_PRECISION
backend_service_provider: Optional[Union[Provider, ProviderVendor, str]] = (
pydantic.Field(
default=None,
description="Provider company or cloud for the requested backend.",
)
)
backend_name: Optional[Union[PydanticBackendName, AllBackendsNameByVendor]] = (
pydantic.Field(
default=None, description="Name of the requested backend or target."
)
)
custom_hardware_settings: CustomHardwareSettings = pydantic.Field(
default_factory=CustomHardwareSettings,
description="Custom hardware settings which will be used during optimization. "
"This field is ignored if backend preferences are given.",
)
debug_mode: bool = pydantic.Field(
default=True,
description="Add debug information to the synthesized result. "
"Setting this option to False can potentially speed up the synthesis, and is "
"recommended for executing iterative algorithms.",
)
output_format: PydanticConstrainedQuantumFormatList = pydantic.Field(
default=[QuantumFormat.QASM],
description="The quantum circuit output format(s). ",
)
pretty_qasm: bool = pydantic.Field(
True,
description="Prettify the OpenQASM2 outputs (use line breaks inside the gate "
"declarations).",
)
qasm3: Optional[bool] = pydantic.Field(
None,
description="Output OpenQASM 3.0 instead of OpenQASM 2.0. Relevant only for "
"the `qasm` and `transpiled_circuit.qasm` attributes of `GeneratedCircuit`.",
)
transpilation_option: TranspilationOption = pydantic.Field(
default=TranspilationOption.AUTO_OPTIMIZE,
description="If true, the returned result will contain a "
"transpiled circuit and its depth",
)
timeout_seconds: pydantic.PositiveInt = pydantic.Field(
default=300, description="Generation timeout in seconds"
)
optimization_timeout_seconds: Optional[pydantic.PositiveInt] = pydantic.Field(
default=None,
description="Optimization timeout in seconds, or None for no "
"optimization timeout (will still timeout when the generation timeout is over)",
)
random_seed: int = pydantic.Field(
default_factory=create_random_seed,
description="The random seed used for the generation",
)
@pydantic.validator("backend_service_provider", pre=True)
def validate_backend_service_provider(
cls, backend_service_provider: Any
) -> Optional[Provider]:
if backend_service_provider is None:
return None
return validate_backend_service_provider(backend_service_provider)
@pydantic.validator("optimization_timeout_seconds")
def optimization_timeout_less_than_generation_timeout(
cls,
optimization_timeout_seconds: Optional[pydantic.PositiveInt],
values: Dict[str, Any],
) -> Optional[pydantic.PositiveInt]:
generation_timeout_seconds = values.get("timeout_seconds")
if generation_timeout_seconds is None or optimization_timeout_seconds is None:
return optimization_timeout_seconds
if optimization_timeout_seconds >= generation_timeout_seconds:
raise ClassiqValueError(
f"Generation timeout ({generation_timeout_seconds})"
f"is greater than or equal to "
f"optimization timeout ({optimization_timeout_seconds}) "
)
return optimization_timeout_seconds
@pydantic.validator("output_format", pre=True)
def make_output_format_list(cls, output_format: Any) -> List:
if not pydantic.utils.sequence_like(output_format):
output_format = [output_format]
return output_format
@pydantic.validator("output_format", always=True)
def validate_output_format(
cls, output_format: PydanticConstrainedQuantumFormatList, values: Dict[str, Any]
) -> PydanticConstrainedQuantumFormatList:
if len(output_format) != len(set(output_format)):
raise ClassiqValueError(
f"output_format={output_format}\n"
"has at least one format that appears twice or more"
)
service_provider = values.get("backend_service_provider")
if service_provider is None:
return output_format
provider_format = _SERVICE_PROVIDER_TO_FORMAT.get(service_provider)
if provider_format is not None and provider_format not in output_format:
output_format.append(provider_format)
return output_format
@pydantic.root_validator()
def validate_backend(cls, values: Dict[str, Any]) -> Dict[str, Any]:
backend_name = values.get("backend_name")
backend_service_provider = values.get("backend_service_provider")
if (backend_name is None) != (backend_service_provider is None):
raise ClassiqValueError(BACKEND_VALIDATION_ERROR_MESSAGE)
return values
@property
def backend_preferences(self) -> Optional[BackendPreferences]:
if self.backend_name is None or self.backend_service_provider is None:
return None
if self._backend_preferences is None:
self._backend_preferences = BackendPreferences(
backend_name=self.backend_name,
backend_service_provider=self.backend_service_provider,
)
return self._backend_preferences
backend_name: Union[classiq.interface.generator.model.preferences.preferences.ConstrainedStrValue, classiq.interface.backend.quantum_backend_providers.IBMQHardwareNames, classiq.interface.backend.quantum_backend_providers.AzureQuantumBackendNames, classiq.interface.backend.quantum_backend_providers.AmazonBraketBackendNames, classiq.interface.backend.quantum_backend_providers.IonqBackendNames, classiq.interface.backend.quantum_backend_providers.ClassiqNvidiaBackendNames, classiq.interface.backend.quantum_backend_providers.AliceBobBackendNames, classiq.interface.backend.quantum_backend_providers.OQCBackendNames]
pydantic-field
Name of the requested backend or target.
backend_service_provider: Union[classiq.interface.hardware.Provider, classiq.interface.backend.quantum_backend_providers.ProviderVendor, str]
pydantic-field
Provider company or cloud for the requested backend.
custom_hardware_settings: CustomHardwareSettings
pydantic-field
Custom hardware settings which will be used during optimization. This field is ignored if backend preferences are given.
debug_mode: bool
pydantic-field
Add debug information to the synthesized result. Setting this option to False can potentially speed up the synthesis, and is recommended for executing iterative algorithms.
optimization_timeout_seconds: PositiveInt
pydantic-field
Optimization timeout in seconds, or None for no optimization timeout (will still timeout when the generation timeout is over)
output_format: ConstrainedListValue
pydantic-field
The quantum circuit output format(s).
pretty_qasm: bool
pydantic-field
Prettify the OpenQASM2 outputs (use line breaks inside the gate declarations).
qasm3: bool
pydantic-field
Output OpenQASM 3.0 instead of OpenQASM 2.0. Relevant only for the qasm
and transpiled_circuit.qasm
attributes of GeneratedCircuit
.
random_seed: int
pydantic-field
The random seed used for the generation
timeout_seconds: PositiveInt
pydantic-field
Generation timeout in seconds
transpilation_option: TranspilationOption
pydantic-field
If true, the returned result will contain a transpiled circuit and its depth
quantum_register
QReg
Represents a logical sequence of qubits.
The QReg can be used as an in_wires
or out_wires
argument to Model function calls,
assisting in model connectivity.
Source code in classiq/interface/generator/model/quantum_register.py
class QReg:
"""Represents a logical sequence of qubits.
The QReg can be used as an `in_wires` or `out_wires` argument to Model function calls,
assisting in model connectivity.
"""
def __init__(self, size: int) -> None:
"""Initializes a new QReg with the specified number of qubits.
Args:
size (int): The number of qubits in the QReg.
"""
if size <= 0:
raise ClassiqQRegError(f"Cannot create {size} new qubits")
self._qubits = [Qubit() for _ in range(size)]
def __hash__(self) -> int:
return super.__hash__(self)
@classmethod
def _from_qubits(cls, qubits: List[Qubit]) -> "QReg":
if (
not isinstance(qubits, list)
or not all(isinstance(qubit, Qubit) for qubit in qubits)
or len(qubits) == 0
):
raise ClassiqQRegError(f"Cannot create QReg from {qubits}")
qreg = cls(size=1)
qreg._qubits = qubits
return qreg
def __getitem__(self, key: Union[int, slice]) -> "QReg":
state = self._qubits[key]
return QReg._from_qubits(state if isinstance(state, list) else [state])
def __setitem__(self, key: Union[int, slice], value: "QReg") -> None:
if isinstance(key, int) and len(value) != 1:
raise ClassiqQRegError(
f"Size mismatch: value size {len(value)}, expected size 1"
)
self._qubits[key] = value._qubits[0] if isinstance(key, int) else value._qubits # type: ignore[call-overload]
def __iter__(self) -> Iterator["QReg"]:
return iter([self[idx] for idx in range(len(self))])
def __eq__(self, other: Any) -> bool:
return isinstance(other, QReg) and self._qubits == other._qubits
def isoverlapping(self, other: "QReg") -> bool:
return isinstance(other, QReg) and not set(self._qubits).isdisjoint(
set(other._qubits)
)
@classmethod
def concat(cls, *qregs: "QReg") -> "QReg":
"""Concatenate two QRegs.
Args:
qregs: the QRegs to concat in order, as separate arguments.
Returns:
A QReg representing the concatenation of the given QRegs.
"""
qubits = list(itertools.chain.from_iterable(qreg._qubits for qreg in qregs))
return cls._from_qubits(qubits)
def __len__(self) -> int:
return len(self._qubits)
@property
def qubits(self) -> List[Qubit]:
return self._qubits
def __class_getitem__(cls, params: Any) -> QRegGenericAlias:
# Supporting python 3.7+, thus returning `typing._GenericAlias` instead of `types.GenericAlias`
if isinstance(params, int):
return QRegGenericAlias(cls, params)
raise ClassiqQRegError(f"Invalid size: {params} ; int required")
def to_register_user_input(self, name: str = "") -> RegisterUserInput:
fraction_places = getattr(self, "fraction_places", 0)
is_signed = getattr(self, "is_signed", False)
return RegisterUserInput(
name=name,
size=len(self),
is_signed=is_signed,
fraction_places=fraction_places,
)
@staticmethod
def from_arithmetic_info(info: RegisterArithmeticInfo) -> "QReg":
method = _get_qreg_type_from_arithmetic_info(info)
frac_attr = {"fraction_places": info.fraction_places} if info.is_frac else {}
return method(size=info.size, **frac_attr)
__init__(self, size)
special
Initializes a new QReg with the specified number of qubits.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
size |
int |
The number of qubits in the QReg. |
required |
Source code in classiq/interface/generator/model/quantum_register.py
def __init__(self, size: int) -> None:
"""Initializes a new QReg with the specified number of qubits.
Args:
size (int): The number of qubits in the QReg.
"""
if size <= 0:
raise ClassiqQRegError(f"Cannot create {size} new qubits")
self._qubits = [Qubit() for _ in range(size)]
concat(*qregs)
classmethod
Concatenate two QRegs.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
qregs |
QReg |
the QRegs to concat in order, as separate arguments. |
() |
Returns:
Type | Description |
---|---|
QReg |
A QReg representing the concatenation of the given QRegs. |
Source code in classiq/interface/generator/model/quantum_register.py
@classmethod
def concat(cls, *qregs: "QReg") -> "QReg":
"""Concatenate two QRegs.
Args:
qregs: the QRegs to concat in order, as separate arguments.
Returns:
A QReg representing the concatenation of the given QRegs.
"""
qubits = list(itertools.chain.from_iterable(qreg._qubits for qreg in qregs))
return cls._from_qubits(qubits)
noise_properties
NoiseProperties (BaseModel)
pydantic-model
Source code in classiq/interface/generator/noise_properties.py
class NoiseProperties(BaseModel):
measurement_bit_flip_probability: Optional[PydanticProbabilityFloat] = (
pydantic.Field(
default=None,
description="Probability of measuring the wrong value for each qubit.",
)
)
measurement_bit_flip_probability: PydanticProbabilityFloat
pydantic-field
Probability of measuring the wrong value for each qubit.
oracles
special
custom_oracle
CustomOracle (OracleABC)
pydantic-model
Source code in classiq/interface/generator/oracles/custom_oracle.py
class CustomOracle(OracleABC[QubitState]):
custom_oracle: str = pydantic.Field(description="Oracle function")
custom_oracle_params: CustomFunction = pydantic.Field(
description="Oracle function parameters",
default_factory=CustomFunction,
)
@pydantic.root_validator(pre=True)
def _parse_oracle(cls, values: Dict[str, Any]) -> Dict[str, Any]:
parse_function_params_values(
values=values,
params_key="custom_oracle_params",
discriminator_key="custom_oracle",
param_classes={CustomFunction},
default_parser_class=CustomFunction,
)
return values
@pydantic.validator("custom_oracle_params")
def _validate_names_match_oracle(
cls, custom_oracle_params: CustomFunction
) -> CustomFunction:
if set(custom_oracle_params.input_decls.keys()) != set(
custom_oracle_params.output_decls.keys()
):
raise ClassiqValueError("Oracle IO names must be identical")
if any(
custom_oracle_params.output_decls[name].size != input_decl.size
for name, input_decl in custom_oracle_params.input_decls.items()
):
raise ClassiqValueError("Oracle IO sizes must be identical")
return custom_oracle_params
def _get_register_transputs(self) -> ArithmeticIODict:
return {**self.custom_oracle_params.input_decls}
def binary_result_to_typed_result(
self, bin_result: VariableBinResultMap
) -> VariableTypedResultMap[QubitState]:
return bin_result
def is_good_result(
self, problem_result: VariableTypedResultMap[QubitState]
) -> bool:
return True
custom_oracle: str
pydantic-field
required
Oracle function
custom_oracle_params: CustomFunction
pydantic-field
Oracle function parameters
partitioned_register
PartitionedRegister
dataclass
PartitionedRegister(name: str, num_qubits: int, partitions: Tuple[Tuple[int, ...], ...])
Source code in classiq/interface/generator/partitioned_register.py
@dataclass(frozen=True)
class PartitionedRegister:
name: str
# There are up to num_qubits qubits within the partitions, with unique values from 0 to num_qubits-1
num_qubits: int
partitions: Tuple[Tuple[int, ...], ...]
def __post_init__(self) -> None:
if not self.partitions:
message = f"Error creating {self.name}. Must contain at least one partition"
raise ClassiqValueError(message)
if not all(self.partitions):
message = f"Error creating {self.name}. Each partition must have at least one qubit"
raise ClassiqValueError(message)
partition_sets = [frozenset(part) for part in self.partitions]
intersection = frozenset.intersection(*partition_sets)
if len(self.partitions) > 1 and intersection:
message = (
f"Overlapping partitions in {self.name}. Intersection: {intersection}"
)
raise ClassiqValueError(message)
union = frozenset.union(*partition_sets)
possible_qubits = frozenset(range(self.num_qubits))
if not union <= possible_qubits:
message = f"Extra qubits in {self.name}: {union - possible_qubits}"
raise ClassiqValueError(message)
def get_partition(self, index: int) -> "RegisterPartition":
return RegisterPartition(self, index)
# Special partition containing qubits from [0..num_qubits) not in any other
# partition. May contain no qubits.
@property
def leftover_partition(self) -> "RegisterPartition":
return RegisterPartition(self, _index=None)
@property
def _leftover_qubits(self) -> Tuple[int, ...]:
total_qubits = set(itertools.chain.from_iterable(self.partitions))
return tuple(
qubit for qubit in range(self.num_qubits) if qubit not in total_qubits
)
@property
def all_qubits_in_partitions(self) -> Iterator[int]:
return itertools.chain.from_iterable(self.partitions)
def all_register_partitions(
self, include_leftover_partition: bool = False
) -> List["RegisterPartition"]:
all_partitions = [self.get_partition(i) for i in range(len(self.partitions))]
if include_leftover_partition:
all_partitions.append(self.leftover_partition)
return all_partitions
RegisterPartition
dataclass
RegisterPartition(partitioned_register: classiq.interface.generator.partitioned_register.PartitionedRegister, _index: Optional[int])
Source code in classiq/interface/generator/partitioned_register.py
@dataclass(frozen=True)
class RegisterPartition:
partitioned_register: PartitionedRegister
# index == None means this is the partition containing the leftover qubits.
_index: Optional[int]
def __post_init__(self) -> None:
num_partitions = len(self.partitioned_register.partitions)
if self._index is not None and (
self._index >= num_partitions or self._index < 0
):
message = f"Partition does not exist in {self.partitioned_register.name}. Index {self._index} not in range [0, {num_partitions})"
raise ClassiqValueError(message)
@property
def qubits(self) -> Tuple[int, ...]:
if self._index is None:
return self.partitioned_register._leftover_qubits
return self.partitioned_register.partitions[self._index]
@property
def _is_single_qubit(self) -> bool:
return len(self.qubits) == 1
# io_string is, for example, 'input' or 'foo[3:5]'
def matches_string(self, io_string: str) -> bool:
name, slice_ = parse_io_slicing(io_string)
qubits = tuple(range(self.partitioned_register.num_qubits)[slice_])
return self.partitioned_register.name == name and self.qubits == qubits
qft
QFT (FunctionParams)
pydantic-model
Creates a quantum Fourier transform on a specified number of qubits.
Source code in classiq/interface/generator/qft.py
class QFT(FunctionParams):
"""
Creates a quantum Fourier transform on a specified number of qubits.
"""
num_qubits: pydantic.PositiveInt = pydantic.Field(
description="The number of qubits on which the QFT acts."
)
approximation_degree: pydantic.NonNegativeInt = pydantic.Field(
default=0,
description="The degree of approximation (0 for no approximation). The smallest "
"'approximation_degree' rotation angles are dropped from the QFT.",
)
do_swaps: bool = pydantic.Field(
default=True, description="Whether to include the final swaps in the QFT."
)
def _create_ios(self) -> None:
self._inputs = {
DEFAULT_INPUT_NAME: RegisterArithmeticInfo(size=self.num_qubits)
}
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterArithmeticInfo(size=self.num_qubits)
}
def get_power_order(self) -> int:
return 4
approximation_degree: NonNegativeInt
pydantic-field
The degree of approximation (0 for no approximation). The smallest 'approximation_degree' rotation angles are dropped from the QFT.
do_swaps: bool
pydantic-field
Whether to include the final swaps in the QFT.
num_qubits: PositiveInt
pydantic-field
required
The number of qubits on which the QFT acts.
qpe
ExponentiationScaling (BaseModel)
pydantic-model
Details of exponentiation scaling for phase estimation.
Source code in classiq/interface/generator/qpe.py
class ExponentiationScaling(pydantic.BaseModel):
"""
Details of exponentiation scaling for phase estimation.
"""
max_depth: pydantic.PositiveInt = pydantic.Field(
description="The max_depth of the smallest exponentiation",
)
max_depth_scaling_factor: pydantic.NonNegativeFloat = pydantic.Field(
default=2.0,
description="The scaling factor of the exponentiation max_depth; defaults to 2.",
)
class Config:
frozen = True
max_depth: PositiveInt
pydantic-field
required
The max_depth of the smallest exponentiation
max_depth_scaling_factor: NonNegativeFloat
pydantic-field
The scaling factor of the exponentiation max_depth; defaults to 2.
ExponentiationSpecification (BaseModel)
pydantic-model
Specifications of individual Exponentiation details for each qubit; only valid if Exponentiation is given as unitary_params for PhaseEstimation. This sets the optimization to ExponentiationOptimization.MINIMIZE_ERROR and overrides the max_depth constraints.
Source code in classiq/interface/generator/qpe.py
class ExponentiationSpecification(pydantic.BaseModel):
"""
Specifications of individual Exponentiation details for each qubit; only valid if Exponentiation is given as unitary_params for PhaseEstimation.
This sets the optimization to ExponentiationOptimization.MINIMIZE_ERROR and overrides the max_depth constraints.
"""
scaling: Optional[ExponentiationScaling] = pydantic.Field(
default=None,
description="The scaling of the exponentiation functions.",
)
max_depths: Optional[Tuple[pydantic.NonNegativeInt, ...]] = pydantic.Field(
default=None,
description="The max_depth of each exponentiation function; overrides scaling.",
)
class Config:
frozen = True
@pydantic.root_validator
def _validate_exponentiation_specification(
cls, values: Dict[str, Any]
) -> Dict[str, Any]:
if values.get("scaling") is None and values.get("max_depths") is None:
raise ClassiqValueError("At least one specification must be provided.")
return values
max_depths: Tuple[pydantic.types.NonNegativeInt, ...]
pydantic-field
The max_depth of each exponentiation function; overrides scaling.
scaling: ExponentiationScaling
pydantic-field
The scaling of the exponentiation functions.
PhaseEstimation (FunctionParams)
pydantic-model
Quantum phase estimation of a given unitary function.
Source code in classiq/interface/generator/qpe.py
class PhaseEstimation(FunctionParams):
"""
Quantum phase estimation of a given unitary function.
"""
size: pydantic.PositiveInt = pydantic.Field(
description="The number of qubits storing the estimated phase."
)
unitary: str = pydantic.Field(
description="The unitary function for phase estimation.",
)
unitary_params: FunctionParams = pydantic.Field(
description="The unitary function parameters.",
default_factory=CustomFunction,
)
exponentiation_specification: Optional[ExponentiationSpecification] = (
pydantic.Field(
default=None,
description="The specifications for phase estimation of exponentiation functions.",
)
)
_output_name: IOName = pydantic.PrivateAttr(
default=PHASE_ESTIMATION_DEFAULT_OUTPUT_NAME
)
@property
def output_name(self) -> str:
return self._output_name
def _create_ios(self) -> None:
self._inputs = {**self.unitary_params.inputs}
self._outputs = {**self.unitary_params.outputs}
self._outputs[self._output_name] = RegisterArithmeticInfo(size=self.size)
self._create_zero_input_registers({DEFAULT_ZERO_NAME: self.size})
@pydantic.root_validator(pre=True)
def _validate_composite_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
if isinstance(values.get("unitary_params"), CustomFunction) and not values.get(
"unitary"
):
raise ClassiqValueError(
"`PhaseEstimation` of a user define function (`CustomFunction`) must receive the function name from the `unitary` field"
)
return values
@pydantic.root_validator(pre=True)
def _parse_function_params(cls, values: Dict[str, Any]) -> Dict[str, Any]:
parse_function_params_values(
values=values,
params_key="unitary_params",
discriminator_key="unitary",
param_classes=function_param_library_without_self_reference.param_list,
default_parser_class=CustomFunction,
)
return values
@pydantic.validator("unitary_params")
def _validate_unitary_params(cls, unitary_params: FunctionParams) -> FunctionParams:
if not unitary_params.is_powerable():
if isinstance(unitary_params, CustomFunction):
raise ClassiqMismatchIOsError(CUSTOM_FUNCTIONS_IO_MISMATCH_ERROR)
raise ClassiqValueError(
f"Phase estimation of {unitary_params.discriminator()} is currently not supported."
)
return unitary_params
@pydantic.validator("exponentiation_specification")
def _validate_exponentiation_specification(
cls,
exponentiation_specification: Optional[ExponentiationSpecification],
values: Dict[str, Any],
) -> Optional[ExponentiationSpecification]:
if exponentiation_specification is None:
return exponentiation_specification
unitary_params = values.get("unitary_params")
if not isinstance(unitary_params, Exponentiation):
raise ClassiqValueError(
"exponentiation_specification is only valid for Exponentiation unitary_params."
)
if exponentiation_specification.max_depths is not None and len(
exponentiation_specification.max_depths
) != values.get("size"):
raise ClassiqValueError(
"Length of max_depths must match the provided size."
)
return exponentiation_specification
exponentiation_specification: ExponentiationSpecification
pydantic-field
The specifications for phase estimation of exponentiation functions.
size: PositiveInt
pydantic-field
required
The number of qubits storing the estimated phase.
unitary: str
pydantic-field
required
The unitary function for phase estimation.
unitary_params: FunctionParams
pydantic-field
The unitary function parameters.
qsvm
QSVMFeatureMap (FunctionParams)
pydantic-model
Feature map circuit used for QSVM
Source code in classiq/interface/generator/qsvm.py
class QSVMFeatureMap(FunctionParams):
"""
Feature map circuit used for QSVM
"""
feature_map: FeatureMapType = pydantic.Field(
description="The feature map for the qsvm",
discriminator="map_type",
)
@property
def num_qubits(self) -> int:
if not self.feature_map.feature_dimension:
raise ClassiqQSVMError(
"Feature dimension should be provided to create a circuit."
)
if isinstance(self.feature_map, QSVMFeatureMapPauli):
return self.feature_map.feature_dimension
else:
return int(np.ceil(self.feature_map.feature_dimension / 2))
def _create_ios(self) -> None:
self._inputs = {
DEFAULT_INPUT_NAME: RegisterUserInput(
name=DEFAULT_INPUT_NAME, size=self.num_qubits
)
}
self._outputs = {
DEFAULT_OUTPUT_NAME: RegisterUserInput(
name=DEFAULT_OUTPUT_NAME, size=self.num_qubits
)
}
feature_map: Union[classiq.interface.generator.qsvm.QSVMFeatureMapBlochSphere, classiq.interface.generator.qsvm.QSVMFeatureMapPauli]
pydantic-field
required
The feature map for the qsvm
quantum_function_call
SynthesisQuantumFunctionCall (BaseModel)
pydantic-model
Source code in classiq/interface/generator/quantum_function_call.py
class SynthesisQuantumFunctionCall(BaseModel):
function: str = pydantic.Field(description="The function that is called")
function_params: f_params.FunctionParams = pydantic.Field(
description="The parameters necessary for defining the function",
default_factory=CustomFunction,
)
is_inverse: bool = pydantic.Field(
default=False, description="Call the function inverse."
)
strict_zero_ios: bool = pydantic.Field(
default=True,
description="Enables automated qubit allocation for pre-determined zero inputs "
"and allows automated qubit release when performing inversion.\n"
"Setting this flag to False exposes zero inputs and outputs as regular "
"functional registers, and shifts the responsibility to the user to manually "
"manage qubit allocation and release.",
)
release_by_inverse: bool = pydantic.Field(
default=False, description="Release zero inputs in inverse call."
)
control_states: List[ControlState] = pydantic.Field(
default_factory=list,
description="Call the controlled function with the given controlled states.",
)
should_control: bool = pydantic.Field(
default=True,
description="False value indicates this call shouldn't be controlled even if the flow is controlled.",
)
inputs: IOType = pydantic.Field(
default_factory=dict,
description="A mapping from the input name to the wire it connects to",
)
inouts: Mapping[IOName, WirePair] = pydantic.Field(
default_factory=dict,
description="A mapping from in/out name to the wires that connect to it",
)
outputs: IOType = pydantic.Field(
default_factory=dict,
description="A mapping from the output name to the wire it connects to",
)
power: PydanticPowerType = pydantic.Field(
default=1, description="Number of successive calls to the operation"
)
name: PydanticNonEmptyString = pydantic.Field(
default=None,
description="The name of the function instance. "
"If not set, determined automatically.",
)
def __eq__(self, other: Any) -> bool:
return (
isinstance(other, SynthesisQuantumFunctionCall) and self.name == other.name
)
def __hash__(self) -> int:
return hash(self.name)
@property
def non_zero_input_wires(self) -> List[WireName]:
in_out_input_wires = [
split_wire_pair_to_wires(inout)[0] for inout in self.inouts.values()
]
return get_non_zero_wires(self.inputs_dict.values()) + in_out_input_wires
@property
def non_zero_output_wires(self) -> List[WireName]:
in_out_output_wires = [
split_wire_pair_to_wires(inout)[1] for inout in self.inouts.values()
]
return get_non_zero_wires(self.outputs_dict.values()) + in_out_output_wires
@property
def inputs_dict(self) -> WireDict:
assert isinstance(self.inputs, dict)
return self.inputs
@property
def outputs_dict(self) -> WireDict:
assert isinstance(self.outputs, dict)
return self.outputs
@property
def input_regs_dict(self) -> ArithmeticIODict:
ctrl_regs_dict = {
ctrl_state.name: ctrl_state.control_register
for ctrl_state in self.control_states
}
return {
**self._true_io_dict(io=PortDirection.Input),
**ctrl_regs_dict,
}
@property
def output_regs_dict(self) -> ArithmeticIODict:
ctrl_regs_dict = {
ctrl_state.name: ctrl_state.control_register
for ctrl_state in self.control_states
}
return {
**self._true_io_dict(io=PortDirection.Output),
**ctrl_regs_dict,
}
def _true_io_dict(self, io: PortDirection) -> ArithmeticIODict:
if (io == PortDirection.Input) != self.is_inverse:
return self.function_params.inputs_full(self.strict_zero_ios)
return self.function_params.outputs
@pydantic.validator("name", pre=True, always=True)
def _create_name(cls, name: Optional[str], values: Dict[str, Any]) -> str:
"""
generates a name to a user defined-functions as follows:
<function_name>_<SUFFIX_MARKER>_<random_suffix>
"""
if name is not None:
match = re.fullmatch(pattern=NAME_REGEX, string=name)
if match is None:
raise ClassiqValueError(BAD_CALL_NAME_ERROR_MSG)
return name
function = values.get("function")
params = values.get("function_params")
if (
isinstance(params, CustomFunction)
and function == CustomFunction.discriminator()
and params.name != ""
):
function = params.name
suffix = f"{SUFFIX_MARKER}_{randomize_suffix()}"
if not function or params is None:
return name if name else suffix
return f"{function.split(f'_{EXPANDED_KEYWORD}')[0]}_{suffix}"
@pydantic.root_validator(pre=True)
def validate_composite_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
if isinstance(values.get("unitary_params"), CustomFunction) and not values.get(
"unitary"
):
raise ClassiqValueError(
"`PhaseEstimation` of a user define function (`CustomFunction`) must receive the function name from the `unitary` field"
)
return values
@pydantic.root_validator(pre=True)
def _parse_function_params(cls, values: Dict[str, Any]) -> Dict[str, Any]:
f_params.parse_function_params_values(
values=values,
params_key="function_params",
discriminator_key="function",
param_classes=function_param_list.function_param_library.param_list,
default_parser_class=CustomFunction,
)
return values
# TODO: note that this checks QuantumFunctionCall input register names
# are PARTIAL to FunctionParams input register names, not EQUAL.
# We might want to change that.
@staticmethod
def _validate_input_names(
*,
params: f_params.FunctionParams,
inputs: WireDict,
is_inverse: bool,
control_states: List[ControlState],
strict_zero_ios: bool,
) -> None:
(
invalid_expressions,
invalid_slicings,
invalid_names,
) = SynthesisQuantumFunctionCall._get_invalid_ios(
expressions=inputs.keys(),
params=params,
io=PortDirection.Input if not is_inverse else PortDirection.Output,
control_states=control_states,
strict_zero_ios=strict_zero_ios,
)
error_msg = []
if invalid_expressions:
error_msg.append(f"{BAD_INPUT_EXPRESSION_MSG}: {invalid_expressions}")
if invalid_names:
error_msg.append(f"{BAD_INPUT_ERROR_MSG}: {invalid_names}")
if invalid_slicings:
error_msg.append(f"{BAD_INPUT_SLICING_MSG}: {invalid_slicings}")
if error_msg:
raise ClassiqValueError("\n".join(error_msg))
@pydantic.validator("strict_zero_ios")
def _validate_arithmetic_cannot_strict_zero_ios(
cls, strict_zero_ios: bool, values: Dict[str, Any]
) -> bool:
assert not (
values.get("function") == Arithmetic.discriminator() and not strict_zero_ios
), "when using the Arithmetic function, assign to the expression result register via the target parameter instead of the strict_zero_ios flag"
return strict_zero_ios
@pydantic.validator("control_states")
def _validate_control_states(
cls, control_states: List[ControlState], values: Dict[str, Any]
) -> List[ControlState]:
control_names = [ctrl_state.name for ctrl_state in control_states]
function_params = values.get("function_params")
strict_zero_ios = values.get("strict_zero_ios")
if not (
isinstance(function_params, FunctionParams)
and isinstance(strict_zero_ios, bool)
):
return control_states
all_input_names = [
*function_params.inputs_full(strict_zero_ios=strict_zero_ios),
*control_names,
]
all_output_names = [*function_params.outputs, *control_names]
if any(
cls._has_repetitions(name_list)
for name_list in (control_names, all_input_names, all_output_names)
):
raise ClassiqControlError()
return control_states
@staticmethod
def _has_repetitions(name_list: Sequence[str]) -> bool:
return len(set(name_list)) < len(name_list)
@staticmethod
def _validate_slices(
io: PortDirection,
inputs: IOType,
fp: FunctionParams,
strict_zero_ios: bool,
control_states: List[ControlState],
) -> None:
name_slice_pairs = [parse_io_slicing(input) for input in inputs]
slices_dict: Dict[str, List[slice]] = defaultdict(list)
for name, slice in name_slice_pairs:
slices_dict[name].append(slice)
fp_inputs = (
fp.inputs_full(strict_zero_ios)
if (io == PortDirection.Input)
else fp.outputs
)
widths = {name: reg.size for name, reg in fp_inputs.items()}
control_names = {state.name for state in control_states}
for name in slices_dict:
if name in control_names:
continue
assert name in widths, "Name not in widths"
if not SynthesisQuantumFunctionCall._register_validate_slices(
slices_dict[name], widths[name]
):
raise ClassiqValueError(BAD_INPUT_SLICING_MSG)
@staticmethod
def _register_validate_slices(slices: List[slice], reg_width: int) -> bool:
widths_separated = [len(range(reg_width)[reg_slice]) for reg_slice in slices]
# examples: slice(0), slice(5,None) when width <= 5, slice(5,3)
empty_slices = 0 in widths_separated
max_stop = max(reg_slice.stop or 0 for reg_slice in slices)
out_of_range = max_stop > reg_width
all_widths_separated = sum(widths_separated)
all_indices = set(
itertools.chain.from_iterable(
range(reg_width)[reg_slice] for reg_slice in slices
)
)
all_widths_combined = len(all_indices)
overlapping_slices = all_widths_combined != all_widths_separated
return not any((empty_slices, out_of_range, overlapping_slices))
@pydantic.validator("inputs")
def _validate_inputs(cls, inputs: IOType, values: Dict[str, Any]) -> WireDict:
params: Optional[FunctionParams] = values.get("function_params")
is_inverse: bool = values.get("is_inverse", False)
strict_zero_ios: bool = values.get("strict_zero_ios", True)
control_states: List[ControlState] = values.get("control_states", list())
if params is None:
return dict()
if isinstance(params, CustomFunction):
if not isinstance(inputs, dict):
raise ClassiqValueError(CUSTOM_FUNCTION_SINGLE_IO_ERROR)
return inputs
if isinstance(inputs, str):
inputs = SynthesisQuantumFunctionCall._single_wire_to_dict(
io=f_params.PortDirection.Input,
is_inverse=is_inverse,
io_wire=inputs,
params=params,
strict_zero_ios=strict_zero_ios,
)
cls._validate_input_names(
params=params,
inputs=inputs,
is_inverse=is_inverse,
control_states=control_states,
strict_zero_ios=strict_zero_ios,
)
cls._validate_slices(
PortDirection.Input if not is_inverse else PortDirection.Output,
inputs,
params,
strict_zero_ios,
control_states,
)
return inputs
@staticmethod
def _validate_output_names(
*,
params: f_params.FunctionParams,
outputs: WireDict,
is_inverse: bool,
control_states: List[ControlState],
strict_zero_ios: bool,
) -> None:
(
invalid_expressions,
invalid_slicings,
invalid_names,
) = SynthesisQuantumFunctionCall._get_invalid_ios(
expressions=outputs.keys(),
params=params,
io=PortDirection.Output if not is_inverse else PortDirection.Input,
control_states=control_states,
strict_zero_ios=strict_zero_ios,
)
error_msg = []
if invalid_expressions:
error_msg.append(f"{BAD_OUTPUT_EXPRESSION_MSG}: {invalid_expressions}")
if invalid_names:
error_msg.append(f"{BAD_OUTPUT_ERROR_MSG}: {invalid_names}")
if invalid_slicings:
error_msg.append(f"{BAD_OUTPUT_SLICING_MSG}: {invalid_slicings}")
if error_msg:
raise ClassiqValueError("\n".join(error_msg))
@pydantic.validator("outputs")
def _validate_outputs(cls, outputs: IOType, values: Dict[str, Any]) -> IOType:
params = values.get("function_params")
is_inverse: bool = values.get("is_inverse", False)
strict_zero_ios: bool = values.get("strict_zero_ios", True)
control_states = values.get("control_states", list())
if params is None:
return outputs
if isinstance(params, CustomFunction):
if not isinstance(outputs, dict):
raise ClassiqValueError(CUSTOM_FUNCTION_SINGLE_IO_ERROR)
return outputs
if isinstance(outputs, str):
outputs = SynthesisQuantumFunctionCall._single_wire_to_dict(
io=f_params.PortDirection.Output,
is_inverse=is_inverse,
io_wire=outputs,
params=params,
strict_zero_ios=strict_zero_ios,
)
cls._validate_output_names(
params=params,
outputs=outputs,
is_inverse=is_inverse,
control_states=control_states,
strict_zero_ios=strict_zero_ios,
)
cls._validate_slices(
PortDirection.Input if is_inverse else PortDirection.Output,
outputs,
params,
strict_zero_ios,
control_states,
)
return outputs
@pydantic.validator("power", always=True)
def _validate_power(
cls, power: pydantic.NonNegativeInt, values: Dict[str, Any]
) -> pydantic.NonNegativeInt:
function_params = values.get("function_params")
if function_params is None:
return power
if power != 1 and not function_params.is_powerable(
values.get("strict_zero_ios")
):
raise ClassiqValueError("Cannot power this operator")
return power
@staticmethod
def _single_wire_to_dict(
io: f_params.PortDirection,
is_inverse: bool,
io_wire: WireName,
params: f_params.FunctionParams,
strict_zero_ios: bool = True,
) -> WireDict:
params_io = list(
params.inputs_full(strict_zero_ios)
if (io == PortDirection.Input) != is_inverse
else params.outputs
)
if len(params_io) == 1:
return {list(params_io)[0]: io_wire}
error_message = _generate_single_io_err(
io_str=io.name.lower(),
io_regs=params_io,
io_wire=io_wire,
function_name=type(params).__name__,
)
raise ClassiqValueError(error_message)
@staticmethod
def _get_invalid_ios(
*,
expressions: Iterable[str],
params: f_params.FunctionParams,
io: f_params.PortDirection,
control_states: List[ControlState],
strict_zero_ios: bool,
) -> Tuple[List[str], List[str], List[str]]:
expression_matches: Iterable[Optional[Match]] = map(
functools.partial(re.fullmatch, IO_REGEX), expressions
)
valid_matches: List[Match] = []
invalid_expressions: List[str] = []
for expression, expression_match in zip(expressions, expression_matches):
(
invalid_expressions.append(expression)
if expression_match is None
else valid_matches.append(expression_match)
)
invalid_slicings: List[str] = []
invalid_names: List[str] = []
valid_names = frozenset(
params.inputs_full(strict_zero_ios)
if io == PortDirection.Input
else params.outputs
)
for match in valid_matches:
name = match.groupdict().get(NAME)
if name is None:
raise AssertionError("Input/output name validation error")
slicing = match.groupdict().get(SLICING)
if slicing is not None and re.fullmatch(LEGAL_SLICING, slicing) is None:
invalid_slicings.append(match.string)
if name in valid_names:
continue
elif all(state.name != name for state in control_states):
invalid_names.append(name)
return invalid_expressions, invalid_slicings, invalid_names
def update_ios(self, inputs: ArithmeticIODict, outputs: ArithmeticIODict) -> None:
if not isinstance(self.function_params, CustomFunction):
raise AssertionError("CustomFunction object expected.")
self.function_params.generate_ios(
inputs=inputs,
outputs=outputs,
)
SynthesisQuantumFunctionCall._validate_input_names(
params=self.function_params,
inputs=self.inputs_dict,
is_inverse=self.is_inverse,
control_states=self.control_states,
strict_zero_ios=self.strict_zero_ios,
)
SynthesisQuantumFunctionCall._validate_output_names(
params=self.function_params,
outputs=self.outputs_dict,
is_inverse=self.is_inverse,
control_states=self.control_states,
strict_zero_ios=self.strict_zero_ios,
)
def inverse(self) -> SynthesisQuantumFunctionCall:
call_kwargs = self.__dict__.copy()
call_kwargs["inputs"] = self.outputs_dict
call_kwargs["outputs"] = self.inputs_dict
call_kwargs["name"] = self.inverse_name(self.name)
call_kwargs["is_inverse"] = not self.is_inverse
return SynthesisQuantumFunctionCall(**call_kwargs)
@staticmethod
def inverse_name(name: str) -> str:
if name.endswith(INVERSE_SUFFIX):
return name[: -len(INVERSE_SUFFIX)]
return f"{name}{INVERSE_SUFFIX}"
def control(
self, control_state: ControlState, input_wire: WireName, output_wire: WireName
) -> SynthesisQuantumFunctionCall:
if (
control_state.name in self.inputs_dict
or control_state.name in self.outputs_dict
):
raise ClassiqValueError(
f"Control name: {control_state.name} already exists"
)
inputs, outputs = dict(self.inputs_dict), dict(self.outputs_dict)
inputs.update({control_state.name: input_wire})
outputs.update({control_state.name: output_wire})
call_kwargs = self.__dict__.copy()
call_kwargs["inputs"] = inputs
call_kwargs["outputs"] = outputs
call_kwargs["name"] = f"{self.name}_{control_state.name}"
call_kwargs["control_states"] = self.control_states + [control_state]
return SynthesisQuantumFunctionCall(**call_kwargs)
class Config:
extra = Extra.forbid
control_states: List[classiq.interface.generator.control_state.ControlState]
pydantic-field
Call the controlled function with the given controlled states.
function: str
pydantic-field
required
The function that is called
function_params: FunctionParams
pydantic-field
The parameters necessary for defining the function
inouts: Mapping[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, classiq.interface.generator.quantum_function_call.WirePair]
pydantic-field
A mapping from in/out name to the wires that connect to it
inputs: Union[Mapping[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue], classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue]
pydantic-field
A mapping from the input name to the wire it connects to
is_inverse: bool
pydantic-field
Call the function inverse.
name: ConstrainedStrValue
pydantic-field
The name of the function instance. If not set, determined automatically.
outputs: Union[Mapping[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue], classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue]
pydantic-field
A mapping from the output name to the wire it connects to
power: Union[classiq.interface.backend.pydantic_backend.ConstrainedStrValue, int]
pydantic-field
Number of successive calls to the operation
release_by_inverse: bool
pydantic-field
Release zero inputs in inverse call.
should_control: bool
pydantic-field
False value indicates this call shouldn't be controlled even if the flow is controlled.
strict_zero_ios: bool
pydantic-field
Enables automated qubit allocation for pre-determined zero inputs and allows automated qubit release when performing inversion. Setting this flag to False exposes zero inputs and outputs as regular functional registers, and shifts the responsibility to the user to manually manage qubit allocation and release.
__eq__(self, other)
special
Return self==value.
Source code in classiq/interface/generator/quantum_function_call.py
def __eq__(self, other: Any) -> bool:
return (
isinstance(other, SynthesisQuantumFunctionCall) and self.name == other.name
)
__hash__(self)
special
Return hash(self).
Source code in classiq/interface/generator/quantum_function_call.py
def __hash__(self) -> int:
return hash(self.name)
quantum_program
QuantumProgram (VersionedModel, CircuitCodeInterface)
pydantic-model
Source code in classiq/interface/generator/quantum_program.py
class QuantumProgram(VersionedModel, CircuitCodeInterface):
hardware_data: SynthesisHardwareData
initial_values: Optional[InitialConditions]
data: GeneratedCircuitData
model: ExecutionModel
transpiled_circuit: Optional[TranspiledCircuitData]
creation_time: str = pydantic.Field(default_factory=datetime.utcnow().isoformat)
synthesis_duration: Optional[SynthesisStepDurations]
debug_info: Optional[List[FunctionDebugInfo]]
program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
def _hardware_agnostic_program_code(self) -> CodeAndSyntax:
circuit_code = self.program_circuit.get_code_by_priority()
if circuit_code is not None:
return circuit_code
raise ClassiqMissingOutputFormatError(
missing_formats=list(INSTRUCTION_SET_TO_FORMAT.values())
)
def _default_program_code(self) -> CodeAndSyntax:
if self.hardware_data.backend_data is None:
return self._hardware_agnostic_program_code()
backend_provider = self.hardware_data.backend_data.hw_provider
instruction_set: QuantumInstructionSet = VENDOR_TO_INSTRUCTION_SET.get(
backend_provider, DEFAULT_INSTRUCTION_SET
)
return self.program_circuit.get_code(instruction_set), instruction_set
def to_base_program(self) -> quantum_code.QuantumBaseCode:
code, syntax = self._default_program_code()
return quantum_code.QuantumBaseCode(code=code, syntax=syntax)
def to_program(
self,
initial_values: Optional[InitialConditions] = None,
instruction_set: Optional[QuantumInstructionSet] = None,
) -> quantum_code.QuantumCode:
initial_values = initial_values or self.initial_values
if instruction_set is not None:
code, syntax = (
self.program_circuit.get_code(instruction_set),
instruction_set,
)
else:
code, syntax = self._default_program_code()
if initial_values is not None:
registers_initialization = self.get_registers_initialization(
initial_values=initial_values
)
else:
registers_initialization = None
return quantum_code.QuantumCode(
code=code,
syntax=syntax,
output_qubits_map=self.data.qubit_mapping.physical_outputs,
registers_initialization=registers_initialization,
synthesis_execution_data=self.data.execution_data,
)
def _get_initialization_qubits(self, name: str) -> Tuple[int, ...]:
qubits = self.data.qubit_mapping.logical_inputs.get(name)
if qubits is None:
raise ClassiqStateInitializationError(
f"Cannot initialize register {name}, it does not appear in circuit inputs"
)
return qubits
def get_registers_initialization(
self, initial_values: InitialConditions
) -> Dict[RegisterName, RegisterInitialization]:
return {
name: RegisterInitialization(
name=name,
qubits=list(self._get_initialization_qubits(name)),
initial_condition=init_value,
)
for name, init_value in initial_values.items()
}
def save_results(self, filename: Optional[Union[str, Path]] = None) -> None:
"""
Saves quantum program results as json.
Parameters:
filename (Union[str, Path]): Optional, path + filename of file.
If filename supplied add `.json` suffix.
Returns:
None
"""
if filename is None:
filename = f"synthesised_circuit_{self.creation_time}.json"
with open(filename, "w") as file:
file.write(self.json(indent=4))
@classmethod
def from_qprog(cls, qprog: str) -> "QuantumProgram":
return cls.parse_raw(qprog)
@property
def _can_use_transpiled_code(self) -> bool:
return self.data.execution_data is None
@property
def program_circuit(self) -> CircuitCodeInterface:
return (
self.transpiled_circuit
if self.transpiled_circuit and self._can_use_transpiled_code
else self
)
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
save_results(self, filename=None)
Saves quantum program results as json.
!!! parameters
filename (Union[str, Path]): Optional, path + filename of file.
If filename supplied add .json
suffix.
!!! returns
None
Source code in classiq/interface/generator/quantum_program.py
def save_results(self, filename: Optional[Union[str, Path]] = None) -> None:
"""
Saves quantum program results as json.
Parameters:
filename (Union[str, Path]): Optional, path + filename of file.
If filename supplied add `.json` suffix.
Returns:
None
"""
if filename is None:
filename = f"synthesised_circuit_{self.creation_time}.json"
with open(filename, "w") as file:
file.write(self.json(indent=4))
standard_gates
special
controlled_standard_gates
C3XGate (ControlledGateWithState)
pydantic-model
The X Gate controlled on 3 qubits
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class C3XGate(ControlledGateWithState): # type: ignore[misc]
"""
The X Gate controlled on 3 qubits
"""
_name: str = "mcx"
num_ctrl_qubits: Literal[3] = pydantic.Field(default=3)
def get_power_order(self) -> int:
return 2
C4XGate (ControlledGateWithState)
pydantic-model
The X Gate controlled on 4 qubits
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class C4XGate(ControlledGateWithState): # type: ignore[misc]
"""
The X Gate controlled on 4 qubits
"""
_name: str = "mcx"
num_ctrl_qubits: Literal[4] = pydantic.Field(default=4)
def get_power_order(self) -> int:
return 2
CCXGate (ControlledGateWithState)
pydantic-model
The Double Controlled-X Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CCXGate(ControlledGateWithState): # type: ignore[misc]
"""
The Double Controlled-X Gate
"""
num_ctrl_qubits: Literal[2] = pydantic.Field(default=2)
def get_power_order(self) -> int:
return 2
CHGate (ControlledGateWithState)
pydantic-model
The Controlled-H Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CHGate(ControlledGateWithState): # type: ignore[misc]
"""
The Controlled-H Gate
"""
def get_power_order(self) -> int:
return 2
CPhaseGate (ControlledGateWithState)
pydantic-model
The Controlled-Phase Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CPhaseGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
"""
The Controlled-Phase Gate
"""
_name: str = "cp"
CRXGate (ControlledGateWithState)
pydantic-model
The Controlled-RX Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CRXGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
"""
The Controlled-RX Gate
"""
CRYGate (ControlledGateWithState)
pydantic-model
The Controlled-RY Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CRYGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
"""
The Controlled-RY Gate
"""
CRZGate (ControlledGateWithState)
pydantic-model
The Controlled-RZ Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CRZGate(ControlledGateWithState, angles=["theta"]): # type: ignore[misc]
"""
The Controlled-RZ Gate
"""
CSXGate (ControlledGateWithState)
pydantic-model
The Controlled-SX Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CSXGate(ControlledGateWithState): # type: ignore[misc]
"""
The Controlled-SX Gate
"""
def get_power_order(self) -> int:
return 4
CXGate (ControlledGateWithState)
pydantic-model
The Controlled-X Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CXGate(ControlledGateWithState): # type: ignore[misc]
"""
The Controlled-X Gate
"""
num_ctrl_qubits: Literal[1] = pydantic.Field(default=1)
def get_power_order(self) -> int:
return 2
CYGate (ControlledGateWithState)
pydantic-model
The Controlled-Y Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CYGate(ControlledGateWithState): # type: ignore[misc]
"""
The Controlled-Y Gate
"""
def get_power_order(self) -> int:
return 2
CZGate (ControlledGateWithState)
pydantic-model
The Controlled-Z Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class CZGate(ControlledGateWithState): # type: ignore[misc]
"""
The Controlled-Z Gate
"""
def get_power_order(self) -> int:
return 2
ControlledGate (_StandardGate)
pydantic-model
Base model for controlled Gates
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class ControlledGate(_StandardGate): # type: ignore[misc]
"""
Base model for controlled Gates
"""
num_ctrl_qubits: pydantic.PositiveInt = pydantic.Field(
default=DEFAULT_NUM_CTRL_QUBITS
)
def _create_ios(self) -> None:
_StandardGate._create_ios(self)
control = RegisterUserInput(
name=CONTROLLED_GATE_CONTROL, size=self.num_ctrl_qubits
)
self._inputs[CONTROLLED_GATE_CONTROL] = control
self._outputs[CONTROLLED_GATE_CONTROL] = control
def to_control_state(self) -> ControlState:
return ControlState(
name=CONTROLLED_GATE_CONTROL, num_ctrl_qubits=self.num_ctrl_qubits
)
ControlledGateWithState (ControlledGate)
pydantic-model
Base model for controlled Gates with control over the controlled_state
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class ControlledGateWithState(ControlledGate): # type: ignore[misc]
"""
Base model for controlled Gates with control over the controlled_state
"""
ctrl_state: CtrlState = pydantic.Field(
description="The control state in decimal or as a bit string (e.g. '1011'). If not specified, the control "
"state is 2**num_ctrl_qubits - 1.\n"
"The gate will be performed if the state of the control qubits matches the control state"
)
@pydantic.validator("ctrl_state", always=True)
def _validate_ctrl_state(
cls, ctrl_state: CtrlState, values: Dict[str, Any]
) -> CtrlState:
num_ctrl_qubits: int = values.get("num_ctrl_qubits", DEFAULT_NUM_CTRL_QUBITS)
ctrl_state = ctrl_state if ctrl_state is not None else "1" * num_ctrl_qubits
if isinstance(ctrl_state, str) and len(ctrl_state) != num_ctrl_qubits:
raise ClassiqValueError(
f"Invalid control state: {ctrl_state!r}. "
f"Expected {num_ctrl_qubits} qubits"
)
elif isinstance(ctrl_state, int) and ctrl_state >= 2**num_ctrl_qubits:
raise ClassiqValueError(
f"Invalid control state: {ctrl_state}. "
f"Expected value between 0 and {2**num_ctrl_qubits-1}"
)
return ctrl_state
def to_control_state(self) -> ControlState:
ctrl_state_str = (
_num_to_control_string(self.ctrl_state, self.num_ctrl_qubits)
if isinstance(self.ctrl_state, int)
else self.ctrl_state
)
return ControlState(name=CONTROLLED_GATE_CONTROL, ctrl_state=ctrl_state_str)
ctrl_state: Union[pydantic.types.StrictStr, pydantic.types.NonNegativeInt]
pydantic-field
The control state in decimal or as a bit string (e.g. '1011'). If not specified, the control state is 2**num_ctrl_qubits - 1. The gate will be performed if the state of the control qubits matches the control state
MCPhaseGate (ControlledGate)
pydantic-model
The Controlled-Phase Gate
Source code in classiq/interface/generator/standard_gates/controlled_standard_gates.py
class MCPhaseGate(ControlledGate, angles=["lam"]): # type: ignore[misc]
"""
The Controlled-Phase Gate
"""
_name: str = "mcphase"
standard_angle_metaclass
MyMetaAngledClass (type)
Source code in classiq/interface/generator/standard_gates/standard_angle_metaclass.py
class MyMetaAngledClass(type):
def __new__(cls, name: str, bases: tuple, namespace: dict, **kwargs: Any) -> type:
a_totally_new_namespace = cls._create_new_namespace(namespace, **kwargs)
return type.__new__(cls, name, bases, a_totally_new_namespace)
@staticmethod
def _create_new_namespace(namespace: dict, **kwargs: Any) -> dict:
angles = kwargs.get("angles", [])
annotations = {angle: ParameterFloatType for angle in angles}
fields = {
angle: pydantic.fields.FieldInfo(is_exec_param=True) for angle in angles
}
original_annotations = namespace.get("__annotations__", {})
return {
**namespace,
**fields,
**{"__annotations__": {**original_annotations, **annotations}},
}
__new__(cls, name, bases, namespace, **kwargs)
special
staticmethod
Create and return a new object. See help(type) for accurate signature.
Source code in classiq/interface/generator/standard_gates/standard_angle_metaclass.py
def __new__(cls, name: str, bases: tuple, namespace: dict, **kwargs: Any) -> type:
a_totally_new_namespace = cls._create_new_namespace(namespace, **kwargs)
return type.__new__(cls, name, bases, a_totally_new_namespace)
MyMetaAngledClassModel (ModelMetaclass, MyMetaAngledClass)
Source code in classiq/interface/generator/standard_gates/standard_angle_metaclass.py
class MyMetaAngledClassModel(PydanticMetaClass, MyMetaAngledClass):
def __new__(
cls, name: str, bases: tuple, namespace: dict, **kwargs: Any
) -> PydanticMetaClass:
# First, populate the namespace (specifically, "__annotations__") according to `MyMetaAngledClass` with the angles defined
namespace = MyMetaAngledClass._create_new_namespace(namespace, **kwargs)
# Clean `kwargs` afterwards so that it won't pass again to the final class
if "angles" in kwargs:
kwargs.pop("angles")
# Next, continue with pydantic's flow
return PydanticMetaClass.__new__(cls, name, bases, namespace, **kwargs)
__new__(cls, name, bases, namespace, **kwargs)
special
staticmethod
Create and return a new object. See help(type) for accurate signature.
Source code in classiq/interface/generator/standard_gates/standard_angle_metaclass.py
def __new__(
cls, name: str, bases: tuple, namespace: dict, **kwargs: Any
) -> PydanticMetaClass:
# First, populate the namespace (specifically, "__annotations__") according to `MyMetaAngledClass` with the angles defined
namespace = MyMetaAngledClass._create_new_namespace(namespace, **kwargs)
# Clean `kwargs` afterwards so that it won't pass again to the final class
if "angles" in kwargs:
kwargs.pop("angles")
# Next, continue with pydantic's flow
return PydanticMetaClass.__new__(cls, name, bases, namespace, **kwargs)
standard_angled_gates
PhaseGate (_StandardGate)
pydantic-model
Add relative phase of lambda
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class PhaseGate(_StandardGate, angles=["theta"]): # type: ignore[misc]
"""
Add relative phase of lambda
"""
_name: str = "p"
RGate (_StandardGate)
pydantic-model
Rotation by theta about the cos(phi)X + sin(phi)Y axis
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class RGate(_StandardGate, angles=["theta", "phi"]): # type: ignore[misc]
"""
Rotation by theta about the cos(phi)X + sin(phi)Y axis
"""
RXGate (_StandardGate)
pydantic-model
Rotation by theta about the X axis
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class RXGate(_StandardGate, angles=["theta"]): # type: ignore[misc]
"""
Rotation by theta about the X axis
"""
RXXGate (_DoubleRotationGate)
pydantic-model
Rotation by theta about the XX axis
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class RXXGate(_DoubleRotationGate): # type: ignore[misc]
"""
Rotation by theta about the XX axis
"""
RYGate (_StandardGate)
pydantic-model
Rotation by theta about the Y axis
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class RYGate(_StandardGate, angles=["theta"]): # type: ignore[misc]
"""
Rotation by theta about the Y axis
"""
RYYGate (_DoubleRotationGate)
pydantic-model
Rotation by theta about the YY axis
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class RYYGate(_DoubleRotationGate): # type: ignore[misc]
"""
Rotation by theta about the YY axis
"""
RZGate (_StandardGate)
pydantic-model
Rotation by phi about the Z axis
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class RZGate(_StandardGate, angles=["phi"]): # type: ignore[misc]
"""
Rotation by phi about the Z axis
"""
RZZGate (_DoubleRotationGate)
pydantic-model
Rotation by theta about the ZZ axis
Source code in classiq/interface/generator/standard_gates/standard_angled_gates.py
class RZZGate(_DoubleRotationGate): # type: ignore[misc]
"""
Rotation by theta about the ZZ axis
"""
standard_gates
HGate (UncontrolledStandardGate)
pydantic-model
creates a Hadamard gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class HGate(UncontrolledStandardGate): # type: ignore[misc]
"""
creates a Hadamard gate
"""
def get_power_order(self) -> int:
return 2
IGate (UncontrolledStandardGate)
pydantic-model
creates the identity gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class IGate(UncontrolledStandardGate): # type: ignore[misc]
"""
creates the identity gate
"""
_name: str = "id"
def get_power_order(self) -> int:
return 1
SGate (UncontrolledStandardGate)
pydantic-model
Z**0.5
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class SGate(UncontrolledStandardGate): # type: ignore[misc]
"""
Z**0.5
"""
def get_power_order(self) -> int:
return 4
SXGate (UncontrolledStandardGate)
pydantic-model
X**0.5
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class SXGate(UncontrolledStandardGate): # type: ignore[misc]
"""
X**0.5
"""
def get_power_order(self) -> int:
return 4
SXdgGate (UncontrolledStandardGate)
pydantic-model
creates the inverse SX gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class SXdgGate(UncontrolledStandardGate): # type: ignore[misc]
"""
creates the inverse SX gate
"""
def get_power_order(self) -> int:
return 4
SdgGate (UncontrolledStandardGate)
pydantic-model
creates the inverse S gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class SdgGate(UncontrolledStandardGate): # type: ignore[misc]
"""
creates the inverse S gate
"""
def get_power_order(self) -> int:
return 4
SwapGate (UncontrolledStandardGate)
pydantic-model
Swaps between two qubit states
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class SwapGate(UncontrolledStandardGate): # type: ignore[misc]
"""
Swaps between two qubit states
"""
_num_target_qubits: pydantic.PositiveInt = pydantic.PrivateAttr(default=2)
def get_power_order(self) -> int:
return 2
TGate (UncontrolledStandardGate)
pydantic-model
Z**0.25
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class TGate(UncontrolledStandardGate): # type: ignore[misc]
"""
Z**0.25
"""
def get_power_order(self) -> int:
return 8
TdgGate (UncontrolledStandardGate)
pydantic-model
creates the inverse T gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class TdgGate(UncontrolledStandardGate): # type: ignore[misc]
"""
creates the inverse T gate
"""
def get_power_order(self) -> int:
return 8
XGate (UncontrolledStandardGate)
pydantic-model
creates a X gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class XGate(UncontrolledStandardGate): # type: ignore[misc]
"""
creates a X gate
"""
def get_power_order(self) -> int:
return 2
YGate (UncontrolledStandardGate)
pydantic-model
creates a Y gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class YGate(UncontrolledStandardGate): # type: ignore[misc]
"""
creates a Y gate
"""
def get_power_order(self) -> int:
return 2
ZGate (UncontrolledStandardGate)
pydantic-model
create a Z gate
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class ZGate(UncontrolledStandardGate): # type: ignore[misc]
"""
create a Z gate
"""
def get_power_order(self) -> int:
return 2
iSwapGate (UncontrolledStandardGate)
pydantic-model
Swaps between two qubit states and add phase of i to the amplitudes of |01> and |10>
Source code in classiq/interface/generator/standard_gates/standard_gates.py
class iSwapGate(UncontrolledStandardGate): # type: ignore[misc] # noqa: N801
"""
Swaps between two qubit states and add phase of i to the amplitudes of |01> and |10>
"""
_num_target_qubits: pydantic.PositiveInt = pydantic.PrivateAttr(default=2)
def get_power_order(self) -> int:
return 4
u_gate
UGate (FunctionParams)
pydantic-model
Matrix representation:
U(gam, phi,theta, lam) = e^(igam) * cos(theta/2) & -e^(ilam)sin(theta/2) \ e^(iphi)sin(theta/2) & e^(i(phi+lam))*cos(theta/2) \
Source code in classiq/interface/generator/standard_gates/u_gate.py
class UGate(function_params.FunctionParams):
"""
Matrix representation:
U(gam, phi,theta, lam) =
e^(i*gam) *
cos(theta/2) & -e^(i*lam)*sin(theta/2) \\
e^(i*phi)*sin(theta/2) & e^(i*(phi+lam))*cos(theta/2) \\
"""
theta: ParameterFloatType = pydantic.Field(
description="Angle to rotate by the Y-axis.",
is_exec_param=True,
)
phi: ParameterFloatType = pydantic.Field(
description="First angle to rotate by the Z-axis.",
is_exec_param=True,
)
lam: ParameterFloatType = pydantic.Field(
description="Second angle to rotate by the Z-axis.",
is_exec_param=True,
)
gam: ParameterFloatType = pydantic.Field(
description="Angle to apply phase gate by.",
is_exec_param=True,
)
_inputs = pydantic.PrivateAttr(
default={
DEFAULT_STANDARD_GATE_ARG_NAME: RegisterUserInput(
name=DEFAULT_STANDARD_GATE_ARG_NAME, size=1
)
}
)
_outputs = pydantic.PrivateAttr(
default={
DEFAULT_STANDARD_GATE_ARG_NAME: RegisterUserInput(
name=DEFAULT_STANDARD_GATE_ARG_NAME, size=1
)
}
)
@property
def is_parametric(self) -> bool:
return not all(
isinstance(getattr(self, angle), (float, int)) for angle in self._params
)
gam: Union[float, str]
pydantic-field
required
Angle to apply phase gate by.
lam: Union[float, str]
pydantic-field
required
Second angle to rotate by the Z-axis.
phi: Union[float, str]
pydantic-field
required
First angle to rotate by the Z-axis.
theta: Union[float, str]
pydantic-field
required
Angle to rotate by the Y-axis.
state_preparation
special
computational_basis_state_preparation
ComputationalBasisStatePreparation (StatePreparationABC)
pydantic-model
Source code in classiq/interface/generator/state_preparation/computational_basis_state_preparation.py
class ComputationalBasisStatePreparation(StatePreparationABC):
computational_state: PydanticNonEmptyString = pydantic.Field(
description="binary computational state to create"
)
@pydantic.validator("computational_state")
def _validate_computational_state(
cls, computational_state: PydanticNonEmptyString
) -> PydanticNonEmptyString:
ControlState.validate_control_string(computational_state)
return computational_state
@property
def num_state_qubits(self) -> int:
return len(self.computational_state)
def get_power_order(self) -> int:
return 2
computational_state: ConstrainedStrValue
pydantic-field
required
binary computational state to create
distributions
GaussianMixture (BaseModel)
pydantic-model
Source code in classiq/interface/generator/state_preparation/distributions.py
class GaussianMixture(pydantic.BaseModel):
gaussian_moment_list: Tuple[GaussianMoments, ...]
num_qubits: pydantic.PositiveInt = pydantic.Field(
description="Number of qubits for the provided state."
)
class Config:
frozen = True
num_qubits: PositiveInt
pydantic-field
required
Number of qubits for the provided state.
state_preparation
StatePreparation (StatePreparationABC)
pydantic-model
Source code in classiq/interface/generator/state_preparation/state_preparation.py
class StatePreparation(StatePreparationABC):
amplitudes: Optional[Amplitudes] = pydantic.Field(
description="vector of probabilities", default=None
)
probabilities: Optional[Probabilities] = pydantic.Field(
description="vector of amplitudes", default=None
)
error_metric: Dict[Metrics, NonNegativeFloatRange] = pydantic.Field(
default_factory=lambda: {
Metrics.L2: NonNegativeFloatRange(lower_bound=0, upper_bound=1e-4)
}
)
# The order of validations is important: amplitudes, probabilities, error_metric
@pydantic.validator("amplitudes", always=True, pre=True)
def _initialize_amplitudes(
cls, amplitudes: Optional[FlexibleAmplitudes]
) -> Optional[Amplitudes]:
if amplitudes is None:
return None
amplitudes = np.array(amplitudes).squeeze()
if amplitudes.ndim == 1:
return validate_amplitudes(tuple(amplitudes))
raise ClassiqValueError(
"Invalid amplitudes were given, please ensure the amplitude is a vector of float in the form of either tuple or list or numpy array"
)
@pydantic.validator("probabilities", always=True, pre=True)
def _initialize_probabilities(
cls, probabilities: Optional[FlexibleProbabilities]
) -> Optional[Union[PMF, GaussianMixture, dict]]:
if probabilities is None:
return None
if isinstance(probabilities, Probabilities.__args__): # type: ignore[attr-defined]
return probabilities
if isinstance(probabilities, dict): # a pydantic object
return probabilities
probabilities = np.array(probabilities).squeeze()
if probabilities.ndim == 1:
return PMF(pmf=probabilities.tolist())
raise ClassiqValueError(
"Invalid probabilities were given, please ensure the probabilities is a vector of float in the form of either tuple or list or numpy array"
)
@pydantic.validator("error_metric", always=True, pre=True)
def _validate_error_metric(
cls, error_metric: Dict[Metrics, NonNegativeFloatRange], values: Dict[str, Any]
) -> Dict[Metrics, NonNegativeFloatRange]:
if not values.get("amplitudes"):
return error_metric
unsupported_metrics = {
Metrics(metric).value
for metric in error_metric
if not Metrics(metric).supports_amplitudes
}
if unsupported_metrics:
raise ClassiqValueError(
f"{unsupported_metrics} are not supported for amplitude preparation"
)
return error_metric
@pydantic.root_validator
def _validate_either_probabilities_or_amplitudes(
cls,
values: Dict[str, Any],
) -> Optional[Union[PMF, GaussianMixture, dict]]:
amplitudes = values.get("amplitudes")
probabilities = values.get("probabilities")
if amplitudes is not None and probabilities is not None:
raise ClassiqValueError(
"StatePreparation can't get both probabilities and amplitudes"
)
return values
@property
def num_state_qubits(self) -> int:
distribution = self.probabilities or self.amplitudes
if distribution is None:
raise ClassiqValueError("Must have either probabilities or amplitudes")
return num_of_qubits(distribution)
amplitudes: Tuple[float, ...]
pydantic-field
vector of probabilities
probabilities: Union[classiq.interface.generator.state_preparation.distributions.PMF, classiq.interface.generator.state_preparation.distributions.GaussianMixture]
pydantic-field
vector of amplitudes
types
special
enum_declaration
EnumDeclaration (HashableASTNode)
pydantic-model
Source code in classiq/interface/generator/types/enum_declaration.py
class EnumDeclaration(HashableASTNode):
name: str
members: Dict[str, int] = pydantic.Field(
default_factory=dict,
description="Dictionary of member names and their values",
)
BUILTIN_ENUM_DECLARATIONS: ClassVar[Dict[str, "EnumDeclaration"]] = {}
@pydantic.validator("members")
def _validate_members(cls, members: Dict[str, int]) -> Dict[str, int]:
underscore_members = [
member for member in members.keys() if member.startswith("_")
]
if len(underscore_members) > 0:
raise ClassiqValueError(
f"Enum member names must not start with an underscore. The offending "
f"members: {underscore_members}"
)
counter = Counter(members.values())
repeating_members = [
member for member, value in members.items() if counter[value] > 1
]
if len(repeating_members) > 0:
raise ClassiqValueError(
f"Cannot assign the same value to more than one enum member. The "
f"offending members: {repeating_members}"
)
return members
def create_enum(self) -> IntEnum:
return IntEnum(self.name, self.members)
members: Dict[str, int]
pydantic-field
Dictionary of member names and their values
struct_declaration
StructDeclaration (HashableASTNode)
pydantic-model
Source code in classiq/interface/generator/types/struct_declaration.py
class StructDeclaration(HashableASTNode):
name: str
variables: Dict[str, ConcreteClassicalType] = pydantic.Field(
default_factory=dict,
description="Dictionary of variable names and their classical types",
)
BUILTIN_STRUCT_DECLARATIONS: ClassVar[Dict[str, "StructDeclaration"]] = {}
def validate_fields(self, fields: Mapping[str, Any]) -> None:
expected_field_names = list(self.variables.keys())
received_field_names = list(fields.keys())
if set(expected_field_names) != set(received_field_names):
raise ClassiqValueError(
f"Invalid fields for {self.name} instance. Expected fields "
f"{expected_field_names}, got {received_field_names}"
)
variables: Dict[str, Annotated[Union[classiq.interface.generator.functions.classical_type.Integer, classiq.interface.generator.functions.classical_type.Real, classiq.interface.generator.functions.classical_type.Bool, classiq.interface.generator.functions.classical_type.ClassicalList, classiq.interface.generator.functions.classical_type.Pauli, classiq.interface.generator.functions.classical_type.StructMetaType, classiq.interface.generator.functions.classical_type.TypeName, classiq.interface.generator.functions.classical_type.ClassicalArray, classiq.interface.generator.functions.classical_type.VQEResult, classiq.interface.generator.functions.classical_type.Histogram, classiq.interface.generator.functions.classical_type.Estimation, classiq.interface.generator.functions.classical_type.LadderOperator, classiq.interface.generator.functions.classical_type.IQAERes], FieldInfo(default=PydanticUndefined, discriminator='kind', extra={})]]
pydantic-field
Dictionary of variable names and their classical types
ucc
UCC (ChemistryFunctionParams)
pydantic-model
Ucc ansatz
Source code in classiq/interface/generator/ucc.py
class UCC(ChemistryFunctionParams):
"""
Ucc ansatz
"""
use_naive_evolution: bool = pydantic.Field(
default=False, description="Whether to evolve the operator naively"
)
excitations: EXCITATIONS_TYPE = pydantic.Field(
default_factory=default_excitation_factory,
description="type of excitation operators in the UCC ansatz",
)
max_depth: Optional[pydantic.PositiveInt] = pydantic.Field(
default=None,
description="Maximum depth of the generated quantum circuit ansatz",
)
parameter_prefix: str = pydantic.Field(
default="param_",
description="Prefix for the generated parameters",
)
@pydantic.validator("excitations")
def _validate_excitations(cls, excitations: EXCITATIONS_TYPE) -> EXCITATIONS_TYPE:
if isinstance(excitations, int):
if excitations not in _EXCITATIONS_DICT.values():
raise ClassiqValueError(
f"possible values of excitations are {list(_EXCITATIONS_DICT.values())}"
)
excitations = [excitations]
elif isinstance(excitations, Iterable):
excitations = list(excitations) # type: ignore[assignment]
if all(isinstance(idx, int) for idx in excitations):
if any(idx not in _EXCITATIONS_DICT.values() for idx in excitations):
raise ClassiqValueError(
f"possible values of excitations are {list(_EXCITATIONS_DICT.values())}"
)
elif all(isinstance(idx, str) for idx in excitations):
if any(idx not in _EXCITATIONS_DICT.keys() for idx in excitations):
raise ClassiqValueError(
f"possible values of excitations are {list(_EXCITATIONS_DICT.keys())}"
)
excitations = sorted(_EXCITATIONS_DICT[idx] for idx in excitations) # type: ignore[index]
else:
raise ClassiqValueError(
"excitations must be of the same type (all str or all int)"
)
return excitations
excitations: Union[str, int, Iterable[int], Iterable[str]]
pydantic-field
type of excitation operators in the UCC ansatz
max_depth: PositiveInt
pydantic-field
Maximum depth of the generated quantum circuit ansatz
parameter_prefix: str
pydantic-field
Prefix for the generated parameters
use_naive_evolution: bool
pydantic-field
Whether to evolve the operator naively
unitary_gate
UnitaryGate (FunctionParams)
pydantic-model
Creates a circuit implementing a specified 2n * 2n unitary transformation.
Source code in classiq/interface/generator/unitary_gate.py
class UnitaryGate(function_params.FunctionParams):
"""
Creates a circuit implementing a specified 2**n * 2**n unitary transformation.
"""
# TODO - add support to numpy array-like (requires custom pydantic type definition)
data: DataArray = pydantic.Field(
description="A 2**n * 2**n (n positive integer) unitary matrix."
)
# TODO - decide if to include assertion on the unitarity of the matrix. It is already done in Qiskit and could be computationally expensive
@pydantic.validator("data")
def validate_data(cls, data: DataArray) -> DataArray:
data_np = np.array(data, dtype=object)
if data_np.ndim != 2:
raise ClassiqValueError("Data must me two dimensional")
if data_np.shape[0] != data_np.shape[1]:
raise ClassiqValueError("Matrix must be square")
if not np.mod(np.log2(data_np.shape[0]), 1) == 0:
raise ClassiqValueError(
"Matrix dimensions must be an integer exponent of 2"
)
return data
@property
def num_target_qubits(self) -> int:
return int(np.log2(len(self.data)))
def _create_ios(self) -> None:
self._inputs = {
UNITARY_GATE_INPUT: RegisterArithmeticInfo(size=self.num_target_qubits)
}
self._outputs = {
UNITARY_GATE_OUTPUT: RegisterArithmeticInfo(size=self.num_target_qubits)
}
data: List[List[Union[classiq.interface.generator.complex_type.Complex, float, int]]]
pydantic-field
required
A 2n * 2n (n positive integer) unitary matrix.
user_defined_function_params
CustomFunction (FunctionParams)
pydantic-model
A user-defined custom function parameters object.
Source code in classiq/interface/generator/user_defined_function_params.py
class CustomFunction(FunctionParams):
"""
A user-defined custom function parameters object.
"""
_name: str = pydantic.PrivateAttr(default="")
input_decls: ArithmeticIODict = pydantic.Field(
default_factory=dict,
description="A mapping from the inputs names to the registers information. should be identical to the register defined in the function creation.",
)
output_decls: ArithmeticIODict = pydantic.Field(
default_factory=dict,
description="A mapping from the outputs names to the registers information. should be identical to the register defined in the function creation.",
)
def _create_ios(self) -> None:
self._inputs = self.input_decls
self._outputs = self.output_decls
def generate_ios(
self,
inputs: Mapping[str, RegisterArithmeticInfo],
outputs: Mapping[str, RegisterArithmeticInfo],
) -> None:
self._inputs = dict(inputs)
self._outputs = dict(outputs)
@property
def name(self) -> str:
return self._name
def set_name(self, name: str) -> None:
self._name = name
def __hash__(self) -> int:
return hash((super().__hash__(), self.name))
input_decls: Dict[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, classiq.interface.generator.arith.register_user_input.RegisterArithmeticInfo]
pydantic-field
A mapping from the inputs names to the registers information. should be identical to the register defined in the function creation.
output_decls: Dict[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue, classiq.interface.generator.arith.register_user_input.RegisterArithmeticInfo]
pydantic-field
A mapping from the outputs names to the registers information. should be identical to the register defined in the function creation.
validations
special
flow_graph
Wire
dataclass
Wire(start: Optional[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue] = None, end: Optional[classiq.interface.helpers.custom_pydantic_types.ConstrainedStrValue] = None)
Source code in classiq/interface/generator/validations/flow_graph.py
@dataclass
class Wire:
start: Optional[PydanticNonEmptyString] = None
end: Optional[PydanticNonEmptyString] = None
grover
special
grover_modelling_params
GroverParams (BaseModel)
pydantic-model
Source code in classiq/interface/grover/grover_modelling_params.py
class GroverParams(BaseModel):
oracle: ArithmeticOracle = pydantic.Field(
description="An arithmatic oracle for the grover search."
)
num_reps: int = pydantic.Field(
default=1, description="The number of repetitions of the " "grover block."
)
num_reps: int
pydantic-field
The number of repetitions of the grover block.
oracle: ArithmeticOracle
pydantic-field
required
An arithmatic oracle for the grover search.
helpers
special
versioned_model
VersionedModel (BaseModel)
pydantic-model
Source code in classiq/interface/helpers/versioned_model.py
class VersionedModel(
pydantic.BaseModel, extra=pydantic.Extra.forbid, json_encoders=CUSTOM_ENCODERS
):
version: str = pydantic.Field(default=VERSION)
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
ide
special
ide_data
IDEData (VersionedModel)
pydantic-model
Source code in classiq/interface/ide/ide_data.py
class IDEData(VersionedModel):
qubits: List[IDEDataQubit]
operations: List[IDEDataOperation]
register_data: List[RegisterData]
segment_data: List[InterfaceSegmentData]
circuit_metrics: Optional[CircuitMetrics]
hardware_data: SynthesisHardwareData
creation_time: str
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
visual_model
ProgramVisualModel (VersionedModel)
pydantic-model
Source code in classiq/interface/ide/visual_model.py
class ProgramVisualModel(VersionedModel):
main_operation: Operation
program_data: ProgramData
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
jobs
JobDescription (GenericModel, Generic)
pydantic-model
Source code in classiq/interface/jobs.py
class JobDescription(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
status: JobStatus
failure_details: Optional[str]
result: Optional[T]
@pydantic.root_validator
def validate_status_and_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]:
status: Optional[JobStatus] = values.get("status")
if status is None or "result" not in values or "failure_details" not in values:
# If any of the fields doesn't exist, then previous validations failed
# result and failure_details are Optional, so we explicitly check if they
# exist in the values dictionary
return values
result = values["result"]
failure_details = values["failure_details"]
if status is JobStatus.COMPLETED:
# Completed job must return result and not have an error
if result is None or failure_details is not None:
raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
elif status in (JobStatus.FAILED, JobStatus.CANCELLED):
# Failed job must return error and not have result
if result is not None or failure_details is None:
raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
elif result is not None or failure_details is not None:
# Pending job must have no result and no error
raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
return values
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
model
special
handle_binding
HandleBinding (ASTNode)
pydantic-model
Source code in classiq/interface/model/handle_binding.py
class HandleBinding(ASTNode):
name: str
class Config:
frozen = True
extra = Extra.forbid
def __str__(self) -> str:
return self.name
def is_bindable(self) -> bool:
return True
__str__(self)
special
Return str(self).
Source code in classiq/interface/model/handle_binding.py
def __str__(self) -> str:
return self.name
model
Model (VersionedModel, ASTNode)
pydantic-model
All the relevant data for generating quantum circuit in one place.
Source code in classiq/interface/model/model.py
class Model(VersionedModel, ASTNode):
"""
All the relevant data for generating quantum circuit in one place.
"""
kind: Literal["user"] = pydantic.Field(default=USER_MODEL_MARKER)
# Must be validated before logic_flow
functions: List[NativeFunctionDefinition] = pydantic.Field(
default_factory=list,
description="The user-defined custom type library.",
)
enums: List[EnumDeclaration] = pydantic.Field(
default_factory=list,
description="user-defined enums",
)
types: List[StructDeclaration] = pydantic.Field(
default_factory=list,
description="user-defined structs",
)
classical_execution_code: str = pydantic.Field(
description="The classical execution code of the model", default=""
)
constants: List[Constant] = pydantic.Field(
default_factory=list,
)
constraints: Constraints = pydantic.Field(default_factory=Constraints)
execution_preferences: ExecutionPreferences = pydantic.Field(
default_factory=ExecutionPreferences
)
preferences: Preferences = pydantic.Field(default_factory=Preferences)
@property
def main_func(self) -> NativeFunctionDefinition:
return self.function_dict[MAIN_FUNCTION_NAME] # type:ignore[return-value]
@property
def body(self) -> StatementBlock:
return self.main_func.body
@pydantic.validator("preferences", always=True)
def _seed_suffix_randomizer(cls, preferences: Preferences) -> Preferences:
SUFFIX_RANDOMIZER.seed(preferences.random_seed)
return preferences
def _get_qualified_direction(
self, port_name: str, direction: PortDeclarationDirection
) -> PortDeclarationDirection:
if port_name in self.main_func.port_declarations:
return PortDeclarationDirection.Inout
return direction
@property
def function_dict(self) -> Dict[str, QuantumFunctionDeclaration]:
return nameables_to_dict(self.functions)
@pydantic.validator("functions", always=True)
def _add_empty_main(
cls, functions: List[NativeFunctionDefinition]
) -> List[NativeFunctionDefinition]:
function_dict = nameables_to_dict(functions)
if MAIN_FUNCTION_NAME not in function_dict:
functions.append(_create_empty_main_function())
return functions
@pydantic.validator("types")
def types_validator(cls, types: List[StructDeclaration]) -> List[StructDeclaration]:
user_defined_types: Set[str] = set()
for ctype in types:
if ctype.name in StructDeclaration.BUILTIN_STRUCT_DECLARATIONS:
raise ClassiqValueError(
TYPE_NAME_CONFLICT_BUILTIN.format(name=ctype.name)
)
if ctype.name in user_defined_types:
raise ClassiqValueError(TYPE_NAME_CONFLICT_USER.format(name=ctype.name))
user_defined_types.add(ctype.name)
return types
def get_model(self) -> SerializedModel:
return SerializedModel(
self.json(exclude_defaults=True, exclude_unset=True, indent=2)
)
@pydantic.validator("functions")
def _validate_entry_point(
cls, functions: List[NativeFunctionDefinition]
) -> List[NativeFunctionDefinition]:
function_dict = nameables_to_dict(functions)
if MAIN_FUNCTION_NAME not in function_dict:
raise ClassiqValueError("The model must contain a `main` function")
if any(
pd.direction != PortDeclarationDirection.Output
for pd in function_dict[MAIN_FUNCTION_NAME].port_declarations.values()
):
raise ClassiqValueError("Function 'main' cannot declare quantum inputs")
return functions
@pydantic.validator("constants")
def _validate_constants(cls, constants: List[Constant]) -> List[Constant]:
constant_definition_counts = Counter(
[constant.name for constant in constants]
).items()
multiply_defined_constants = {
constant for constant, count in constant_definition_counts if count > 1
}
if len(multiply_defined_constants) > 0:
raise ClassiqValueError(
f"The following constants were defined more than once: "
f"{multiply_defined_constants}"
)
return constants
classical_execution_code: str
pydantic-field
The classical execution code of the model
enums: List[classiq.interface.generator.types.enum_declaration.EnumDeclaration]
pydantic-field
user-defined enums
functions: List[classiq.interface.model.native_function_definition.NativeFunctionDefinition]
pydantic-field
The user-defined custom type library.
types: List[classiq.interface.generator.types.struct_declaration.StructDeclaration]
pydantic-field
user-defined structs
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
VersionedSerializedModel (VersionedModel)
pydantic-model
Source code in classiq/interface/model/model.py
class VersionedSerializedModel(VersionedModel):
model: SerializedModel
__json_encoder__(obj)
special
staticmethod
partial(func, args, *keywords) - new function with partial application of the given arguments and keywords.
native_function_definition
NativeFunctionDefinition (QuantumFunctionDeclaration)
pydantic-model
Facilitates the creation of a user-defined composite function
This class sets extra to forbid so that it can be used in a Union and not "steal" objects from other classes.
Source code in classiq/interface/model/native_function_definition.py
class NativeFunctionDefinition(QuantumFunctionDeclaration):
"""
Facilitates the creation of a user-defined composite function
This class sets extra to forbid so that it can be used in a Union and not "steal"
objects from other classes.
"""
body: StatementBlock = pydantic.Field(
default_factory=list, description="List of function calls to perform."
)
body: List[Annotated[Union[classiq.interface.model.quantum_function_call.QuantumFunctionCall, classiq.interface.model.quantum_expressions.arithmetic_operation.ArithmeticOperation, classiq.interface.model.quantum_expressions.amplitude_loading_operation.AmplitudeLoadingOperation, classiq.interface.model.variable_declaration_statement.VariableDeclarationStatement, classiq.interface.model.bind_operation.BindOperation, classiq.interface.model.inplace_binary_operation.InplaceBinaryOperation, classiq.interface.model.repeat.Repeat, classiq.interface.model.power.Power, classiq.interface.model.invert.Invert, classiq.interface.model.classical_if.ClassicalIf, classiq.interface.model.control.Control, classiq.interface.model.within_apply_operation.WithinApply], FieldInfo(default=Ellipsis, discriminator='kind', extra={})]]
pydantic-field
List of function calls to perform.
quantum_expressions
special
arithmetic_operation
ArithmeticOperation (QuantumAssignmentOperation)
pydantic-model
Source code in classiq/interface/model/quantum_expressions/arithmetic_operation.py
class ArithmeticOperation(QuantumAssignmentOperation):
kind: Literal["ArithmeticOperation"]
inplace_result: bool = pydantic.Field(
description="Determines whether the result variable is initialized",
)
def initialize_var_types(
self,
var_types: Dict[str, QuantumType],
machine_precision: int,
) -> None:
super().initialize_var_types(var_types, machine_precision)
self._result_type = compute_arithmetic_result_type(
self.expression.expr, var_types, machine_precision
)
@property
def wiring_inouts(
self,
) -> Mapping[
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
]:
inouts = dict(super().wiring_inouts)
if self.inplace_result:
inouts[self.result_name()] = self.result_var
return inouts
@property
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
if self.inplace_result:
return {}
return super().wiring_outputs
@classmethod
def result_name(cls) -> str:
return ARITHMETIC_EXPRESSION_RESULT_NAME
inplace_result: bool
pydantic-field
required
Determines whether the result variable is initialized
quantum_expression
QuantumAssignmentOperation (QuantumExpressionOperation)
pydantic-model
Source code in classiq/interface/model/quantum_expressions/quantum_expression.py
class QuantumAssignmentOperation(QuantumExpressionOperation):
result_var: HandleBinding = pydantic.Field(
description="The variable storing the expression result"
)
_result_type: Optional[QuantumType] = pydantic.PrivateAttr(
default=None,
)
@property
def result_type(self) -> QuantumType:
assert self._result_type is not None
return self._result_type
@property
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
return {self.result_name(): self.result_var}
@classmethod
@abc.abstractmethod
def result_name(cls) -> str:
raise NotImplementedError()
result_var: HandleBinding
pydantic-field
required
The variable storing the expression result
quantum_function_call
QuantumFunctionCall (QuantumOperation)
pydantic-model
Source code in classiq/interface/model/quantum_function_call.py
class QuantumFunctionCall(QuantumOperation):
kind: Literal["QuantumFunctionCall"]
function: Union[str, OperandIdentifier] = pydantic.Field(
description="The function that is called"
)
designated_params: Dict[str, Expression] = pydantic.Field(default_factory=dict)
designated_inputs: Dict[str, HandleBinding] = pydantic.Field(
default_factory=dict,
description="A mapping from the input name to the wire it connects to",
)
designated_inouts: Dict[
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
] = pydantic.Field(
default_factory=dict,
description="A mapping from in/out name to the wires that connect to it",
)
designated_outputs: Dict[str, HandleBinding] = pydantic.Field(
default_factory=dict,
description="A mapping from the output name to the wire it connects to",
)
designated_operands: Dict[str, QuantumOperand] = pydantic.Field(
description="Function calls passed to the operator",
default_factory=dict,
)
positional_args: List[ArgValue] = pydantic.Field(default_factory=list)
_func_decl: Optional[QuantumFunctionDeclaration] = pydantic.PrivateAttr(
default=None
)
@property
def func_decl(self) -> QuantumFunctionDeclaration:
if self._func_decl is None:
raise ClassiqError("Accessing an unresolved quantum function call")
return self._func_decl
def set_func_decl(self, fd: Optional[FunctionDeclaration]) -> None:
if fd is not None and not isinstance(fd, QuantumFunctionDeclaration):
raise ClassiqValueError(
"the declaration of a quantum function call cannot be set to a non-quantum function declaration."
)
self._func_decl = fd
@property
def func_name(self) -> str:
if isinstance(self.function, OperandIdentifier):
return self.function.name
return self.function
@property
def wiring_inputs(self) -> Mapping[str, HandleBinding]:
return self.inputs
@property
def wiring_inouts(
self,
) -> Mapping[
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
]:
return self.inouts
@property
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
return self.outputs
def get_positional_args(self) -> List[ArgValue]:
result: List[ArgValue] = self.positional_args
if not result:
result = list(self.designated_params.values())
result.extend(self.designated_operands.values())
result.extend(self.designated_inputs.values())
result.extend(self.designated_inouts.values())
result.extend(self.designated_outputs.values())
return result
@property
def positional_params(self) -> Dict[str, Expression]:
return dict(
zip(
self.func_decl.param_decls.keys(),
(
param
for param in self.positional_args
if isinstance(param, Expression)
),
)
)
@property
def params(self) -> Dict[str, Expression]:
return self.positional_params or self.designated_params
@property
def positional_operands(self) -> Dict[str, "QuantumOperand"]:
return dict(
zip(
self.func_decl.operand_declarations.keys(),
(
param
for param in self.positional_args
if not isinstance(param, (Expression, HandleBinding))
),
)
)
@property
def operands(self) -> Dict[str, "QuantumOperand"]:
return self.positional_operands or self.designated_operands
@property
def pos_port_args(self) -> Dict[str, HandleBinding]:
return dict(
zip(
self.func_decl.port_declarations.keys(),
(
param
for param in self.positional_args
if isinstance(param, HandleBinding)
),
)
)
def _get_pos_port_args_by_direction(
self, direction: PortDeclarationDirection
) -> Dict[str, HandleBinding]:
# This is a hack for handles to wires reduction tests,
# that initialize function definitions or calls not in the scope of a model,
# so there is no function resolution annotation.
if self._func_decl is None:
return dict()
return {
port_decl.name: port
for port_decl, port in zip(
self.func_decl.port_declarations.values(),
(
param
for param in self.positional_args
if isinstance(param, HandleBinding)
),
)
if direction == port_decl.direction
}
@property
def inputs(self) -> Dict[str, HandleBinding]:
return (
self._get_pos_port_args_by_direction(PortDeclarationDirection.Input)
or self.designated_inputs
)
@property
def inouts(
self,
) -> Dict[str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]]:
return (
self._get_pos_port_args_by_direction(PortDeclarationDirection.Inout)
or self.designated_inouts
)
@property
def outputs(self) -> Dict[str, HandleBinding]:
return (
self._get_pos_port_args_by_direction(PortDeclarationDirection.Output)
or self.designated_outputs
)
@pydantic.root_validator()
def validate_handles(cls, values: Dict[str, Any]) -> Dict[str, Any]:
inputs = values.get("designated_inputs", dict())
outputs = values.get("designated_outputs", dict())
inouts = values.get("designated_inouts", dict())
_validate_no_duplicated_ports(inputs, outputs, inouts)
_validate_no_duplicated_handles(inputs, outputs, inouts)
_validate_no_mixing_sliced_and_whole_handles(inouts)
return values
designated_inouts: Dict[str, Union[classiq.interface.model.handle_binding.SlicedHandleBinding, classiq.interface.model.handle_binding.SubscriptHandleBinding, classiq.interface.model.handle_binding.HandleBinding]]
pydantic-field
A mapping from in/out name to the wires that connect to it
designated_inputs: Dict[str, classiq.interface.model.handle_binding.HandleBinding]
pydantic-field
A mapping from the input name to the wire it connects to
designated_operands: Dict[str, Union[str, classiq.interface.model.quantum_lambda_function.QuantumLambdaFunction, List[Union[str, classiq.interface.model.quantum_lambda_function.QuantumLambdaFunction]]]]
pydantic-field
Function calls passed to the operator
designated_outputs: Dict[str, classiq.interface.model.handle_binding.HandleBinding]
pydantic-field
A mapping from the output name to the wire it connects to
function: Union[str, classiq.interface.model.quantum_function_call.OperandIdentifier]
pydantic-field
required
The function that is called
quantum_function_declaration
QuantumFunctionDeclaration (FunctionDeclaration)
pydantic-model
Facilitates the creation of a common quantum function interface object.
Source code in classiq/interface/model/quantum_function_declaration.py
class QuantumFunctionDeclaration(FunctionDeclaration):
"""
Facilitates the creation of a common quantum function interface object.
"""
port_declarations: Dict[str, PortDeclaration] = pydantic.Field(
description="The input and output ports of the function.",
default_factory=dict,
)
operand_declarations: Mapping[str, "QuantumOperandDeclaration"] = pydantic.Field(
description="The expected interface of the quantum function operands",
default_factory=dict,
)
positional_arg_declarations: List[PositionalArg] = pydantic.Field(
default_factory=list
)
BUILTIN_FUNCTION_DECLARATIONS: ClassVar[Dict[str, "QuantumFunctionDeclaration"]] = (
{}
)
@property
def input_set(self) -> Set[str]:
return set(self.inputs.keys())
@property
def output_set(self) -> Set[str]:
return set(self.outputs.keys())
@property
def inputs(self) -> ArithmeticIODict:
return _ports_to_registers(self.port_declarations, PortDirection.Input)
@property
def outputs(self) -> ArithmeticIODict:
return _ports_to_registers(self.port_declarations, PortDirection.Output)
def update_logic_flow(
self, function_dict: Mapping[str, "QuantumFunctionDeclaration"]
) -> None:
pass
@property
def port_names(self) -> List[str]:
return list(self.port_declarations.keys())
@property
def operand_names(self) -> List[str]:
return list(self.operand_declarations.keys())
def ports_by_direction(
self, direction: PortDirection
) -> Mapping[str, PortDeclaration]:
return {
name: port
for name, port in self.port_declarations.items()
if port.direction.includes_port_direction(direction)
}
def ports_by_declaration_direction(
self, direction: PortDeclarationDirection
) -> Set[str]:
return {
name
for name, port in self.port_declarations.items()
if port.direction == direction
}
def get_positional_arg_decls(self) -> List[PositionalArg]:
result: List[PositionalArg] = self.positional_arg_declarations
if not result:
result = [
ClassicalParameterDeclaration(name=name, classical_type=ctype)
for name, ctype in self.param_decls.items()
]
result.extend(self.operand_declarations.values())
result.extend(self.port_declarations.values())
return result
@pydantic.validator("operand_declarations")
def _validate_operand_declarations_names(
cls, operand_declarations: Dict[str, "QuantumOperandDeclaration"]
) -> Dict[str, "QuantumOperandDeclaration"]:
validate_nameables_mapping(operand_declarations, "Operand")
return operand_declarations
@pydantic.validator("port_declarations")
def _validate_port_declarations_names(
cls, port_declarations: Dict[str, PortDeclaration]
) -> Dict[str, PortDeclaration]:
validate_nameables_mapping(port_declarations, "Port")
return port_declarations
@pydantic.root_validator()
def _validate_params_and_operands_uniqueness(
cls, values: Dict[str, Any]
) -> Dict[str, Any]:
operand_declarations = values.get("operand_declarations")
parameter_declarations = values.get("param_decls")
port_declarations = values.get("port_declarations")
operand_parameter = validate_nameables_no_overlap(
operand_declarations, parameter_declarations, "operand", "parameter"
)
operand_port = validate_nameables_no_overlap(
operand_declarations, port_declarations, "operand", "port"
)
parameter_port = validate_nameables_no_overlap(
parameter_declarations, port_declarations, "parameter", "port"
)
error_message = ",".join(
msg
for msg in [operand_parameter, operand_port, parameter_port]
if msg is not None
)
if error_message:
raise ClassiqValueError(error_message)
return values
@pydantic.root_validator()
def _reduce_positional_declarations_to_keyword(
cls, values: Dict[str, Any]
) -> Dict[str, Any]:
operand_declarations = values.get("operand_declarations", dict())
parameter_declarations = values.get("param_decls", dict())
port_declarations = values.get("port_declarations", dict())
positional_arg_declarations = values.get("positional_arg_declarations", list())
_populate_declaration_dicts_with_positional_lists(
positional_arg_declarations,
parameter_declarations,
ClassicalParameterDeclaration,
)
_populate_declaration_dicts_with_positional_lists(
positional_arg_declarations,
operand_declarations,
QuantumOperandDeclaration,
)
_populate_declaration_dicts_with_positional_lists(
positional_arg_declarations, port_declarations, PortDeclaration
)
values["operand_declarations"] = operand_declarations
values["param_decls"] = parameter_declarations
values["port_declarations"] = port_declarations
return values
operand_declarations: Mapping[str, QuantumOperandDeclaration]
pydantic-field
The expected interface of the quantum function operands
port_declarations: Dict[str, classiq.interface.model.port_declaration.PortDeclaration]
pydantic-field
The input and output ports of the function.
QuantumOperandDeclaration (QuantumFunctionDeclaration)
pydantic-model
Source code in classiq/interface/model/quantum_function_declaration.py
class QuantumOperandDeclaration(QuantumFunctionDeclaration):
kind: Literal["QuantumOperandDeclaration"]
is_list: bool = pydantic.Field(
description="Indicate whether the operand expects an unnamed list of lambdas",
default=False,
)
@pydantic.root_validator(pre=True)
def _set_kind(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values_with_discriminator(values, "kind", "QuantumOperandDeclaration")
is_list: bool
pydantic-field
Indicate whether the operand expects an unnamed list of lambdas
quantum_lambda_function
QuantumLambdaFunction (ASTNode)
pydantic-model
The definition of an anonymous function passed as operand to higher-level functions
Source code in classiq/interface/model/quantum_lambda_function.py
class QuantumLambdaFunction(ASTNode):
"""
The definition of an anonymous function passed as operand to higher-level functions
"""
rename_params: Dict[str, str] = pydantic.Field(
default_factory=dict,
description="Mapping of the declared param to the actual variable name used ",
)
body: "StatementBlock" = pydantic.Field(
description="A list of function calls passed to the operator"
)
_func_decl: Optional[QuantumOperandDeclaration] = pydantic.PrivateAttr(default=None)
@property
def func_decl(self) -> Optional[QuantumOperandDeclaration]:
return self._func_decl
def set_op_decl(self, fd: QuantumOperandDeclaration) -> None:
self._func_decl = fd
body: List[Annotated[Union[classiq.interface.model.quantum_function_call.QuantumFunctionCall, classiq.interface.model.quantum_expressions.arithmetic_operation.ArithmeticOperation, classiq.interface.model.quantum_expressions.amplitude_loading_operation.AmplitudeLoadingOperation, classiq.interface.model.variable_declaration_statement.VariableDeclarationStatement, classiq.interface.model.bind_operation.BindOperation, classiq.interface.model.inplace_binary_operation.InplaceBinaryOperation, classiq.interface.model.repeat.Repeat, classiq.interface.model.power.Power, classiq.interface.model.invert.Invert, classiq.interface.model.classical_if.ClassicalIf, classiq.interface.model.control.Control, classiq.interface.model.within_apply_operation.WithinApply], FieldInfo(default=Ellipsis, discriminator='kind', extra={})]]
pydantic-field
required
A list of function calls passed to the operator
rename_params: Dict[str, str]
pydantic-field
Mapping of the declared param to the actual variable name used
validation_handle
ValidationHandle
dataclass
ValidationHandle(initial_state: Optional[classiq.interface.model.validation_handle.HandleState] = None, errors: Optional[List[str]] = None)
Source code in classiq/interface/model/validation_handle.py
@dataclasses.dataclass
class ValidationHandle:
_state: HandleState
errors: List[str] = dataclasses.field(default_factory=list)
def __init__(
self,
initial_state: Optional[HandleState] = None,
errors: Optional[List[str]] = None,
) -> None:
if initial_state is None and not errors:
raise ClassiqError("Missing initial state for ValidationHandle")
self._state = initial_state or HandleState.ERRORED
self.errors = errors or []
@property
def state(self) -> HandleState:
return self._state
def append_error(self, error: str) -> None:
self.errors.append(error)
self._state = HandleState.ERRORED
def initialize(self) -> None:
self._state = HandleState.INITIALIZED
def uninitialize(self) -> None:
self._state = HandleState.UNINITIALIZED
builtins
special
structs
ChemistryAtom
dataclass
ChemistryAtom(element: classiq.qmod.qmod_parameter.CInt, position: 'Position')
Source code in classiq/qmod/builtins/structs.py
@dataclass
class ChemistryAtom:
element: CInt
position: "Position"
CombinatorialOptimizationSolution
dataclass
CombinatorialOptimizationSolution(probability: classiq.qmod.qmod_parameter.CReal, cost: classiq.qmod.qmod_parameter.CReal, solution: classiq.qmod.qmod_parameter.CArray[(
Source code in classiq/qmod/builtins/structs.py
@dataclass
class CombinatorialOptimizationSolution:
probability: CReal
cost: CReal
solution: CArray[CInt]
count: CInt
FinanceFunction
dataclass
FinanceFunction(f: classiq.qmod.qmod_parameter.CInt, threshold: classiq.qmod.qmod_parameter.CReal, larger: classiq.qmod.qmod_parameter.CBool, polynomial_degree: classiq.qmod.qmod_parameter.CInt, use_chebyshev_polynomial_approximation: classiq.qmod.qmod_parameter.CBool, tail_probability: classiq.qmod.qmod_parameter.CReal)
Source code in classiq/qmod/builtins/structs.py
@dataclass
class FinanceFunction:
f: CInt
threshold: CReal
larger: CBool
polynomial_degree: CInt
use_chebyshev_polynomial_approximation: CBool
tail_probability: CReal
FockHamiltonianProblem
dataclass
FockHamiltonianProblem(mapping: classiq.qmod.qmod_parameter.CInt, z2_symmetries: classiq.qmod.qmod_parameter.CBool, terms: classiq.qmod.qmod_parameter.CArray[(ForwardRef('LadderTerm'),)], num_particles: classiq.qmod.qmod_parameter.CArray[(
Source code in classiq/qmod/builtins/structs.py
@dataclass
class FockHamiltonianProblem:
mapping: CInt
z2_symmetries: CBool
terms: CArray["LadderTerm"]
num_particles: CArray[CInt]
GaussianModel
dataclass
GaussianModel(num_qubits: classiq.qmod.qmod_parameter.CInt, normal_max_value: classiq.qmod.qmod_parameter.CReal, default_probabilities: classiq.qmod.qmod_parameter.CArray[(
Source code in classiq/qmod/builtins/structs.py
@dataclass
class GaussianModel:
num_qubits: CInt
normal_max_value: CReal
default_probabilities: CArray[CReal]
rhos: CArray[CReal]
loss: CArray[CInt]
min_loss: CInt
LadderOp
dataclass
LadderOp(op: classiq.qmod.builtins.enums.LadderOperator, index: classiq.qmod.qmod_parameter.CInt)
Source code in classiq/qmod/builtins/structs.py
@dataclass
class LadderOp:
op: LadderOperator
index: CInt
LadderTerm
dataclass
LadderTerm(coefficient: classiq.qmod.qmod_parameter.CReal, ops: classiq.qmod.qmod_parameter.CArray[(ForwardRef('LadderOp'),)])
Source code in classiq/qmod/builtins/structs.py
@dataclass
class LadderTerm:
coefficient: CReal
ops: CArray["LadderOp"]
LogNormalModel
dataclass
LogNormalModel(num_qubits: classiq.qmod.qmod_parameter.CInt, mu: classiq.qmod.qmod_parameter.CReal, sigma: classiq.qmod.qmod_parameter.CReal)
Source code in classiq/qmod/builtins/structs.py
@dataclass
class LogNormalModel:
num_qubits: CInt
mu: CReal
sigma: CReal
Molecule
dataclass
Molecule(atoms: classiq.qmod.qmod_parameter.CArray[(ForwardRef('ChemistryAtom'),)], spin: classiq.qmod.qmod_parameter.CInt, charge: classiq.qmod.qmod_parameter.CInt)
Source code in classiq/qmod/builtins/structs.py
@dataclass
class Molecule:
atoms: CArray["ChemistryAtom"]
spin: CInt
charge: CInt
MoleculeProblem
dataclass
MoleculeProblem(mapping: classiq.qmod.qmod_parameter.CInt, z2_symmetries: classiq.qmod.qmod_parameter.CBool, molecule: 'Molecule', freeze_core: classiq.qmod.qmod_parameter.CBool, remove_orbitals: classiq.qmod.qmod_parameter.CArray[(
Source code in classiq/qmod/builtins/structs.py
@dataclass
class MoleculeProblem:
mapping: CInt
z2_symmetries: CBool
molecule: "Molecule"
freeze_core: CBool
remove_orbitals: CArray[CInt]
PauliTerm
dataclass
PauliTerm(pauli: classiq.qmod.qmod_parameter.CArray[(
Source code in classiq/qmod/builtins/structs.py
@dataclass
class PauliTerm:
pauli: CArray[Pauli]
coefficient: CReal
Position
dataclass
Position(x: classiq.qmod.qmod_parameter.CReal, y: classiq.qmod.qmod_parameter.CReal, z: classiq.qmod.qmod_parameter.CReal)
Source code in classiq/qmod/builtins/structs.py
@dataclass
class Position:
x: CReal
y: CReal
z: CReal
QSVMFeatureMapPauli
dataclass
QSVMFeatureMapPauli(feature_dimension: classiq.qmod.qmod_parameter.CInt, reps: classiq.qmod.qmod_parameter.CInt, entanglement: classiq.qmod.qmod_parameter.CInt, alpha: classiq.qmod.qmod_parameter.CReal, paulis: classiq.qmod.qmod_parameter.CArray[(classiq.qmod.qmod_parameter.CArray[(
Source code in classiq/qmod/builtins/structs.py
@dataclass
class QSVMFeatureMapPauli:
feature_dimension: CInt
reps: CInt
entanglement: CInt
alpha: CReal
paulis: CArray[CArray[Pauli]]
QsvmResult
dataclass
QsvmResult(test_score: classiq.qmod.qmod_parameter.CReal, predicted_labels: classiq.qmod.qmod_parameter.CArray[(
Source code in classiq/qmod/builtins/structs.py
@dataclass
class QsvmResult:
test_score: CReal
predicted_labels: CArray[CReal]
cfunc
get_caller_locals()
Print the local variables in the caller's frame.
Source code in classiq/qmod/cfunc.py
def get_caller_locals() -> Dict[str, Any]:
"""Print the local variables in the caller's frame."""
import inspect
frame = inspect.currentframe()
try:
assert frame is not None
cfunc_frame = frame.f_back
assert cfunc_frame is not None
caller_frame = cfunc_frame.f_back
assert caller_frame is not None
return caller_frame.f_locals
finally:
# See here for information about the `del`
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack
del frame
native
special
expression_to_qmod
ASTToQMODCode
dataclass
ASTToQMODCode(level: int, decimal_precision: Optional[int], indent_seq: str = ' ')
Source code in classiq/qmod/native/expression_to_qmod.py
@dataclass
class ASTToQMODCode:
level: int
decimal_precision: Optional[int]
indent_seq: str = " "
@property
def indent(self) -> str:
return self.level * self.indent_seq
def visit(self, node: ast.AST) -> str:
return self.ast_to_code(node)
def ast_to_code(self, node: ast.AST) -> str:
if isinstance(node, ast.Module):
return self.indent.join(self.ast_to_code(child) for child in node.body)
elif isinstance(node, ast.Attribute):
# Enum attribute access
if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
raise AssertionError("Error parsing enum attribute access")
if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
raise AssertionError("Error parsing enum attribute access")
return f"{node.value.id!s}::{node.attr!s}"
elif isinstance(node, ast.Name):
return node.id
elif isinstance(node, ast.Num):
if self.decimal_precision is None:
return str(node.n)
return str(np.round(node.n, self.decimal_precision))
elif isinstance(node, ast.Str):
return repr(node.s)
elif isinstance(node, ast.Constant):
return repr(node.value)
elif isinstance(node, ast.BinOp):
return "({} {} {})".format(
self.ast_to_code(node.left),
BINARY_OPS[type(node.op)],
self.ast_to_code(node.right),
)
elif isinstance(node, ast.UnaryOp):
unary_op = UNARY_OPS[type(node.op)]
space = " " if unary_op == "not" else ""
return f"({unary_op}{space}{self.ast_to_code(node.operand)})"
elif isinstance(node, ast.BoolOp):
return "({})".format(
(" " + BOOL_OPS[type(node.op)] + " ").join(
self.ast_to_code(value) for value in node.values
)
)
elif isinstance(node, ast.Compare):
if len(node.ops) != 1 or len(node.comparators) != 1:
raise AssertionError("Error parsing comparison expression.")
return "({} {} {})".format(
self.ast_to_code(node.left),
COMPARE_OPS[type(node.ops[0])],
self.ast_to_code(node.comparators[0]),
)
elif isinstance(node, ast.List):
elts = node.elts
elements = self.indent_items(
lambda: [self.ast_to_code(element) for element in elts]
)
return f"[{elements}]"
elif isinstance(node, ast.Subscript):
return f"{self.ast_to_code(node.value)}[{_remove_redundant_parentheses(self.ast_to_code(node.slice))}]"
elif isinstance(node, ast.Slice):
# A QMOD expression does not support slice step
if node.lower is None or node.upper is None or node.step is not None:
raise AssertionError("Error parsing slice expression.")
return f"{self.ast_to_code(node.lower)}:{self.ast_to_code(node.upper)}"
elif isinstance(node, ast.Call):
func = self.ast_to_code(node.func)
if func == "get_field":
if len(node.args) != 2:
raise AssertionError("Error parsing struct field access.")
field = str(self.ast_to_code(node.args[1])).replace("'", "")
if not IDENTIFIER.match(field):
raise AssertionError("Error parsing struct field access.")
return f"{self.ast_to_code(node.args[0])}.{field}"
elif func == "struct_literal":
if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
raise AssertionError("Error parsing struct literal.")
keywords = node.keywords
initializer_list = self.indent_items(
lambda: [
f"{keyword.arg}={self._cleaned_ast_to_code(keyword.value)}"
for keyword in keywords
if keyword.arg is not None
]
)
return f"{self.ast_to_code(node.args[0])} {{{initializer_list}}}"
else:
return "{}({})".format(
func, ", ".join(self._cleaned_ast_to_code(arg) for arg in node.args)
)
elif isinstance(node, ast.Expr):
return self._cleaned_ast_to_code(node.value)
else:
raise AssertionError("Error parsing expression: unsupported AST node.")
def indent_items(self, items: Callable[[], List[str]]) -> str:
should_indent = (
len("".join([i.strip() for i in items()])) >= LIST_FORMAT_CHAR_LIMIT
)
if should_indent:
self.level += 1
left_ws = "\n" + self.indent
inner_ws = ",\n" + self.indent
else:
left_ws = ""
inner_ws = ", "
items_ = items()
if should_indent:
self.level -= 1
right_ws = "\n" + self.indent
else:
right_ws = ""
return f"{left_ws}{inner_ws.join(items_)}{right_ws}"
def _cleaned_ast_to_code(self, node: ast.AST) -> str:
return _remove_redundant_parentheses(self.ast_to_code(node))
pretty_print
special
expression_to_python
ASTToQMODCode (NodeVisitor)
dataclass
ASTToQMODCode(level: int, imports: Dict[str, int], symbolic_imports: Dict[str, int], decimal_precision: int, indent_seq: str = ' ')
Source code in classiq/qmod/pretty_print/expression_to_python.py
@dataclass
class ASTToQMODCode(ast.NodeVisitor):
level: int
imports: Dict[str, int]
symbolic_imports: Dict[str, int]
decimal_precision: int
indent_seq: str = " "
@property
def indent(self) -> str:
return self.level * self.indent_seq
def _handle_imports(self, name: str, is_possibly_symbolic: bool = False) -> None:
if name in dir(classiq):
self.imports[name] = 1
if is_possibly_symbolic and name in dir(classiq.qmod.symbolic):
self.symbolic_imports[name] = 1
def visit(self, node: ast.AST) -> str:
res = super().visit(node)
if not isinstance(res, str):
raise AssertionError("Error parsing expression: unsupported AST node.")
return res
def visit_Module(self, node: ast.Module) -> str:
return self.indent.join(self.visit(child) for child in node.body)
def visit_Attribute(self, node: ast.Attribute) -> str:
if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
raise AssertionError("Error parsing enum attribute access")
if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
raise AssertionError("Error parsing enum attribute access")
self._handle_imports(node.value.id)
return f"{node.value.id!s}.{node.attr!s}"
def visit_Name(self, node: ast.Name) -> str:
self._handle_imports(node.id, True)
return node.id
def visit_Num(self, node: ast.Num) -> str:
return str(np.round(node.n, self.decimal_precision))
def visit_Str(self, node: ast.Str) -> str:
return repr(node.s)
def visit_Constant(self, node: ast.Constant) -> str:
return repr(node.value)
def visit_BinOp(self, node: ast.BinOp) -> str:
return "({} {} {})".format(
self.visit(node.left),
BINARY_OPS[type(node.op)],
self.visit(node.right),
)
def visit_UnaryOp(self, node: ast.UnaryOp) -> str:
unary_op = UNARY_OPS[type(node.op)]
space = " " if unary_op == "not" else ""
return f"({unary_op}{space}{self.visit(node.operand)})"
def visit_BoolOp(self, node: ast.BoolOp) -> str:
return "({})".format(
(" " + BOOL_OPS[type(node.op)] + " ").join(
self.visit(value) for value in node.values
)
)
def visit_Compare(self, node: ast.Compare) -> str:
if len(node.ops) != 1 or len(node.comparators) != 1:
raise AssertionError("Error parsing comparison expression.")
return "({} {} {})".format(
self.visit(node.left),
COMPARE_OPS[type(node.ops[0])],
self.visit(node.comparators[0]),
)
def visit_List(self, node: ast.List) -> str:
elts = node.elts
elements = self.indent_items(lambda: [self.visit(element) for element in elts])
return f"[{elements}]"
def visit_Subscript(self, node: ast.Subscript) -> str:
return f"{self.visit(node.value)}[{_remove_redundant_parentheses(self.visit(node.slice))}]"
def visit_Slice(self, node: ast.Slice) -> str:
if node.lower is None or node.upper is None or node.step is not None:
raise AssertionError("Error parsing slice expression.")
return f"{self.visit(node.lower)}:{self.visit(node.upper)}"
def visit_Call(self, node: ast.Call) -> str:
func = self.visit(node.func)
self._handle_imports(func, True)
if func == "get_field":
if len(node.args) != 2:
raise AssertionError("Error parsing struct field access.")
field = str(self.visit(node.args[1])).replace("'", "")
if not IDENTIFIER.match(field):
raise AssertionError("Error parsing struct field access.")
return f"{self.visit(node.args[0])}.{field}"
elif func == "struct_literal":
if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
raise AssertionError("Error parsing struct literal.")
keywords = node.keywords
initializer_list = self.indent_items(
lambda: [
f"{keyword.arg} = {self._cleaned_ast_to_code(keyword.value)}"
for keyword in keywords
if keyword.arg is not None
]
)
return f"{self.visit(node.args[0])}({initializer_list})"
else:
return "{}({})".format(
func, ", ".join(self._cleaned_ast_to_code(arg) for arg in node.args)
)
def visit_Expr(self, node: ast.Expr) -> str:
return self._cleaned_ast_to_code(node.value)
def generic_visit(self, node: ast.AST) -> None:
raise AssertionError("Cannot parse node of type: " + type(node).__name__)
def indent_items(self, items: Callable[[], List[str]]) -> str:
should_indent = (
len("".join([i.strip() for i in items()])) >= LIST_FORMAT_CHAR_LIMIT
)
if should_indent:
self.level += 1
left_ws = "\n" + self.indent
inner_ws = ",\n" + self.indent
else:
left_ws = ""
inner_ws = ", "
items_ = items()
if should_indent:
self.level -= 1
right_ws = "\n" + self.indent
else:
right_ws = ""
return f"{left_ws}{inner_ws.join(items_)}{right_ws}"
def _cleaned_ast_to_code(self, node: ast.AST) -> str:
return _remove_redundant_parentheses(self.visit(node))
generic_visit(self, node)
Called if no explicit visitor function exists for a node.
Source code in classiq/qmod/pretty_print/expression_to_python.py
def generic_visit(self, node: ast.AST) -> None:
raise AssertionError("Cannot parse node of type: " + type(node).__name__)
visit(self, node)
Visit a node.
Source code in classiq/qmod/pretty_print/expression_to_python.py
def visit(self, node: ast.AST) -> str:
res = super().visit(node)
if not isinstance(res, str):
raise AssertionError("Error parsing expression: unsupported AST node.")
return res
semantics
special
error_manager
ErrorManager
Source code in classiq/qmod/semantics/error_manager.py
class ErrorManager:
def __new__(cls) -> "ErrorManager":
if not hasattr(cls, "_instance"):
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self) -> None:
if hasattr(self, "_instantiated"):
return
self._instantiated = True
self._errors: List[str] = []
self._current_nodes_stack: List[ASTNode] = []
def add_error(self, error: str) -> None:
source_referenced_error = (
f"{error}\n\t\tat {self._current_nodes_stack[-1].source_ref}"
if self._current_nodes_stack
and self._current_nodes_stack[-1].source_ref is not None
else error
)
self._errors.append(source_referenced_error)
def get_errors(self) -> List[str]:
return self._errors
def clear(self) -> None:
self._current_nodes_stack = []
self._errors = []
def has_errors(self) -> bool:
return len(self._errors) > 0
def report_errors(self, error_type: Type[Exception]) -> None:
if self.has_errors():
errors = self._errors
self.clear()
raise error_type("\n\t" + "\n\t".join(errors))
@contextmanager
def node_context(self, node: ASTNode) -> Iterator[None]:
self._current_nodes_stack.append(node)
yield
self._current_nodes_stack.pop()
__new__(cls)
special
staticmethod
Create and return a new object. See help(type) for accurate signature.
Source code in classiq/qmod/semantics/error_manager.py
def __new__(cls) -> "ErrorManager":
if not hasattr(cls, "_instance"):
cls._instance = super().__new__(cls)
return cls._instance
all_hardware_devices
get_all_hardware_devices()
Returns a list of all hardware devices known to Classiq.
Source code in classiq/execution/all_hardware_devices.py
def get_all_hardware_devices() -> List[HardwareInformation]:
"""
Returns a list of all hardware devices known to Classiq.
"""
return async_utils.run(ApiWrapper.call_get_all_hardware_devices())