Skip to content

Commit c6ca51d

Browse files
authored
In GD, change objective_function --> f and deprecate objective_function argument (#2006)
--------- Signed-off-by: Margaret Duff <[email protected]>
1 parent b2f1c62 commit c6ca51d

File tree

6 files changed

+69
-57
lines changed

6 files changed

+69
-57
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- Deprecated `norms` and `prob` in the `SPDHG` algorithm to be set in the `BlockOperator` and `Sampler` respectively (#1644)
2121
- The `run` method in the cil algorithm class will no longer run if a number of iterations is not passed (#1940)
2222
- Paganin processor now requires the CIL data order (#1920)
23+
- The gradient descent algorithm now takes `f` instead of `objective_function` to match with ISTA and FISTA (#2006)
2324
- Testing
2425
- Added a new test file `test_algorithm_convergence` that will hold our algorithm tests that run to convergence (#2019)
2526

Wrappers/Python/cil/optimisation/algorithms/GD.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class GD(Algorithm):
3333
----------
3434
initial: DataContainer (e.g. ImageData)
3535
The initial point for the optimisation
36-
objective_function: CIL function (:meth:`~cil.optimisation.functions.Function`. ) with a defined gradient method
36+
f: CIL function (:meth:`~cil.optimisation.functions.Function`. ) with a defined gradient method
3737
The function to be minimised.
3838
step_size: positive real float or subclass of :meth:`~cil.optimisation.utilities.StepSizeRule`, default = None
3939
If you pass a float this will be used as a constant step size. If left as None and do not pass a step_size_rule then the Armijio rule will be used to perform backtracking to choose a step size at each iteration. If a child class of :meth:`cil.optimisation.utilities.StepSizeRule`' is passed then it's method `get_step_size` is called for each update.
@@ -47,12 +47,19 @@ class GD(Algorithm):
4747
4848
"""
4949

50-
def __init__(self, initial=None, objective_function=None, step_size=None, rtol=1e-5, atol=1e-8, preconditioner=None, **kwargs):
50+
def __init__(self, initial=None, f=None, step_size=None, rtol=1e-5, atol=1e-8, preconditioner=None, **kwargs):
5151
'''GD algorithm creator
5252
'''
5353

5454
self.alpha = kwargs.pop('alpha', None)
5555
self.beta = kwargs.pop('beta', None)
56+
57+
if kwargs.get('objective_function') is not None:
58+
warn('The argument `objective_function` will be deprecated in the future. Please use `f` instead.', DeprecationWarning, stacklevel=2)
59+
if f is not None:
60+
raise ValueError('The argument `objective_function` is being deprecated, replaced by `f`. Please use just `f` not both')
61+
f = kwargs.pop('objective_function')
62+
5663

5764
super().__init__(**kwargs)
5865

@@ -61,18 +68,18 @@ def __init__(self, initial=None, objective_function=None, step_size=None, rtol=1
6168

6269
self.rtol = rtol
6370
self.atol = atol
64-
if initial is not None and objective_function is not None:
65-
self.set_up(initial=initial, objective_function=objective_function,
71+
if initial is not None and f is not None:
72+
self.set_up(initial=initial, f=f,
6673
step_size=step_size, preconditioner=preconditioner)
6774

68-
def set_up(self, initial, objective_function, step_size, preconditioner):
75+
def set_up(self, initial, f, step_size, preconditioner):
6976
'''initialisation of the algorithm
7077
7178
Parameters
7279
----------
7380
initial: DataContainer (e.g. ImageData)
7481
The initial point for the optimisation
75-
objective_function: CIL function with a defined gradient
82+
f: CIL function with a defined gradient
7683
The function to be minimised.
7784
step_size: positive real float or subclass of :meth:`~cil.optimisation.utilities.StepSizeRule`, default = None
7885
If you pass a float this will be used as a constant step size. If left as None and do not pass a step_size_rule then the Armijio rule will be used to perform backtracking to choose a step size at each iteration. If a child class of :meth:`cil.optimisation.utilities.StepSizeRule`' is passed then it's method `get_step_size` is called for each update.
@@ -84,7 +91,7 @@ def set_up(self, initial, objective_function, step_size, preconditioner):
8491
log.info("%s setting up", self.__class__.__name__)
8592

8693
self.x = initial.copy()
87-
self._objective_function = objective_function
94+
self._objective_function = f
8895

8996
if step_size is None:
9097
self.step_size_rule = ArmijoStepSizeRule(

Wrappers/Python/test/test_algorithms.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ def test_GD(self):
8888

8989
step_size = norm2sq.L / 3.
9090

91-
alg = GD(initial=initial, objective_function=norm2sq, step_size=step_size,
91+
alg = GD(initial=initial, f=norm2sq, step_size=step_size,
9292
atol=1e-9, rtol=1e-6)
9393
alg.run(1000, verbose=0)
9494
self.assertNumpyArrayAlmostEqual(alg.x.as_array(), b.as_array())
9595

96-
alg = GD(initial=initial, objective_function=norm2sq, step_size=step_size,
96+
alg = GD(initial=initial, f=norm2sq, step_size=step_size,
9797
atol=1e-9, rtol=1e-6, update_objective_interval=2)
9898
self.assertTrue(alg.update_objective_interval == 2)
9999
alg.run(20, verbose=0)
@@ -114,7 +114,7 @@ def test_update_interval_0(self):
114114
identity = IdentityOperator(ig)
115115
norm2sq = LeastSquares(identity, b)
116116
alg = GD(initial=initial,
117-
objective_function=norm2sq,
117+
f=norm2sq,
118118
update_objective_interval=0,
119119
atol=1e-9, rtol=1e-6)
120120
self.assertTrue(alg.update_objective_interval == 0)
@@ -124,11 +124,11 @@ def test_update_interval_0(self):
124124
self.assertNumpyArrayAlmostEqual(alg.x.as_array(), b.as_array())
125125

126126
def test_gd_step_size_init(self):
127-
gd = GD(initial=self.initial, objective_function=self.f, step_size=0.002)
127+
gd = GD(initial=self.initial, f=self.f, step_size=0.002)
128128
self.assertEqual(gd.step_size_rule.step_size, 0.002)
129129
self.assertEqual(gd.step_size, 0.002)
130130

131-
gd = GD(initial=self.initial, objective_function=self.f)
131+
gd = GD(initial=self.initial, f=self.f)
132132
self.assertEqual(gd.step_size_rule.alpha_orig, 1e6)
133133
self.assertEqual(gd.step_size_rule.beta, 0.5)
134134
self.assertEqual(gd.step_size_rule.max_iterations, np.ceil(
@@ -137,27 +137,27 @@ def test_gd_step_size_init(self):
137137
gd.step_size
138138

139139
gd = GD(initial=self.initial,
140-
objective_function=self.f, alpha=1e2, beta=0.25)
140+
f=self.f, alpha=1e2, beta=0.25)
141141
self.assertEqual(gd.step_size_rule.alpha_orig, 1e2)
142142
self.assertEqual(gd.step_size_rule.beta, 0.25)
143143
self.assertEqual(gd.step_size_rule.max_iterations, np.ceil(
144144
2 * np.log10(1e2) / np.log10(2)))
145145

146146
with self.assertRaises(TypeError):
147-
gd = GD(initial=self.initial, objective_function=self.f,
147+
gd = GD(initial=self.initial, f=self.f,
148148
step_size=0.1, step_size_rule=ConstantStepSize(0.5))
149149

150150
def test_gd_constant_step_size_init(self):
151151
rule = ConstantStepSize(0.4)
152152
self.assertEqual(rule.step_size, 0.4)
153153
gd = GD(initial=self.initial,
154-
objective_function=self.f, step_size=rule)
154+
f=self.f, step_size=rule)
155155
self.assertEqual(gd.step_size_rule.step_size, 0.4)
156156
self.assertEqual(gd.step_size, 0.4)
157157

158158
def test_gd_fixed_step_size_rosen(self):
159159

160-
gd = GD(initial=self.initial, objective_function=self.f, step_size=0.002,
160+
gd = GD(initial=self.initial, f=self.f, step_size=0.002,
161161
update_objective_interval=500)
162162
gd.run(3000, verbose=0)
163163
np.testing.assert_allclose(
@@ -174,7 +174,7 @@ def test_armijo_step_size_init(self):
174174
2 * np.log10(1e6) / np.log10(2)))
175175

176176
gd = GD(initial=self.initial,
177-
objective_function=self.f, step_size=rule)
177+
f=self.f, step_size=rule)
178178
self.assertEqual(gd.step_size_rule.alpha_orig, 1e6)
179179
self.assertEqual(gd.step_size_rule.beta, 0.5)
180180
self.assertEqual(gd.step_size_rule.max_iterations, np.ceil(
@@ -186,7 +186,7 @@ def test_armijo_step_size_init(self):
186186
self.assertEqual(rule.max_iterations, 5)
187187

188188
gd = GD(initial=self.initial,
189-
objective_function=self.f, step_size=rule)
189+
f=self.f, step_size=rule)
190190
self.assertEqual(gd.step_size_rule.alpha_orig, 5e5)
191191
self.assertEqual(gd.step_size_rule.beta, 0.2)
192192
self.assertEqual(gd.step_size_rule.max_iterations, 5)
@@ -205,18 +205,22 @@ def test_GDArmijo(self):
205205

206206
norm2sq = LeastSquares(identity, b)
207207

208-
alg = GD(initial=initial, objective_function=norm2sq)
208+
alg = GD(initial=initial, f=norm2sq)
209209
alg.run(100, verbose=0)
210210
self.assertNumpyArrayAlmostEqual(alg.x.as_array(), b.as_array())
211-
alg = GD(initial=initial, objective_function=norm2sq,
211+
alg = GD(initial=initial, f=norm2sq, update_objective_interval=2)
212+
self.assertTrue(alg.update_objective_interval==2)
213+
214+
alg = GD(initial=initial, f=norm2sq,
212215
update_objective_interval=2)
213216
self.assertTrue(alg.update_objective_interval == 2)
217+
214218
alg.run(20, verbose=0)
215219
self.assertNumpyArrayAlmostEqual(alg.x.as_array(), b.as_array())
216220

217221
def test_gd_armijo_rosen(self):
218222
armj = ArmijoStepSizeRule(alpha=50, max_iterations=50, warmstart=False)
219-
gd = GD(initial=self.initial, objective_function=self.f, step_size=armj,
223+
gd = GD(initial=self.initial, f=self.f, step_size=armj,
220224
update_objective_interval=500)
221225
gd.run(3500, verbose=0)
222226
np.testing.assert_allclose(
@@ -225,12 +229,12 @@ def test_gd_armijo_rosen(self):
225229
gd.solution.array[1], self.scipy_opt_high.x[1], atol=1e-2)
226230

227231
def test_gd_run_no_iterations(self):
228-
gd = GD(initial=self.initial, objective_function=self.f, step_size=0.002)
232+
gd = GD(initial=self.initial, f=self.f, step_size=0.002)
229233
with self.assertRaises(ValueError):
230234
gd.run()
231235

232236
def test_gd_run_infinite(self):
233-
gd = GD(initial=self.initial, objective_function=self.f, step_size=0.002)
237+
gd = GD(initial=self.initial, f=self.f, step_size=0.002)
234238
with self.assertRaises(ValueError):
235239
gd.run(np.inf)
236240

Wrappers/Python/test/test_approximate_gradient.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def test_toy_example(self):
159159
stochastic_objective = self.stochastic_estimator(functions, sampler)
160160

161161
alg_stochastic = GD(initial=initial,
162-
objective_function=stochastic_objective, update_objective_interval=1000,
162+
f=stochastic_objective, update_objective_interval=1000,
163163
step_size=0.05)
164164
alg_stochastic.run(600, verbose=0)
165165

@@ -246,7 +246,7 @@ def test_SAG_toy_example_warm_start(self):
246246
stochastic_objective.warm_start_approximate_gradients(initial)
247247

248248
alg_stochastic = GD(initial=initial,
249-
objective_function=stochastic_objective, update_objective_interval=1000,
249+
f=stochastic_objective, update_objective_interval=1000,
250250
step_size=0.05, max_iteration =5000)
251251
alg_stochastic.run( 80, verbose=0)
252252
np.testing.assert_allclose(p.value ,stochastic_objective(alg_stochastic.x) , atol=1e-1)
@@ -307,7 +307,7 @@ def test_SAGA_toy_example_warm_start(self):
307307
stochastic_objective.warm_start_approximate_gradients(initial)
308308

309309
alg_stochastic = GD(initial=initial,
310-
objective_function=stochastic_objective, update_objective_interval=1000,
310+
f=stochastic_objective, update_objective_interval=1000,
311311
step_size=0.05, max_iteration =5000)
312312
alg_stochastic.run( 100, verbose=0)
313313
np.testing.assert_allclose(p.value ,stochastic_objective(alg_stochastic.x) , atol=1e-1)
@@ -343,7 +343,7 @@ def test_SVRG_snapshot_update_interval_and_data_passes(self):
343343
sampler = Sampler.random_with_replacement(self.n_subsets)
344344
objective = SVRGFunction(self.f_subsets, sampler)
345345
alg_stochastic = GD(initial=self.initial,
346-
objective_function=objective, update_objective_interval=500,
346+
f=objective, update_objective_interval=500,
347347
step_size=5e-8, max_iteration=5000)
348348
alg_stochastic.run(2, verbose=0)
349349
self.assertNumpyArrayAlmostEqual(
@@ -366,7 +366,7 @@ def test_SVRG_snapshot_update_interval_and_data_passes(self):
366366
objective = SVRGFunction(
367367
self.f_subsets, self.sampler, snapshot_update_interval=3)
368368
alg_stochastic = GD(initial=self.initial,
369-
objective_function=objective, update_objective_interval=500,
369+
f=objective, update_objective_interval=500,
370370
step_size=5e-8, max_iteration=5000)
371371
alg_stochastic.run(2, verbose=0)
372372
self.assertNumpyArrayAlmostEqual(
@@ -418,7 +418,7 @@ def test_SVRG_toy_example_store_gradients(self):
418418
stochastic_objective = SVRGFunction(functions, sampler, store_gradients=True)
419419

420420
alg_stochastic = GD(initial=initial,
421-
objective_function=stochastic_objective, update_objective_interval=1000,
421+
f=stochastic_objective, update_objective_interval=1000,
422422
step_size=1/stochastic_objective.L)
423423

424424
alg_stochastic.run(10, verbose=0)
@@ -463,7 +463,7 @@ def test_LSVRG_init(self):
463463
def test_LSVRG_data_passes_and_snapshot_update_probability_and_seed(self):
464464
objective = LSVRGFunction(self.f_subsets, self.sampler, snapshot_update_probability=1)
465465
alg_stochastic = GD(initial=self.initial, update_objective_interval=500,
466-
objective_function=objective, step_size=5e-8, max_iteration=5000)
466+
f=objective, step_size=5e-8, max_iteration=5000)
467467
alg_stochastic.run(2, verbose=0)
468468
self.assertNumpyArrayAlmostEqual(
469469
np.array(objective.data_passes), np.array([1., 2,]))
@@ -474,7 +474,7 @@ def test_LSVRG_data_passes_and_snapshot_update_probability_and_seed(self):
474474
self.assertNumpyArrayAlmostEqual(np.array(objective.data_passes_indices[-1]), np.array(list(range(self.n_subsets))))
475475
objective = LSVRGFunction(self.f_subsets, self.sampler, seed=3)
476476
alg_stochastic = GD(initial=self.initial,
477-
objective_function=objective, update_objective_interval=500,
477+
f=objective, update_objective_interval=500,
478478
step_size=5e-8, max_iteration=5000)
479479
alg_stochastic.run(10, verbose=0)
480480
self.assertNumpyArrayAlmostEqual(np.array(objective.data_passes),
@@ -522,7 +522,7 @@ def test_LSVRG_toy_example_store_gradients(self):
522522
stochastic_objective = LSVRGFunction(functions, sampler, store_gradients=True)
523523

524524
alg_stochastic = GD(initial=initial,
525-
objective_function=stochastic_objective, update_objective_interval=1000,
525+
f=stochastic_objective, update_objective_interval=1000,
526526
step_size=1/stochastic_objective.L)
527527

528528

Wrappers/Python/test/test_preconditioners.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def test_preconditioner_called(self):
2020
A = IdentityOperator(ig)
2121
test_precon = MagicMock(None)
2222
f = LeastSquares(A=A, b=data, c=0.5)
23-
alg = GD(initial=ig.allocate('random', seed=10), objective_function=f, preconditioner=test_precon,
23+
alg = GD(initial=ig.allocate('random', seed=10), f=f, preconditioner=test_precon,
2424
max_iteration=100, update_objective_interval=1, step_size=0.0000001)
2525
alg.run(5)
2626
self.assertEqual(len(test_precon.apply.mock_calls), 5)
@@ -63,7 +63,7 @@ def test_sensitivity_calculation(self):
6363
2., 2., 2., 2., 0, 0, 0, 0, 0, 0]))
6464

6565
f = LeastSquares(A, data)
66-
alg = GD(initial=ig.allocate(0), objective_function=f,
66+
alg = GD(initial=ig.allocate(0), f=f,
6767
max_iteration=100, update_objective_interval=1, step_size=1.)
6868
alg.gradient_update = ig.allocate(2)
6969
preconditioner.apply(alg, alg.gradient_update, out=alg.gradient_update)
@@ -84,17 +84,17 @@ def test_sensitivity_gd_against_sirt(self):
8484
step_size = 1.
8585
preconditioner = Sensitivity(A)
8686

87-
alg = GD(initial=ig.allocate(0), objective_function=f,
87+
alg = GD(initial=ig.allocate(0), f=f,
8888
max_iteration=100, update_objective_interval=1, step_size=step_size)
8989
self.assertEqual(alg.preconditioner, None)
9090

91-
precond_pwls = GD(initial=ig.allocate(0), objective_function=f, preconditioner=preconditioner,
91+
precond_pwls = GD(initial=ig.allocate(0), f=f, preconditioner=preconditioner,
9292
max_iteration=100, update_objective_interval=1, step_size=step_size)
9393

9494
def correct_update_objective(alg):
9595
# SIRT computes |Ax_{k} - b|_2^2
9696
# GD with weighted LeastSquares computes the objective included the weight, so we remove the weight
97-
return 0.5*(alg.objective_function.A.direct(alg.x) - alg.objective_function.b).squared_norm()
97+
return 0.5*(alg._objective_function.A.direct(alg.x) - alg._objective_function.b).squared_norm()
9898

9999
precond_pwls.run(10)
100100
np.testing.assert_allclose(
@@ -122,13 +122,13 @@ def test_sensitivity_ista_against_sirt(self):
122122
max_iteration=100, update_objective_interval=1, step_size=step_size)
123123
self.assertEqual(alg.preconditioner, None)
124124

125-
precond_pwls = GD(initial=ig.allocate(0), objective_function=f, preconditioner=preconditioner,
125+
precond_pwls = GD(initial=ig.allocate(0), f=f, preconditioner=preconditioner,
126126
max_iteration=100, update_objective_interval=1, step_size=step_size)
127127

128128
def correct_update_objective(alg):
129129
# SIRT computes |Ax_{k} - b|_2^2
130130
# GD with weighted LeastSquares computes the objective included the weight, so we remove the weight
131-
return 0.5*(alg.objective_function.A.direct(alg.x) - alg.objective_function.b).squared_norm()
131+
return 0.5*(alg._objective_function.A.direct(alg.x) - alg._objective_function.b).squared_norm()
132132

133133
precond_pwls.run(10)
134134
np.testing.assert_allclose(
@@ -185,7 +185,7 @@ def test_adaptive_sensitivity_calculations(self):
185185
2., 2., 2., 2., 0, 0, 0, 0, 0, 0]))
186186

187187
f = LeastSquares(A, data)
188-
alg = GD(initial=ig.allocate(0), objective_function=f,
188+
alg = GD(initial=ig.allocate(0), f=f,
189189
max_iteration=100, update_objective_interval=1, step_size=1.)
190190
alg.gradient_update = ig.allocate(1)
191191
preconditioner.apply(alg, alg.gradient_update, out=alg.gradient_update)
@@ -242,7 +242,7 @@ def test_adaptive_sensitivity_gd_converges(self):
242242
step_size = 1.
243243
preconditioner = AdaptiveSensitivity(A, max_iterations=3000, delta=1e-8)
244244

245-
precond_pwls = GD(initial=initial, objective_function=f,
245+
precond_pwls = GD(initial=initial, f=f,
246246
preconditioner=preconditioner, update_objective_interval=1, step_size=step_size)
247247

248248
precond_pwls.run(3000)

0 commit comments

Comments
 (0)