Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlockOperator: added a function to return a list of norms and the ability to set this list of norms #1513

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6575af6
Initial changes and tests- currently failing tests
MargaretDuff Sep 28, 2023
6b463bc
Sorted tests and checks on the set_norms function
MargaretDuff Oct 2, 2023
215bfa6
Changed a comment
MargaretDuff Oct 2, 2023
96e4730
Changes based on Gemma's review
MargaretDuff Oct 5, 2023
1ca3a2b
Comments from Edo fixed
MargaretDuff Oct 9, 2023
4b541e7
Merge branch 'master' into blockoperator-norms
MargaretDuff Oct 9, 2023
5a302c8
Fixed tests
MargaretDuff Oct 9, 2023
cf1b7f1
Vaggelis comment on checks
MargaretDuff Oct 17, 2023
544a215
Merge branch 'TomographicImaging:master' into blockoperator-norms
MargaretDuff Oct 17, 2023
32e057b
Docstring change
MargaretDuff Oct 18, 2023
4e0ca6a
Docstring change
MargaretDuff Oct 18, 2023
8f100e0
Updated changelog
MargaretDuff Oct 18, 2023
381342c
Changes to docstring
MargaretDuff Oct 25, 2023
876d4c9
Added size to the BlockOperator
MargaretDuff Oct 26, 2023
f0f4de3
Changes after discussion with Edo and Gemma
MargaretDuff Nov 2, 2023
100a42d
Merge branch 'blockoperator-norms' of github.com:MargaretDuff/CIL-mar…
MargaretDuff Nov 2, 2023
26584c9
Documentation changes
MargaretDuff Nov 2, 2023
2c072c0
Changes to documentation
MargaretDuff Nov 3, 2023
b669fb9
Documentation
MargaretDuff Nov 3, 2023
7cdd521
Merge branch 'master' into blockoperator-norms
MargaretDuff Nov 6, 2023
0962313
Documentation update
MargaretDuff Nov 7, 2023
1eea20b
Documentation update
MargaretDuff Nov 7, 2023
8de3bd6
Documentation update
MargaretDuff Nov 7, 2023
3d40876
Update Wrappers/Python/cil/optimisation/operators/Operator.py
MargaretDuff Nov 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 24 additions & 17 deletions Wrappers/Python/cil/optimisation/operators/BlockOperator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import numpy
import functools
from numbers import Number
from cil.framework import ImageData, BlockDataContainer, DataContainer
from cil.optimisation.operators import Operator, LinearOperator
from cil.framework import BlockGeometry
Expand Down Expand Up @@ -135,26 +136,32 @@ def get_item(self, row, col):
index = row*self.shape[1]+col
return self.operators[index]

def norm(self, **kwargs):
'''Returns the norm of the BlockOperator

if the operator in the block do not have method norm defined, i.e. they are SIRF
AcquisitionModel's we use PowerMethod if applicable, otherwise we raise an Error
def norm(self):
'''Returns the square root of the sum of the norms of the individual operators in the BlockOperators
MargaretDuff marked this conversation as resolved.
Show resolved Hide resolved
'''
return numpy.sqrt(numpy.sum(numpy.array(self.norms())**2))
MargaretDuff marked this conversation as resolved.
Show resolved Hide resolved

def norms(self, ):
MargaretDuff marked this conversation as resolved.
Show resolved Hide resolved
'''Returns a list of the individual norms of the Operators in the BlockOperator
'''
norm = []
norms= []
for op in self.operators:
if hasattr(op, 'norm'):
norm.append(op.norm(**kwargs) ** 2.)
else:
# use Power method
if op.is_linear():
norm.append(
LinearOperator.PowerMethod(op, 20)[0]
)
else:
raise TypeError('Operator {} does not have a norm method and is not linear'.format(op))
return numpy.sqrt(sum(norm))
norms.append(op.norm())
return norms
MargaretDuff marked this conversation as resolved.
Show resolved Hide resolved

def set_norms(self, norms):
'''Uses the set_norm() function in Operator to set the norms of the operators in the BlockOperator from a list of custom values.
'''
if len(norms)==len(self):
pass
else:
raise ValueError("The length of the list of norms should be equal to the number of operators in the BlockOperator")
MargaretDuff marked this conversation as resolved.
Show resolved Hide resolved

for j,value in enumerate(norms):
self.operators[j].set_norm(value)



def direct(self, x, out=None):
'''Direct operation for the BlockOperator

