Skip to content

Commit 2d667be

Browse files
Cleaned up and added tests
1 parent 34af94a commit 2d667be

File tree

6 files changed

+93
-29
lines changed

6 files changed

+93
-29
lines changed

c3/experiment.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ def expect_oper(self, state, lindbladian, oper):
631631
trace = np.trace(np.matmul(rho, oper))
632632
return [[np.real(trace)]] # ,[np.imag(trace)]]
633633

634-
def compute_states(self, solver="rk4", step_function="schroedinger"):
634+
def compute_states(self, solver="rk4", step_function="schrodinger"):
635635
"""
636636
Use a state solver to compute the trajectory of the system.
637637
@@ -681,7 +681,7 @@ def compute_states(self, solver="rk4", step_function="schroedinger"):
681681

682682
return {"states": state_list, "ts": ts_list}
683683

684-
def compute_final_state(self, solver="rk4", step_function="schroedinger"):
684+
def compute_final_state(self, solver="rk4", step_function="schrodinger"):
685685
"""
686686
Solve the Lindblad master equation by integrating the differential
687687
equation of the density matrix

c3/libraries/fidelities.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -791,10 +791,15 @@ def orbit_infid(
791791

792792

793793
@fid_reg_deco
794-
def state_transfer_from_states(states: List[tf.Tensor], index, dims, params, n_eval=-1):
794+
def state_transfer_from_states(states: tf.Tensor, index, dims, params, n_eval=-1):
795795
infids = []
796796
psi_0 = params["target"]
797-
overlap = calculateStateOverlap(states[-1], psi_0)
797+
798+
if len(states.shape) > 2:
799+
overlap = calculateStateOverlap(states[-1], psi_0)
800+
else:
801+
overlap = calculateStateOverlap(states, psi_0)
802+
798803
infid = 1 - overlap
799804
infids.append(infid)
800805
return tf.reduce_max(tf.math.real(infids))

c3/libraries/propagation.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,6 @@ def ode_solver(
689689
model: Model, gen: Generator, instr: Instruction, init_state, solver, step_function
690690
) -> Dict:
691691

692-
init_state = model.get_init_state()
693692
signal = gen.generate_signals(instr)
694693

695694
if model.lindbladian:
@@ -728,7 +727,6 @@ def ode_solver_final_state(
728727
model: Model, gen: Generator, instr: Instruction, init_state, solver, step_function
729728
) -> Dict:
730729

731-
init_state = model.get_init_state()
732730
signal = gen.generate_signals(instr)
733731

734732
if model.lindbladian:
@@ -899,7 +897,7 @@ def lindblad(rho, h, dt, col):
899897

900898

901899
@step_deco
902-
def schroedinger(psi, h, dt, col=None):
900+
def schrodinger(psi, h, dt, col=None):
903901
return -1j * tf.matmul(h, psi) * dt
904902

905903

c3/optimizers/optimalcontrol.py

+17-19
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,6 @@
1212
from c3.libraries.fidelities import fidelities
1313

1414

15-
goal_functions_dict = dict()
16-
17-
18-
def goal_functions_deco(func):
19-
"""
20-
Decorator for making registry of functions
21-
"""
22-
goal_functions_dict[str(func.__name__)] = func
23-
return func
24-
25-
2615
class OptimalControl(Optimizer):
2716
"""
2817
Object that deals with the open loop optimal control.
@@ -69,7 +58,7 @@ def __init__(
6958
logger=None,
7059
fid_func_kwargs={},
7160
ode_solver=None,
72-
ode_step_function="schroedinger",
61+
ode_step_function="schrodinger",
7362
only_final_state=False,
7463
) -> None:
7564
if type(algorithm) is str:
@@ -96,17 +85,29 @@ def __init__(
9685
self.optimize_controls
9786
) # Alias the legacy name for the method running the
9887
# optimization
88+
self.set_goal_function(
89+
ode_solver=ode_solver,
90+
ode_step_function=ode_step_function,
91+
only_final_state=only_final_state,
92+
)
93+
94+
def set_goal_function(
95+
self,
96+
ode_solver=None,
97+
ode_step_function="schrodinger",
98+
only_final_state=False,
99+
):
99100
self.ode_solver = ode_solver
100101
self.ode_step_function = ode_step_function
101102
self.only_final_state = only_final_state
102103

103-
self.goal_function = goal_functions_dict["goal_run"]
104-
105104
if self.ode_solver is not None:
106105
if self.only_final_state:
107-
self.goal_function = goal_functions_dict["goal_run_ode_only_final"]
106+
self.goal_function = self.goal_run_ode_only_final
108107
else:
109-
self.goal_function = goal_functions_dict["goal_run_ode"]
108+
self.goal_function = self.goal_run_ode
109+
else:
110+
self.goal_function = self.goal_run
110111

111112
def set_fid_func(self, fid_func) -> None:
112113
if type(fid_func) is str:
@@ -182,7 +183,6 @@ def optimize_controls(self, setup_log: bool = True) -> None:
182183
self.end_log()
183184

184185
@tf.function
185-
@goal_functions_deco
186186
def goal_run(self, current_params: tf.Tensor) -> tf.float64:
187187
"""
188188
Evaluate the goal function for current parameters.
@@ -213,7 +213,6 @@ def goal_run(self, current_params: tf.Tensor) -> tf.float64:
213213
return goal
214214

215215
@tf.function
216-
@goal_functions_deco
217216
def goal_run_ode(self, current_params: tf.Tensor) -> tf.float64:
218217
"""
219218
Evaluate the goal function using ode solver for current parameters.
@@ -246,7 +245,6 @@ def goal_run_ode(self, current_params: tf.Tensor) -> tf.float64:
246245
return goal
247246

248247
@tf.function
249-
@goal_functions_deco
250248
def goal_run_ode_only_final(self, current_params: tf.Tensor) -> tf.float64:
251249
"""
252250
Evaluate the goal function using ode solver for current parameters.

c3/optimizers/optimizer.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def goal_run(
208208
def goal_run_with_grad(self, current_params):
209209
with tf.GradientTape(watch_accessed_variables=False) as t:
210210
t.watch(current_params)
211-
goal = self.goal_function(self, current_params)
211+
goal = self.goal_function(current_params)
212212
grad = t.gradient(goal, current_params)
213213
return goal, grad
214214

@@ -262,12 +262,12 @@ def fct_to_min(
262262
self.optim_status["params"] = pars
263263
if isinstance(input_parameters, np.ndarray):
264264
current_params = tf.constant(input_parameters)
265-
goal = self.goal_run(current_params)
265+
goal = self.goal_function(current_params)
266266
self.optim_status["goal"] = float(goal)
267267
goal = float(goal)
268268
else:
269269
current_params = input_parameters
270-
goal = self.goal_run(current_params)
270+
goal = self.goal_function(current_params)
271271
self.optim_status["goal"] = float(goal)
272272
self.log_parameters(input_parameters)
273273
return goal

test/test_two_qubits.py

+63
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import pytest
88
from numpy.testing import assert_array_almost_equal as almost_equal
99
import c3.libraries.algorithms as algorithms
10+
from c3.libraries.fidelities import state_transfer_from_states
1011

1112
# Libs and helpers
1213
from c3.libraries.propagation import rk4_unitary
14+
import tensorflow as tf
1315

1416
with open("test/two_qubit_data.pickle", "rb") as filename:
1517
test_data = pickle.load(filename)
@@ -82,6 +84,64 @@ def test_optim(get_OC_optimizer) -> None:
8284
assert opt.evaluation == opt.options[maxiterKey] - 1
8385

8486

87+
@pytest.mark.slow
88+
@pytest.mark.tensorflow
89+
@pytest.mark.optimizers
90+
@pytest.mark.integration
91+
def test_optim_ode_solver(get_OC_optimizer) -> None:
92+
"""
93+
check if optimization result is below 1e-1
94+
"""
95+
opt = get_OC_optimizer
96+
opt.fid_func = state_transfer_from_states
97+
opt.set_goal_function(ode_solver="rk4", ode_step_function="schrodinger")
98+
99+
model = opt.exp.pmap.model
100+
psi_init = [[0] * model.tot_dim]
101+
psi_init[0][0] = 1 / np.sqrt(2)
102+
index = model.get_state_indeces([(1, 0)])[0]
103+
psi_init[0][index] = 1 / np.sqrt(2)
104+
target_state = tf.transpose(tf.constant(psi_init, tf.complex128))
105+
params = {"target": target_state}
106+
opt.fid_func_kwargs = {"params": params}
107+
108+
assert opt.evaluation == 0
109+
opt.optimize_controls()
110+
assert opt.current_best_goal < 0.5 # TODO - Make it 0.1
111+
maxiterKey = "maxiters" if opt.algorithm == algorithms.tf_sgd else "maxiter"
112+
assert opt.evaluation == opt.options[maxiterKey] - 1
113+
114+
115+
@pytest.mark.slow
116+
@pytest.mark.tensorflow
117+
@pytest.mark.optimizers
118+
@pytest.mark.integration
119+
def test_optim_ode_solver_final(get_OC_optimizer) -> None:
120+
"""
121+
check if optimization result is below 1e-1
122+
"""
123+
opt = get_OC_optimizer
124+
opt.fid_func = state_transfer_from_states
125+
opt.set_goal_function(
126+
ode_solver="rk4", ode_step_function="schrodinger", only_final_state=True
127+
)
128+
129+
model = opt.exp.pmap.model
130+
psi_init = [[0] * model.tot_dim]
131+
psi_init[0][0] = 1 / np.sqrt(2)
132+
index = model.get_state_indeces([(1, 0)])[0]
133+
psi_init[0][index] = 1 / np.sqrt(2)
134+
target_state = tf.transpose(tf.constant(psi_init, tf.complex128))
135+
params = {"target": target_state}
136+
opt.fid_func_kwargs = {"params": params}
137+
138+
assert opt.evaluation == 0
139+
opt.optimize_controls()
140+
assert opt.current_best_goal < 0.5 # TODO - Make it 0.1
141+
maxiterKey = "maxiters" if opt.algorithm == algorithms.tf_sgd else "maxiter"
142+
assert opt.evaluation == opt.options[maxiterKey] - 1
143+
144+
85145
@pytest.mark.tensorflow
86146
@pytest.mark.integration
87147
def test_rk4_unitary(get_two_qubit_chip) -> None:
@@ -101,3 +161,6 @@ def test_ode_solver(get_two_qubit_chip) -> None:
101161
exp.compute_states(solver="rk4")
102162
exp.compute_states(solver="rk5")
103163
exp.compute_states(solver="Tsit5")
164+
exp.compute_states(solver="rk4", step_function="vonNeumann")
165+
exp.compute_states(solver="rk5", step_function="vonNeumann")
166+
exp.compute_states(solver="Tsit5", step_function="vonNeumann")

0 commit comments

Comments
 (0)