Analog computation addon

The analog computation addon is for creating analog schedules that can be run on the quantum hardware or simulator.

Schedule from ParityOSOutputs

The analog schedule for the Party compiled problem can be represented as follows [Lechner15]:

\[H(t) = A(t) H_d + B(t) H_p + C(t) H_c\]

where \(H_d\) is the driver Hamiltonian (e.g. \(\sum_{i=1}^n{-X}\), where \(n\) is the number of qubits), \(H_p\) is the problem Hamiltonian, \(H_c\) is the constraint Hamiltonian, \(A(t)\), \(B(t)\) and \(C(t)\) are time dependent functions that define the analog schedule. As an example one can define the time dependent parameters in the following way:

\[A(t) = 1 - t\]
\[B(t) = t\]
\[C(t) = t\]

where \(t \in [0,1]\) is the time parameter.

Here’s how to create the above-defined schedule from ParityOSOutput using the analog computation addon (assuming parityos_output is given – see Quickstart section).

import sympy

from parityos_addons.analog_computation import ParitySchedule

t = sympy.Symbol("t")
duration = 1.0
problem_expr = t
constraint_expr = t
driver_expr = duration - t

parity_schedule = ParitySchedule(
    output=parityos_output,
    interactions_coefficient=problem_expr,
    constraints_coefficient=constraint_expr,
    driver_coefficient=driver_expr,
    time_parameter=t,
    duration=duration,
)

The ParitySchedule is using the default driver Hamiltonian \(H_d = \sum_{i=0}^n{-X}\). One can access interactions_schedule, constraints_schedule and driver_schedule properties from a ParitySchedule, which are going to give the corresponding ScheduleTerm instances.

Custom Schedules

For constructing a more generic schedule (not necessarily related to LHZ arcitecture) one can use Schedule class. For that one should define the corresponding Observable and time dependent expression pairs to define ScheduleTerms, which are going to be summed up in the Schedule class.

For example let’s define a Schedule that consists of two ScheduleTerms. One will be the driver ScheduleTerm:

from parityos import Qubit
from parityos.base.gates import X
from parityos_addons.analog_computation import Observable, PauliOp, ScheduleTerm

qubit_count = 3
qubits = [Qubit(index) for index in range(qubit_count)]

driver_interactions = [
    PauliOp([X(qubits[0])]),
    PauliOp([X(qubits[1])]),
    PauliOp([X(qubits[2])]),
    ]
driver_coefficient = [-1] * len(driver_interactions)
driver_observable = Observable(driver_interactions, driver_coefficient)

t = sympy.Symbol('t')
driver_expression = 1 - t
driver_schedule_term = ScheduleTerm(driver_observable, driver_expression)

And the other one will be the problem ScheduleTerm:

from parityos.base.gates import Z

problem_interactions = [
    PauliOp([Z(qubits[0]), Z(qubits[1])]),
    PauliOp([Z(qubits[0]), Z(qubits[2])]),
    PauliOp([Z(qubits[1]), Z(qubits[2])]),
    ]
problem_coefficient = [1] * len(problem_interactions)
problem_observable = Observable(problem_interactions, problem_coefficient)

problem_expression = t
problem_schedule_term = ScheduleTerm(problem_observable, problem_expression)

Then one just need to combine these two ScheduleTerms into a single Schedule instance:

from parityos_addons.analog_computation import Schedule

custom_schedule = Schedule([problem_schedule_term, driver_schedule_term], t)

The Schedule class is more generic than ParitySchedule class and one can obtain the corresponding Schedule for the given ParitySchedule in the following way:

schedule = parity_schedule.schedule

References

[Lechner15]

Lechner, Wolfgang, Philipp Hauke, and Peter Zoller. “A quantum annealing architecture with all-to-all connectivity from local interactions.” Science advances 1.9 (2015): e1500838.