Arithmetic Expressions¶
Use the Arithmetic
function to write complex mathematical expression in free format.
The notation follows the Python language for math notation.
The function first parses the expression and builds an abstract syntax tree (AST). Then, the Classiq engine finds a computation strategy for a specified number of qubits, and compiles the desired circuit.
As opposed to classical computers, when quantum computers evaluate arithmetic expression, the calculations are reversible and are applied on all quantum states in parallel. To do so, quantum computers store all intermediate computation results in a quantum registers. Qubits that are not freed cannot be used later on in the circuit.
Analogously to the classical world, there is a form of quantum "garbage collection", usually referred to as uncomputation, which returns the garbage qubits to their original state. The computation strategy determines the order in which qubits are released and reused. By employing different strategies, you can produce a variety of circuits with the same functionality. In general, longer circuits require less qubits than shorter ones.
The Classiq platform offers several computation strategies you can specify under the uncomputation_method
argument:
- The
naive
strategy [1] traverses the AST in topological order. - The
optimized
strategy chooses a more complicated order of computations and uncomputations, given constraints.
Besides computation strategies, you can allow input override, which may save both qubits and depth.
Do it using the inputs_to_save
field, defaulted to the empty set.
Every input you specify in this set is available as an output of the arithmetic function.
Specify inputs by name, to match keys from the definitions
field.
The result of the arithmetic operation is named expression_result
.
Furthermore, many arithmetic simplifications are available through the 'simplify' parameter, allowing substantial shortening of the circuit. Among them are methods such as collecting terms, replacing operations, order commuting operations, and many more.
Supported operators:
- Add:
+
- Subtract:
-
(two arguments) - Negate:
-
(a single argument) - Multiply:
*
- Bitwise Or:
|
- Bitwise And:
&
- Bitwise Xor:
^
- Invert:
~
- Equal:
==
- Not Equal:
!=
- Greater Than:
>
- Greater Or Equal:
>=
- Less Than:
<
- Less Or Equal:
<=
- Modulo:
%
limited for power of 2 - Logical And:
and
- Logical Or:
or
- Right Bit Shift:
>>
- Left Bit Shift:
<<
- Cyclic Right Bit Shift:
CRShift
- Cyclic Left Bit Shift:
CLShift
- Max:
max
(n>=2 arguments) - Min:
min
(n>=2 arguments)
Syntax¶
Function: Arithmetic
Parameters:
expression: str
definitions: Dict[str, Union[int, float, RegisterUserInput]
(see RegisterUserInput)uncomputation_method: ['naive', 'optimized']
inputs_to_save: Set[str]
qubit_count: Optional[PositiveInt]
simplify: bool
max_fraction_places: Optional[PositiveInt]
{
"function": "Arithmetic",
"function_params": {
"expression": "a ^ 3 + b + (2 - c % 4) - max(a, b, -c)",
"definitions": {
"a": {
"size": 2
},
"b": {
"size": 1
},
"c": {
"size": 3
}
},
"uncomputation_method": "optimized",
"qubit_count": 25
}
}
Register Names¶
Determine the names of the input registers using the definitions
field. Each defined
variable has a corresponding input register with the same name.
The result is then placed in the expression_result
output register. If you set the allow_input_override
flag to False
, all inputs are also available as outputs, and thus have corresponding output registers with the same names.
Example¶
{
"functions": [
{
"name": "main",
"body": [
{
"function": "Arithmetic",
"function_params": {
"expression": "(a + b + c & 15) % 8 ^ 3 & a ^ 10 == 4",
"definitions": {
"a": {
"size": 2,
"is_signed": false
},
"b": {
"size": 1
},
"c": {
"size": 3
}
},
"uncomputation_method": "optimized",
"qubit_count": 25
}
}
]
}
]
}
from classiq import Model, RegisterUserInput, synthesize
from classiq.builtin_functions import Arithmetic
params = Arithmetic(
expression="(a + b + c & 15) % 8 ^ 3 & a ^ 10 == 4",
definitions=dict(
a=RegisterUserInput(size=2, is_signed=False),
b=RegisterUserInput(size=1),
c=RegisterUserInput(size=3),
),
uncomputation_method="optimized",
qubit_count=25,
)
result = params.outputs[
"expression_result"
] # The type of the operation result quantum register
model = Model()
model.Arithmetic(params)
quantum_program = synthesize(model.get_model())
This example generates a circuit that calculates the expression \(\verb|(a + b + c & 15) % 8 ^ 3 & a ^ 10 == 4|\).
Each of the variables a
,b
, and c
is defined as a quantum register in the definitions.
The uncomputation strategy is set to optimized
and the maximum number of qubits allowed for the segment is 25.
Output circuit:
References¶
[1]C. H. Bennett, “Time/space trade-offs for reversible computation,” SIAM Journal on Computing, vol. 18, no. 4, pp. 766–776, 1989.