Skip to content

Fidelity reported by GRAPE doesn't match manually computed one #46

@pmenczel

Description

@pmenczel

The following script sets up a simple example problem where a (dissipative) qubit should be transferred from the ground state to the excited state. We use GRAPE and ask for an infidelity smaller then 0.001. We then use the optimized control returned by GRAPE to re-run the time evolution manually.

The final infidelity claimed by GRAPE is 0.0008. The infidelity found in the manual run is 0.0038.

A small discrepancy would be fine due to numerical inaccuracies, different integration methods etc, and small discrepancies also occur using other algorithms. However, when we ask for an infidelity of 0.001 and the result returned by GRAPE misses it by almost 300%, it seems like an issue to me.

import numpy as np
import qutip as qt
from qutip_qoc import Objective, optimize_pulses

def infidelity(state, target_state):
    """
    Infidelity used for density matrices in qutip-qtrl and qutip-qoc
    """
    diff = state - target_state
    return np.real(diff.overlap(diff)) / (2 * target_state.norm())

# --- Setup problem ---
Hd = qt.Qobj(np.diag([1, 2]))
c_ops = [np.sqrt(0.1) * qt.sigmam()]
Hc = qt.sigmax()

Ld = qt.liouvillian(H=Hd, c_ops=c_ops)
Lc = qt.liouvillian(Hc)
L = [Ld, Lc]

initial_state = qt.fock_dm(2, 0)
target_state = qt.fock_dm(2, 1)

times = np.linspace(0, 2 * np.pi, 250)

# --- Run GRAPE ---
guess_pulse = np.sin(times)

res_grape = optimize_pulses(
    objectives = Objective(initial_state, L, target_state),
    control_parameters = {
        "ctrl_x": {"guess": guess_pulse, "bounds": [-1, 1]},
    },
    tlist = times,
    algorithm_kwargs = {
        "alg": "GRAPE",
        "fid_err_targ": 0.001,
    },
)

grape_infidelity = res_grape.infidelity

# --- Manually run evolution ---
state = initial_state
dt = times[1] - times[0]

for c in res_grape.optimized_controls[0][:-1]:
    propagator = ((Ld + c * Lc) * dt).expm()
    state = propagator(state)

real_infidelity = infidelity(state, target_state)

# --- These should be the same ---
print(grape_infidelity)
print(real_infidelity)
assert np.isclose(grape_infidelity, real_infidelity, atol=1e-3)

Note that I do the manual evolution by just exponentiating the propagator on every time step, since GRAPE protocols are piecewise constant. Doing the manual evolution with mesolve gives the same result as this.

This issue was discovered while creating the interactive test notebooks in #44

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions