From b0fd00c747b1997886d5350afab32d1d9f83ed29 Mon Sep 17 00:00:00 2001 From: Rochisha Agarwal Date: Wed, 26 Feb 2025 08:09:30 +0530 Subject: [PATCH 1/5] add check for operator input --- src/qutip_qoc/pulse_optim.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/qutip_qoc/pulse_optim.py b/src/qutip_qoc/pulse_optim.py index 94d424c..349cebf 100644 --- a/src/qutip_qoc/pulse_optim.py +++ b/src/qutip_qoc/pulse_optim.py @@ -11,6 +11,8 @@ from qutip_qoc._optimizer import _global_local_optimization from qutip_qoc._time import _TimeInterval +import qutip as qt + try: from qutip_qoc._rl import _RL _rl_available = True @@ -191,6 +193,11 @@ def optimize_pulses( # prepare qtrl optimizers qtrl_optimizers = [] if alg == "CRAB" or alg == "GRAPE": + dyn_type = "GEN_MAT" + for objective in objectives: + if any(qt.isoper(H_i) for H_i in (objective.H if isinstance(objective.H, list) else [objective.H])): + dyn_type = "UNIT" + if alg == "GRAPE": # algorithm specific kwargs use_as_amps = True minimizer_kwargs.setdefault("method", "L-BFGS-B") # gradient @@ -247,7 +254,7 @@ def optimize_pulses( "accuracy_factor": None, # deprecated "alg_params": alg_params, "optim_params": algorithm_kwargs.get("optim_params", None), - "dyn_type": algorithm_kwargs.get("dyn_type", "GEN_MAT"), + "dyn_type": algorithm_kwargs.get("dyn_type", dyn_type), "dyn_params": algorithm_kwargs.get("dyn_params", None), "prop_type": algorithm_kwargs.get( "prop_type", "DEF" From 37de33fef4d5d2bb56f1126f8a319b31187c45a1 Mon Sep 17 00:00:00 2001 From: Rochisha Agarwal Date: Mon, 10 Mar 2025 09:44:33 +0530 Subject: [PATCH 2/5] Fix objective for open system --- src/qutip_qoc/objective.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/qutip_qoc/objective.py b/src/qutip_qoc/objective.py index d91caac..06b8294 100644 --- a/src/qutip_qoc/objective.py +++ b/src/qutip_qoc/objective.py @@ -83,8 +83,14 @@ def __init__(self, initial, H, target, weight=1): # Check if any Hamiltonian in H is a superoperator if any(qt.issuper(H_i) for H_i in (H if isinstance(H, list) else [H])): # Convert initial and target accordingly - self.initial = qt.to_super(self.initial) - self.target = qt.to_super(self.target) + if qt.isket(self.initial): + self.initial = qt.operator_to_vector(qt.ket2dm(self.initial)) + elif qt.isoper(self.initial): + self.initial = qt.operator_to_vector(self.initial) + if qt.isket(self.target): + self.target = qt.operator_to_vector(qt.ket2dm(self.target)) + elif qt.isoper(self.target): + self.target = qt.operator_to_vector(self.target) def __getstate__(self): """ From e2781bcd1f02da591eee876e7334ddfa5bb698b8 Mon Sep 17 00:00:00 2001 From: Rochisha Agarwal Date: Wed, 12 Mar 2025 12:18:30 +0530 Subject: [PATCH 3/5] add logic for gate synthesis --- src/qutip_qoc/objective.py | 12 ------------ src/qutip_qoc/pulse_optim.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/qutip_qoc/objective.py b/src/qutip_qoc/objective.py index 06b8294..986961a 100644 --- a/src/qutip_qoc/objective.py +++ b/src/qutip_qoc/objective.py @@ -80,18 +80,6 @@ def __init__(self, initial, H, target, weight=1): self.target = target self.weight = weight - # Check if any Hamiltonian in H is a superoperator - if any(qt.issuper(H_i) for H_i in (H if isinstance(H, list) else [H])): - # Convert initial and target accordingly - if qt.isket(self.initial): - self.initial = qt.operator_to_vector(qt.ket2dm(self.initial)) - elif qt.isoper(self.initial): - self.initial = qt.operator_to_vector(self.initial) - if qt.isket(self.target): - self.target = qt.operator_to_vector(qt.ket2dm(self.target)) - elif qt.isoper(self.target): - self.target = qt.operator_to_vector(self.target) - def __getstate__(self): """ Extract picklable information from the objective. diff --git a/src/qutip_qoc/pulse_optim.py b/src/qutip_qoc/pulse_optim.py index 349cebf..3f3377d 100644 --- a/src/qutip_qoc/pulse_optim.py +++ b/src/qutip_qoc/pulse_optim.py @@ -30,6 +30,7 @@ def optimize_pulses( optimizer_kwargs=None, minimizer_kwargs=None, integrator_kwargs=None, + optimization_type=None, ): """ Run GOAT, JOPT, GRAPE, CRAB or RL optimization. @@ -126,6 +127,9 @@ def optimize_pulses( Options for the solver, see :obj:`MESolver.options` and `Integrator <./classes.html#classes-ode>`_ for a list of all options. + optimization_type : str, optional + Type of optimization: "state_transfer", "gate_synthesis", or None. + Returns ------- result : :class:`qutip_qoc.Result` @@ -189,6 +193,31 @@ def optimize_pulses( "maxiter": algorithm_kwargs.get("max_iter", 1000), "gtol": algorithm_kwargs.get("min_grad", 0.0 if alg == "CRAB" else 1e-8), } + # Iterate over objectives and convert initial and target states based on the optimization type + for objective in objectives: + if any(qt.issuper(H_i) for H_i in (objective.H if isinstance(objective.H, list) else [objective.H])): + if optimization_type == "state_transfer": + if qt.isket(objective.initial): + objective.initial = qt.operator_to_vector(qt.ket2dm(objective.initial)) + elif qt.isoper(objective.initial): + objective.initial = qt.operator_to_vector(objective.initial) + if qt.isket(objective.target): + objective.target = qt.operator_to_vector(qt.ket2dm(objective.target)) + elif qt.isoper(objective.target): + objective.target = qt.operator_to_vector(objective.target) + elif optimization_type == "gate_synthesis": + objective.initial = qt.to_super(objective.initial) + objective.target = qt.to_super(objective.target) + elif optimization_type is None: + if qt.isoper(objective.initial) and qt.isoper(objective.target): + if np.isclose(qt.tr(objective.initial), 1) and np.isclose(qt.tr(objective.target), 1): + objective.initial = qt.operator_to_vector(objective.initial) + objective.target = qt.operator_to_vector(objective.target) + else: + objective.initial = qt.to_super(objective.initial) + objective.target = qt.to_super(objective.target) + if qt.isket(objective.initial): + objective.initial = qt.operator_to_vector(qt.ket2dm(objective.initial)) # prepare qtrl optimizers qtrl_optimizers = [] From 02d361a0e0efb704eb2980f89f17048131b16a25 Mon Sep 17 00:00:00 2001 From: Rochisha Agarwal Date: Tue, 18 Mar 2025 10:07:12 +0530 Subject: [PATCH 4/5] improve the clarity of the code --- src/qutip_qoc/pulse_optim.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/qutip_qoc/pulse_optim.py b/src/qutip_qoc/pulse_optim.py index 3f3377d..faa0e09 100644 --- a/src/qutip_qoc/pulse_optim.py +++ b/src/qutip_qoc/pulse_optim.py @@ -128,7 +128,9 @@ def optimize_pulses( `Integrator <./classes.html#classes-ode>`_ for a list of all options. optimization_type : str, optional - Type of optimization: "state_transfer", "gate_synthesis", or None. + Type of optimization. By default, QuTiP-QOC will try to automatically determine + whether this is a *state transfer* or a *gate synthesis* problem. Set this + flag to ``"state_transfer"`` or ``"gate_synthesis"`` to set the mode manually. Returns ------- @@ -195,8 +197,9 @@ def optimize_pulses( } # Iterate over objectives and convert initial and target states based on the optimization type for objective in objectives: - if any(qt.issuper(H_i) for H_i in (objective.H if isinstance(objective.H, list) else [objective.H])): - if optimization_type == "state_transfer": + H_list = objective.H if isinstance(objective.H, list) else [objective.H] + if any(qt.issuper(H_i) for H_i in H_list): + if isinstance(optimization_type, str) and optimization_type.lower() == "state_transfer": if qt.isket(objective.initial): objective.initial = qt.operator_to_vector(qt.ket2dm(objective.initial)) elif qt.isoper(objective.initial): @@ -205,7 +208,7 @@ def optimize_pulses( objective.target = qt.operator_to_vector(qt.ket2dm(objective.target)) elif qt.isoper(objective.target): objective.target = qt.operator_to_vector(objective.target) - elif optimization_type == "gate_synthesis": + elif isinstance(optimization_type, str) and optimization_type.lower() == "gate_synthesis": objective.initial = qt.to_super(objective.initial) objective.target = qt.to_super(objective.target) elif optimization_type is None: @@ -218,6 +221,8 @@ def optimize_pulses( objective.target = qt.to_super(objective.target) if qt.isket(objective.initial): objective.initial = qt.operator_to_vector(qt.ket2dm(objective.initial)) + if qt.isket(objective.target): + objective.target = qt.operator_to_vector(qt.ket2dm(objective.target)) # prepare qtrl optimizers qtrl_optimizers = [] From ae447b655b456e259c4f51fd99fe03b3fab3da2c Mon Sep 17 00:00:00 2001 From: Rochisha Agarwal Date: Fri, 28 Mar 2025 00:13:18 +0530 Subject: [PATCH 5/5] fix trace --- src/qutip_qoc/pulse_optim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qutip_qoc/pulse_optim.py b/src/qutip_qoc/pulse_optim.py index faa0e09..935bf18 100644 --- a/src/qutip_qoc/pulse_optim.py +++ b/src/qutip_qoc/pulse_optim.py @@ -213,7 +213,7 @@ def optimize_pulses( objective.target = qt.to_super(objective.target) elif optimization_type is None: if qt.isoper(objective.initial) and qt.isoper(objective.target): - if np.isclose(qt.tr(objective.initial), 1) and np.isclose(qt.tr(objective.target), 1): + if np.isclose((objective.initial).tr(), 1) and np.isclose((objective.target).tr(), 1): objective.initial = qt.operator_to_vector(objective.initial) objective.target = qt.operator_to_vector(objective.target) else: