krotov.objectives module¶
Summary¶
Data:
CtrlCounter |
Constructor for a counter of controls. |
Objective |
A single objective for optimization with Krotov’s method |
ensemble_objectives |
Extend objectives for an “ensemble optimization” |
gate_objectives |
Construct a list of objectives for optimizing towards a quantum gate |
summarize_qobj |
Summarize a quantum object |
__all__
: CtrlCounter
, Objective
, ensemble_objectives
, gate_objectives
, summarize_qobj
Reference¶
-
krotov.objectives.
FIX_QUTIP_932
= True¶ Workaround for QuTiP issue 932.
If True, and only when running on macOS, in
Objective.mesolve()
, replace any array controls with an equivalent function. This results in a signficant slowdown of the propagation, as it circumvents the use of Cython.
-
krotov.objectives.
CtrlCounter
()[source]¶ Constructor for a counter of controls.
Returns a callable that returns a unique integer (starting at 1) for every distinct control that is passed to it. This is intended for use with
summarize_qobj()
.Example
>>> ctrl_counter = CtrlCounter() >>> ctrl1 = np.zeros(10) >>> ctrl_counter(ctrl1) 1 >>> ctrl2 = np.zeros(10) >>> assert ctrl2 is not ctrl1 >>> ctrl_counter(ctrl2) 2 >>> ctrl_counter(ctrl1) 1 >>> ctrl3 = lambda t, args: 0.0 >>> ctrl_counter(ctrl3) 3
-
class
krotov.objectives.
Objective
(*, initial_state, H, target, c_ops=None)[source]¶ Bases:
object
A single objective for optimization with Krotov’s method
Parameters: - initial_state (qutip.Qobj) – value for
initial_state
- H (qutip.Qobj or list) – value for
H
- target (qutip.Qobj or None) – value for
target
- c_ops (list or None) – value for
c_ops
-
H
¶ The (time-dependent) Hamiltonian, cf.
qutip.mesolve.mesolve()
. This includes the control fields.Type: qutip.Qobj or list
-
initial_state
¶ The initial state
Type: qutip.Qobj
-
target
¶ An object describing the “target” of the optimization, for the dynamics starting from
initial_state
. Usually, this will be the target state (the state into whichinitial_state
should evolve). More generally, it can be an arbitrary object meeting the requirements of a specific chi_constructor function that will be passed tooptimize_pulses()
.
Raises: ValueError
– If any arguments have an invalid type-
adjoint
¶ The
Objective
containing the adjoint of all components.This does not affect the controls in
H
: these are assumed to be real-valued. Also,Objective.target
will be left unchanged if it is not aqutip.Qobj
.
-
mesolve
(tlist, rho0=None, e_ops=None, **kwargs)[source]¶ Run
qutip.mesolve.mesolve()
on the system of the objectiveSolve the dynamics for the
H
andc_ops
of the objective. If rho0 is not given, theinitial_state
will be propagated. All other arguments will be passed toqutip.mesolve.mesolve()
.Returns: Result of the propagation, see qutip.mesolve.mesolve()
for details.Return type: qutip.solver.Result
-
propagate
(tlist, *, propagator, rho0=None, e_ops=None)[source]¶ Propagates the system of the objective over the entire time grid
Solve the dynamics for the H and c_ops of the objective. If rho0 is not given, the initial_state will be propagated. This is similar to the
mesolve()
method, but instead of usingqutip.mesolve.mesolve()
, the propagate function is used to go between points on the time grid. This function is the same as what is passed tooptimize_pulses()
. The crucial difference between this andmesolve()
is in the time discretization convention. Whilemesolve()
uses piecewise-constant controls centered around the values in tlist (the control field switches in the middle between two points in tlist),propagate()
uses piecewise-constant controls on the intervals of tlist (the control field switches on the points in tlist)Comparing the result of
mesolve()
andpropagate()
allows to estimate the “time discretization error”. If the error is significant, a shorter time step shoud be used.Returns: Result of the propagation, using the same structure as mesolve()
.Return type: qutip.solver.Result
-
summarize
(ctrl_counter=None)[source]¶ Return a one-line summary of the objective as a string
Example
>>> from qutip import ket, tensor, sigmaz, sigmax, sigmap, identity >>> u1 = lambda t, args: 1.0 >>> u2 = lambda t, args: 1.0 >>> a1 = np.random.random(100) + 1j*np.random.random(100) >>> a2 = np.random.random(100) + 1j*np.random.random(100) >>> H = [ ... tensor(sigmaz(), identity(2)) + ... tensor(identity(2), sigmaz()), ... [tensor(sigmax(), identity(2)), u1], ... [tensor(identity(2), sigmax()), u2]] >>> C1 = [tensor(identity(2), sigmap()), a1] >>> C2 = [tensor(sigmap(), identity(2)), a2] >>> ket00 = ket((0,0)) >>> ket11 = ket((1,1)) >>> obj = Objective( ... initial_state=ket00, ... target=ket11, ... H=H ... ) >>> obj.summarize() '|(2⊗2)⟩ - {[Herm[2⊗2,2⊗2], [Herm[2⊗2,2⊗2], u1(t)], [Herm[2⊗2,2⊗2], u2(t)]]} - |(2⊗2)⟩' >>> obj = Objective( ... initial_state=ket00, ... target=ket11, ... H=H, ... c_ops=[C1, C2] ... ) >>> obj.summarize() '|(2⊗2)⟩ - {H:[Herm[2⊗2,2⊗2], [Herm[2⊗2,2⊗2], u1(t)], [Herm[2⊗2,2⊗2], u2(t)]], c_ops:([NonHerm[2⊗2,2⊗2], u3[complex128]],[NonHerm[2⊗2,2⊗2], u4[complex128]])} - |(2⊗2)⟩'
- initial_state (qutip.Qobj) – value for
-
krotov.objectives.
summarize_qobj
(obj, ctrl_counter=None)[source]¶ Summarize a quantum object
A counter created by
CtrlCounter()
may be passed to distinguish control fields.Example
>>> ket = qutip.ket([1, 0, 1]) >>> summarize_qobj(ket) '|(2⊗2⊗2)⟩' >>> bra = ket.dag() >>> summarize_qobj(bra) '⟨(2⊗2⊗2)|' >>> rho = ket * bra >>> summarize_qobj(rho) 'Herm[2⊗2⊗2,2⊗2⊗2]' >>> a = qutip.create(10) >>> summarize_qobj(a) 'NonHerm[10,10]' >>> S = qutip.to_super(a) >>> summarize_qobj(S) '[[10,10],[10,10]]'
-
krotov.objectives.
gate_objectives
(basis_states, gate, H, c_ops=None, local_invariants=False)[source]¶ Construct a list of objectives for optimizing towards a quantum gate
Parameters: - basis_states (list[qutip.Qobj]) – A list of \(n\) canonical basis states
- gate – The gate to optimize for, as a \(n \times n\) matrix-like object (must have a shape attribute, and be indexable by two indices). Alternatively, gate may be the string ‘perfect_entangler’ or ‘PE’, to indicate the optimization for an arbitrary two-qubit perfect entangler.
- H (list or qutip.Qobj) – The Hamiltonian for the time evolution
- c_ops (list or None) – A list of collapse (Lindblad) operators, or None for unitary dynamics
- local_invariants (bool) – If True, initialize the objectives for an optimization towards a two-qubit gate that is “locally equivalent” to gate. That is, the result of the optimization should implement gate up to single-qubit operations.
Returns: The objectives that define the optimization towards the gate. For a “normal” gate with a basis in Hilbert space, the objectives will have the basis_states as each
initial_state
and the result of applying gate to the basis_states as eachtarget
.For an optimization towards a perfect-entangler, or for the local_invariants of the given gate, each
initial_state
will be the Bell basis state described in “Theorem 1” in Y. Makhlin, Quantum Inf. Process. 1, 243 (2002), derived from the canonical basis_states. Thetarget
will be the string ‘PE’ for a perfect-entanglers optimization, and gate for the local-invariants optimization.Return type: Raises: ValueError
– If gate, basis_states, and local_invariants are incompatible, or gate is invalid (not a recognized string)Note
The dimension of the basis_states is not required to be the dimension of the gate; the basis_states may define a logical subspace in a larger Hilbert space.
Examples
A single-qubit gate:
>>> from qutip import ket, tensor, sigmaz, sigmax, sigmay, identity >>> basis = [ket([0]), ket([1])] >>> gate = sigmay() >>> gate Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True Qobj data = [[0.+0.j 0.-1.j] [0.+1.j 0.+0.j]] >>> H = [sigmaz(),[sigmax(), lambda t, args: 1.0]] >>> objectives = gate_objectives(basis, gate, H) >>> assert objectives == [ ... Objective( ... initial_state=basis[0], ... target=(1j * basis[1]), ... H=H ... ), ... Objective( ... initial_state=basis[1], ... target=(-1j * basis[0]), ... H=H ... ) ... ]
An arbitrary two-qubit perfect entangler:
>>> basis = [ket(n) for n in [(0, 0), (0, 1), (1, 0), (1, 1)]] >>> H = [ ... tensor(sigmaz(), identity(2)) + ... tensor(identity(2), sigmaz()), ... [tensor(sigmax(), identity(2)), lambda t, args: 1.0], ... [tensor(identity(2), sigmax()), lambda t, args: 1.0]] >>> objectives = gate_objectives(basis, 'PE', H) >>> from weylchamber import bell_basis >>> for i in range(4): ... assert objectives[i] == Objective( ... initial_state=bell_basis(basis)[i], ... target='PE', ... H=H ... )
A two-qubit gate, up to single-qubit operation (“local invariants”):
>>> objectives = gate_objectives( ... basis, qutip.gates.cnot(), H, local_invariants=True ... ) >>> for i in range(4): ... assert objectives[i] == Objective( ... initial_state=bell_basis(basis)[i], ... target=qutip.gates.cnot(), ... H=H ... )
-
krotov.objectives.
ensemble_objectives
(objectives, Hs)[source]¶ Extend objectives for an “ensemble optimization”
This creates a list of objectives for an optimization for robustness with respect to variations in some parameter of the Hamiltonian. The trick is to simply optimize over the average of multiple copies of the system (the Hs) sampling that variation. See Goerz, Halperin, Aytac, Koch, Whaley. Phys. Rev. A 90, 032329 (2014) for details.
Parameters: Returns: List of \(n (m+1)\) new objectives that consists of the original objectives, plus one copy of the original objectives per element of Hs where the H attribute of each objectives is replaced by that element.
Return type: