Defining Algorithms¶
Quantum algorithms are constructed from functions, which are the functional building blocks that implement quantum logic, and the data flow between them.
Calling a Function¶
As in classical programming, calling a function requires specifying its name and its parameters. The following example calls a state preparation function, which is a built-in function developed by Classiq. There are multiple such functions, listed in the Built-In Function Library. You can also define your own functions, as described in the Function section.
To call a function using the textual interface, list it under the
body
field and pass on its parameters under the function_params
field of the
function.
{
"functions": [
{
"name": "main",
"body": [{
"function": "StatePreparation",
"function_params": {
"probabilities": [0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125]
}
}
]
}
]
}
In the SDK, create the StatePreparation
object, which represents the parameters of
the function, and then pass it on to the StatePreparation
method of the Model
object.
from classiq import Model, synthesize, show
from classiq.builtin_functions import StatePreparation
model = Model()
probabilities = (0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125)
sp_params = StatePreparation(probabilities=probabilities)
model.StatePreparation(sp_params)
qprog = synthesize(model.get_model())
show(qprog)
You can also determine the name of a call, e.g., to distinguish multiple calls to
the same function. Do it by setting the name
field of the call in the textual
interface or the call_name
argument in the SDK.
Defining the Data Flow¶
Define the data flow between the functions by connecting an output of a function to an input of another function.
The following example connects the output of the state preparation function to the input of the quantum Fourier transform (QFT) function.
To define a connection using the textual model, name the connection
"sp_out"
, and list it under the outputs of StatePreparation
and the inputs of QFT
.
{
"functions": [
{
"name": "main",
"body": [{
"function": "StatePreparation",
"function_params": {
"probabilities": [0.5, 0.1, 0.2, 0.005, 0.015, 0.12, 0.035, 0.025],
"error_metric": { "KL": {"upper_bound": 0.3}}
},
"outputs": "sp_out"
},
{
"function": "QFT",
"function_params": {
"num_qubits": 3
},
"inputs": "sp_out"
}
]
}
]
}
The outputs of a called function in the SDK are returned in a dictionary. The keys
of the dictionary are the function output names. The values of the dictionary are
Wire
objects that represent the outputs.
The inputs of a function are also defined in a dictionary and passed to the in_wires
argument. Similarly, the keys of the dictionary are the input names and the values
are a Wire
object. Passing a Wire
object that represents an output to the input
of another function connects the two.
from classiq import Model, synthesize, show
from classiq.builtin_functions import QFT, StatePreparation
from classiq.builtin_functions.state_preparation import Metrics
from classiq.builtin_functions.range_types import NonNegativeFloatRange
model = Model()
num_qubits = 3
probabilities = (0.5, 0.1, 0.2, 0.005, 0.015, 0.12, 0.035, 0.025)
sp_params = StatePreparation(
probabilities=probabilities,
error_metric={Metrics.KL: NonNegativeFloatRange(upper_bound=0.3)},
)
x = model.StatePreparation(sp_params)["OUT"]
qft_params = QFT(num_qubits=num_qubits)
model.QFT(qft_params, in_wires=x)
qprog = synthesize(model.get_model())
show(qprog)
The state preparation function has a single pre-defined output, and the QFT function has a single pre-defined input. In this case, the Classiq platform allows skipping the explicit specification of each, requiring only to define the connection. The following example shows how to handle functions with multiple inputs/outputs. See how to split a single input/output into multiple ones using register slicing.
The following example implements the addition of two prepared states.
The states are prepared using the StatePreparation
function and
the functions' outputs are added using the Adder
function. Use
the left_arg
and right_arg
inputs of the adder function to
connect each state preparation output into its corresponding
addend. Generally, each function has a set of predefined input and output
names.
{
"functions": [
{
"name": "main",
"body": [{
"function": "StatePreparation",
"function_params": {
"probabilities": [0.4, 0.05, 0.2, 0.05, 0.3, 0.0, 0.0, 0.0],
"error_metric": {"KL": {"upper_bound": 0.2}}
},
"outputs": "sp_output_a",
"name": "state_preparation_1"
},
{
"function": "StatePreparation",
"function_params": {
"probabilities": [0.5, 0.1, 0.2, 0.005, 0.015, 0.12, 0.035, 0.025],
"error_metric": {"KL": {"upper_bound": 0.2}}
},
"outputs": "sp_output_b",
"name": "state_preparation_2"
},
{
"function": "Adder",
"function_params": {
"left_arg": { "size": 3 },
"right_arg": { "size": 3 }
},
"inputs": {
"left_arg": "sp_output_a",
"right_arg": "sp_output_b"
},
"name": "adder"
}]
}
]
}
from classiq import Model, RegisterUserInput, synthesize, show
from classiq.builtin_functions import StatePreparation, Adder
from classiq.builtin_functions.state_preparation import Metrics
from classiq.builtin_functions.range_types import NonNegativeFloatRange
model = Model()
num_qubits = 3
probabilities_a = (0.4, 0.05, 0.2, 0.05, 0.3, 0.0, 0.0, 0.0)
error_metric_a = {Metrics.KL: NonNegativeFloatRange(upper_bound=0.2)}
sp_params_a = StatePreparation(
probabilities=probabilities_a,
error_metric=error_metric_a,
)
x = model.StatePreparation(sp_params_a, call_name="state_preparation_1")["OUT"]
probabilities_b = (0.5, 0.1, 0.2, 0.005, 0.015, 0.12, 0.035, 0.025)
error_metric_b = {Metrics.KL: NonNegativeFloatRange(upper_bound=0.2)}
sp_params_b = StatePreparation(
probabilities=probabilities_b,
error_metric=error_metric_b,
)
y = model.StatePreparation(sp_params_b, call_name="state_preparation_2")["OUT"]
adder_arg = RegisterUserInput(size=num_qubits)
adder_params = Adder(
left_arg=x.to_register_user_input(), right_arg=y.to_register_user_input()
)
model.Adder(adder_params, in_wires=[x, y], call_name="adder")
qprog = synthesize(model.get_model())
show(qprog)
The output quantum program is shown below at the functional level.