diff options
Diffstat (limited to 'Wrappers')
-rwxr-xr-x | Wrappers/Python/ccpi/framework/framework.py | 4 | ||||
-rwxr-xr-x | Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py | 49 | ||||
-rw-r--r-- | Wrappers/Python/test/test_BlockOperator.py | 49 |
3 files changed, 99 insertions, 3 deletions
diff --git a/Wrappers/Python/ccpi/framework/framework.py b/Wrappers/Python/ccpi/framework/framework.py index 5bc01e5..bf8273b 100755 --- a/Wrappers/Python/ccpi/framework/framework.py +++ b/Wrappers/Python/ccpi/framework/framework.py @@ -150,12 +150,12 @@ class ImageGeometry(object): if value != 0: out += value else: - if value == ImageData.RANDOM: + if value == ImageGeometry.RANDOM: seed = kwargs.get('seed', None) if seed is not None: numpy.random.seed(seed) out.fill(numpy.random.random_sample(self.shape)) - elif value == ImageData.RANDOM_INT: + elif value == ImageGeometry.RANDOM_INT: seed = kwargs.get('seed', None) if seed is not None: numpy.random.seed(seed) diff --git a/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py b/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py index 4ff38c6..3679136 100755 --- a/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py +++ b/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py @@ -23,6 +23,9 @@ class BlockOperator(Operator): Nx1 BlockDataContainer, will yield and Mx1 BlockDataContainer. Notice: BlockDatacontainer are only allowed to have the shape of N x 1, with N rows and 1 column. + + Operators in a Block are required to have the same domain column-wise and the + same range row-wise. ''' __array_priority__ = 1 def __init__(self, *args, **kwargs): @@ -52,6 +55,39 @@ class BlockOperator(Operator): raise ValueError( 'Dimension and size do not match: expected {} got {}' .format(n_elements,len(args))) + # test if operators are compatible + if not self.column_wise_compatible(): + raise ValueError('Operators in each column must have the same domain') + if not self.row_wise_compatible(): + raise ValueError('Operators in each row must have the same range') + + def column_wise_compatible(self): + '''Operators in a Block should have the same domain per column''' + rows, cols = self.shape + compatible = True + for col in range(cols): + row_compatible = True + for row in range(1,rows): + dg0 = self.get_item(row-1,col).domain_geometry() + dg1 = self.get_item(row,col).domain_geometry() + row_compatible = dg0.__dict__ == dg1.__dict__ and row_compatible + compatible = compatible and row_compatible + return compatible + + def row_wise_compatible(self): + '''Operators in a Block should have the same range per row''' + rows, cols = self.shape + compatible = True + for row in range(rows): + column_compatible = True + for col in range(1,cols): + dg0 = self.get_item(row,col-1).range_geometry() + dg1 = self.get_item(row,col).range_geometry() + column_compatible = dg0.__dict__ == dg1.__dict__ and column_compatible + print ("column_compatible" , column_compatible, dg0.shape, dg1.shape) + compatible = compatible and column_compatible + return compatible + def get_item(self, row, col): if row > self.shape[0]: raise ValueError('Requested row {} > max {}'.format(row, self.shape[0])) @@ -153,5 +189,18 @@ class BlockOperator(Operator): shape = (self.shape[1], self.shape[0]) return type(self)(*self.operators, shape=shape) + def domain_geometry(self): + if self.shape[1] == 1: + # column BlockOperator + return self[0].domain_geometry() + else: + shape = (self.shape[0], 1) + return BlockGeometry(*[el.domain_geometry() for el in self.operators], + shape=shape) + + def range_geometry(self): + shape = (self.shape[1], 1) + return BlockGeometry(*[el.range_geometry() for el in self.operators], + shape=shape) if __name__ == '__main__': pass diff --git a/Wrappers/Python/test/test_BlockOperator.py b/Wrappers/Python/test/test_BlockOperator.py index 951aa0a..c042d04 100644 --- a/Wrappers/Python/test/test_BlockOperator.py +++ b/Wrappers/Python/test/test_BlockOperator.py @@ -6,6 +6,13 @@ from ccpi.framework import ImageGeometry, ImageData import numpy from ccpi.optimisation.operators import FiniteDiff +class TestOperator(TomoIdentity): + def __init__(self, *args, **kwargs): + super(TestOperator, self).__init__(*args, **kwargs) + self.range = kwargs.get('range', self.geometry) + def range_geometry(self): + return self.range + class TestBlockOperator(unittest.TestCase): def test_BlockOperator(self): @@ -32,7 +39,47 @@ class TestBlockOperator(unittest.TestCase): zero = numpy.zeros(X.get_item(0).shape) numpy.testing.assert_array_equal(Y.get_item(0).as_array(),len(x)+zero) - + try: + # this should fail as the domain is not compatible + ig = [ ImageGeometry(10,20,31) , \ + ImageGeometry(10,20,30) , \ + ImageGeometry(10,20,30) ] + x = [ g.allocate() for g in ig ] + ops = [ TomoIdentity(g) for g in ig ] + + K = BlockOperator(*ops) + self.assertTrue(False) + except ValueError as ve: + print (ve) + self.assertTrue(True) + + try: + # this should fail as the range is not compatible + ig = [ ImageGeometry(10,20,30) , \ + ImageGeometry(10,20,30) , \ + ImageGeometry(10,20,30) ] + rg0 = [ ImageGeometry(10,20,31) , \ + ImageGeometry(10,20,31) , \ + ImageGeometry(10,20,31) ] + rg1 = [ ImageGeometry(10,22,31) , \ + ImageGeometry(10,22,31) , \ + ImageGeometry(10,20,31) ] + x = [ g.allocate() for g in ig ] + ops = [ TestOperator(g, range=r) for g,r in zip(ig, rg0) ] + ops += [ TestOperator(g, range=r) for g,r in zip(ig, rg1) ] + print (len(ops)) + K = BlockOperator(*ops) + print ("K col comp? " , K.column_wise_compatible()) + print ("K row comp? " , K.row_wise_compatible()) + for op in ops: + print ("range" , op.range_geometry().shape) + for op in ops: + print ("domain" , op.domain_geometry().shape) + self.assertTrue(False) + except ValueError as ve: + print (ve) + self.assertTrue(True) + def test_ScaledBlockOperatorSingleScalar(self): ig = [ ImageGeometry(10,20,30) , \ ImageGeometry(10,20,30) , \ |