Skip to content

Commit

Permalink
Merge branch 'master' into fista-second-term
Browse files Browse the repository at this point in the history
  • Loading branch information
MargaretDuff authored Dec 11, 2023
2 parents 02e4dba + 86f2534 commit 9544314
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 15 deletions.
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@

* x.x.x
- Allow reduction methods on the DataContainer class to accept axis argument as string which matches values in dimension_labels
- Added the functions `set_norms` and `get_norms` to the `BlockOperator` class
- Internal variable name change in BlockOperator to aid understanding
- Internal variable name change in BlockOperator to aid understanding
- Bug fix for BlockDataContainer as iterator
- Dropped support for IPP versions older than 2021.10 due to header changes
- Fix build include directories


- proximal of MixedL21Norm with numpy backend now accepts numpy ndarray, DataContainer and float as tau parameter
- Allow show2D to be used with 3D `DataContainer` instances

* 23.1.0
- Fix bug in IndicatorBox proximal_conjugate
- Allow CCPi Regulariser functions for non CIL object
Expand Down
7 changes: 5 additions & 2 deletions Wrappers/Python/cil/framework/framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -2791,11 +2791,14 @@ def as_array(self):
return self.array


def get_slice(self,**kw):
def get_slice(self, **kw):
'''
Returns a new DataContainer containing a single slice of in the requested direction. \
Returns a new DataContainer containing a single slice in the requested direction. \
Pass keyword arguments <dimension label>=index
'''
# Force is not relevant for a DataContainer:
kw.pop('force', None)

new_array = None

#get ordered list of current dimensions
Expand Down
21 changes: 15 additions & 6 deletions Wrappers/Python/cil/optimisation/functions/MixedL21Norm.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ def _proximal_step_numba(arr, abstau):



def _proximal_step_numpy(tmp, tau):
def _proximal_step_numpy(arr, tau):
'''Numpy implementation of a step in the calculation of the proximal of MixedL21Norm
Parameters:
-----------
tmp : DataContainer/ numpy array, best if contiguous memory.
tau: float or DataContainer
arr : DataContainer, best if contiguous memory.
tau: float, numpy array or DataContainer
Returns:
--------
Expand All @@ -72,15 +72,24 @@ def _proximal_step_numpy(tmp, tau):
'''
# Note: we divide x by tau so the cases of tau both scalar and
# DataContainers run
tmp /= np.abs(tau, dtype=np.float32)
res = tmp - 1
try:
tmp = np.abs(tau, dtype=np.float32)
except np.core._exceptions._UFuncInputCastingError:
tmp = tau.abs()


arr /= tmp
res = arr - 1
res.maximum(0.0, out=res)
res /= tmp
res /= arr

arr *= tmp

resarray = res.as_array()
resarray[np.isnan(resarray)] = 0
res.fill(resarray)
return res


class MixedL21Norm(Function):

Expand Down
4 changes: 2 additions & 2 deletions Wrappers/Python/cil/utilities/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ class show2D(show_base):
Plots 1 or more 2D plots in an (n x num_cols) matrix.
Can plot multiple slices from one 3D dataset, or compare multiple datasets
Inputs can be single arguments or list of arguments that will be sequentally applied to subplots
Inputs can be single arguments or list of arguments that will be sequentially applied to subplots
If no slice_list is passed a 3D dataset will display the centre slice of the outer dimension, a 4D dataset will show the centre slices of the two outer dimension.
Expand All @@ -392,7 +392,7 @@ class show2D(show_base):
The title for each figure
slice_list: tuple, int, list of tuples, list of ints, optional
The slices to show. A list of integers will show slices for the outer dimension. For 3D datacontainers single slice: (direction, index). For 4D datacontainers two slices: [(direction0, index),(direction1, index)].
fix_range: boolian, tuple, list of tuples
fix_range: boolean, tuple, list of tuples
Sets the display range of the data. `True` sets all plots to the global (min, max).
axis_labels: tuple, list of tuples, optional
The axis labels for each figure e.g. ('x','y')
Expand Down
31 changes: 30 additions & 1 deletion Wrappers/Python/test/test_DataContainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,36 @@ def test_ImageDataSubset(self):
ss3 = vol.get_slice(channel=0)
self.assertListEqual([ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X], list(ss3.geometry.dimension_labels))