Expand Down
7 changes: 7 additions & 0 deletions Wrappers/Python/cil/optimisation/operators/Operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ def norm(self, **kwargs):
def set_norm(self,norm=None):
'''Sets the norm of the operator to a custom value.
'''
try:
MargaretDuff marked this conversation as resolved.
Show resolved Hide resolved
if norm is not None and norm <=0:
raise ValueError("Norm must be a positive real value or None, got {}".format(norm))
except TypeError:
raise TypeError("Norm must be a positive real value or None, got {} of type {}".format(norm, type(norm)))

MargaretDuff marked this conversation as resolved.
Show resolved Hide resolved

self._norm = norm

def calculate_norm(self):
Expand Down
52 changes: 51 additions & 1 deletion Wrappers/Python/test/test_BlockOperator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import unittest
from utils import initialise_tests
import logging
from cil.optimisation.operators import BlockOperator
from cil.optimisation.operators import BlockOperator, GradientOperator
from cil.framework import BlockDataContainer
from cil.optimisation.operators import IdentityOperator
from cil.framework import ImageGeometry, ImageData
Expand All @@ -30,6 +30,56 @@
initialise_tests()

class TestBlockOperator(unittest.TestCase):
def test_norms(self):
numpy.random.seed(1)
N, M = 200, 300

ig = ImageGeometry(N, M)
G = GradientOperator(ig)
G2 = GradientOperator(ig)

A=BlockOperator(G,G2)


#calculates norm
self.assertAlmostEqual(G.norm(), numpy.sqrt(8), 2)
self.assertAlmostEqual(G2.norm(), numpy.sqrt(8), 2)
self.assertAlmostEqual(A.norm(), numpy.sqrt(16), 2)
self.assertAlmostEqual(A.norms()[0], numpy.sqrt(8), 2)
self.assertAlmostEqual(A.norms()[1], numpy.sqrt(8), 2)


#sets_norm
A.set_norms([2,3])
#gets cached norm
self.assertListEqual(A.norms(), [2,3], 2)
self.assertEqual(A.norm(), numpy.sqrt(13))


#Check that it changes the underlying operators
self.assertEqual(A.operators[0]._norm, 2)
self.assertEqual(A.operators[1]._norm, 3)

#sets cache to None
A.set_norms([None, None])
#recalculates norm
self.assertAlmostEqual(A.norm(), numpy.sqrt(16), 2)
self.assertAlmostEqual(A.norms()[0], numpy.sqrt(8), 2)
self.assertAlmostEqual(A.norms()[1], numpy.sqrt(8), 2)

#Check the warnings on set_norms
#Check the length of list that is passed
with self.assertRaises(ValueError):
A.set_norms([1])
#Check that elements in the list are numbers or None
with self.assertRaises(TypeError):
A.set_norms(['Banana', 'Apple'])
#Check that numbers in the list are positive
with self.assertRaises(ValueError):
A.set_norms([-1,-3])




def test_BlockOperator(self):
ig = [ ImageGeometry(10,20,30) , \
Expand Down
7 changes: 7 additions & 0 deletions Wrappers/Python/test/test_Operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,13 @@ def test_Norm(self):
#recalculates norm
self.assertAlmostEqual(G.norm(), numpy.sqrt(8), 2)


#Check that the provided element is a number or None
with self.assertRaises(TypeError):
G.set_norm['Banana']
#Check that the provided norm is positive
with self.assertRaises(ValueError):
G.set_norm(-1)

def test_ProjectionMap(self):
# Check if direct is correct
Expand Down