def test_DataContainerSubset(self):
dc = DataContainer(numpy.ones((2,3,4,5)))

dc.dimension_labels =[AcquisitionGeometry.CHANNEL ,
AcquisitionGeometry.ANGLE , AcquisitionGeometry.VERTICAL ,
AcquisitionGeometry.HORIZONTAL]

# test reshape
new_order = [AcquisitionGeometry.HORIZONTAL ,
AcquisitionGeometry.CHANNEL , AcquisitionGeometry.VERTICAL ,
AcquisitionGeometry.ANGLE]
dc.reorder(new_order)

self.assertListEqual(new_order, list(dc.dimension_labels))

ss1 = dc.get_slice(vertical=0)

self.assertListEqual([AcquisitionGeometry.HORIZONTAL ,
AcquisitionGeometry.CHANNEL ,
AcquisitionGeometry.ANGLE], list(ss1.dimension_labels))

ss2 = dc.get_slice(vertical=0, channel=0)
self.assertListEqual([AcquisitionGeometry.HORIZONTAL ,
AcquisitionGeometry.ANGLE], list(ss2.dimension_labels))

# Check we can get slice still even if force parameter is passed:
ss3 = dc.get_slice(vertical=0, channel=0, force=True)
self.assertListEqual([AcquisitionGeometry.HORIZONTAL ,
AcquisitionGeometry.ANGLE], list(ss3.dimension_labels))


def test_DataContainerChaining(self):
dc = self.create_DataContainer(256,256,256,1)
Expand Down Expand Up @@ -1286,4 +1316,3 @@ def test_fill_dimension_AcquisitionData(self):
numpy.testing.assert_array_equal(u.get_slice(channel=1, vertical=1).as_array(), 3 * a)



46 changes: 46 additions & 0 deletions Wrappers/Python/test/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,52 @@ def test_MixedL21Norm_step(self):
# check they are the same
np.testing.assert_allclose(res1, res2.as_array(), atol=1e-5, rtol=1e-6)

def test_MixedL21Norm_proximal_step_numpy_float(self):
from cil.optimisation.functions.MixedL21Norm import _proximal_step_numpy
from cil.framework import ImageGeometry

tau = 1.1

ig = ImageGeometry(2,3,4)
tmp = ig.allocate(1)
a = _proximal_step_numpy(tmp, tau)

b = _proximal_step_numpy(tmp, -tau)

np.testing.assert_allclose(a.as_array(), b.as_array())

def test_MixedL21Norm_proximal_step_numpy_dc(self):
from cil.optimisation.functions.MixedL21Norm import _proximal_step_numpy
from cil.framework import ImageGeometry


ig = ImageGeometry(2,3,4)
tmp = ig.allocate(1)
tau = ig.allocate(2)
a = _proximal_step_numpy(tmp, tau)

tau *= -1
b = _proximal_step_numpy(tmp, tau)

np.testing.assert_allclose(a.as_array(), b.as_array())

def test_MixedL21Norm_proximal_step_numpy_ndarray(self):
from cil.optimisation.functions.MixedL21Norm import _proximal_step_numpy
from cil.framework import ImageGeometry


ig = ImageGeometry(2,3,4)
tmp = ig.allocate(1)
tau = ig.allocate(2)
tauarr = tau.as_array()
a = _proximal_step_numpy(tmp, tauarr)

tauarr *= -1
b = _proximal_step_numpy(tmp, tauarr)

np.testing.assert_allclose(a.as_array(), b.as_array())


def test_smoothL21Norm(self):
ig = ImageGeometry(4, 5)
bg = BlockGeometry(ig, ig)
Expand Down

0 comments on commit 9544314

Please sign in to comment.