summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdoardo Pasca <edo.paskino@gmail.com>2019-06-13 22:46:31 +0100
committerGitHub <noreply@github.com>2019-06-13 22:46:31 +0100
commit3854b6f03a5c2c7293b61d61b0db4e42838278de (patch)
tree093b1993d8ef893ddb28d537323bef6115b7d58c
parentb11f9e3c1fbf2181b906b185e1b32314e021d573 (diff)
parent00a9428244fabe9d69f19470aae92b3b3ebdbb03 (diff)
downloadframework-3854b6f03a5c2c7293b61d61b0db4e42838278de.tar.gz
framework-3854b6f03a5c2c7293b61d61b0db4e42838278de.tar.bz2
framework-3854b6f03a5c2c7293b61d61b0db4e42838278de.tar.xz
framework-3854b6f03a5c2c7293b61d61b0db4e42838278de.zip
Merge pull request #254 from vais-ral/demos
Demos
-rwxr-xr-xWrappers/Python/ccpi/framework/BlockDataContainer.py25
-rwxr-xr-xWrappers/Python/ccpi/framework/BlockGeometry.py51
-rwxr-xr-xWrappers/Python/ccpi/framework/TestData.py121
-rwxr-xr-xWrappers/Python/ccpi/framework/Vector.py96
-rwxr-xr-xWrappers/Python/ccpi/framework/__init__.py54
-rwxr-xr-xWrappers/Python/ccpi/framework/framework.py94
-rwxr-xr-xWrappers/Python/ccpi/optimisation/algorithms/Algorithm.py6
-rwxr-xr-xWrappers/Python/ccpi/optimisation/algorithms/CGLS.py54
-rw-r--r--Wrappers/Python/ccpi/optimisation/algorithms/FBPD.py86
-rwxr-xr-xWrappers/Python/ccpi/optimisation/algorithms/FISTA.py121
-rwxr-xr-xWrappers/Python/ccpi/optimisation/algorithms/GradientDescent.py3
-rw-r--r--Wrappers/Python/ccpi/optimisation/algorithms/PDHG.py180
-rw-r--r--Wrappers/Python/ccpi/optimisation/algorithms/SIRT.py8
-rw-r--r--Wrappers/Python/ccpi/optimisation/algorithms/__init__.py2
-rwxr-xr-xWrappers/Python/ccpi/optimisation/algs.py307
-rwxr-xr-xWrappers/Python/ccpi/optimisation/funcs.py265
-rw-r--r--Wrappers/Python/ccpi/optimisation/functions/BlockFunction.py34
-rw-r--r--Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition.py103
-rw-r--r--Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition_old.py85
-rwxr-xr-xWrappers/Python/ccpi/optimisation/functions/IndicatorBox.py135
-rw-r--r--Wrappers/Python/ccpi/optimisation/functions/KullbackLeibler.py178
-rw-r--r--Wrappers/Python/ccpi/optimisation/functions/L1Norm.py126
-rw-r--r--Wrappers/Python/ccpi/optimisation/functions/L2NormSquared.py53
-rwxr-xr-xWrappers/Python/ccpi/optimisation/functions/MixedL21Norm.py61
-rwxr-xr-xWrappers/Python/ccpi/optimisation/functions/Norm2Sq.py1
-rwxr-xr-xWrappers/Python/ccpi/optimisation/functions/ScaledFunction.py6
-rw-r--r--Wrappers/Python/ccpi/optimisation/functions/ZeroFunction.py8
-rwxr-xr-xWrappers/Python/ccpi/optimisation/operators/BlockOperator.py67
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator.py2
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator_old.py374
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/GradientOperator.py54
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/IdentityOperator.py2
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/LinearOperatorMatrix.py17
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/SparseFiniteDiff.py4
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/SymmetrizedGradientOperator.py240
-rw-r--r--Wrappers/Python/ccpi/optimisation/operators/ZeroOperator.py27
-rwxr-xr-xWrappers/Python/ccpi/optimisation/operators/__init__.py2
-rwxr-xr-xWrappers/Python/ccpi/processors/CenterOfRotationFinder.py (renamed from Wrappers/Python/ccpi/processors.py)106
-rwxr-xr-xWrappers/Python/ccpi/processors/Normalizer.py124
-rw-r--r--Wrappers/Python/conda-recipe/conda_build_config.yaml2
-rw-r--r--Wrappers/Python/conda-recipe/meta.yaml3
-rw-r--r--Wrappers/Python/data/boat.tiffbin0 -> 262278 bytes
-rw-r--r--Wrappers/Python/data/camera.pngbin0 -> 114228 bytes
-rw-r--r--Wrappers/Python/data/peppers.tiffbin0 -> 786572 bytes
-rwxr-xr-xWrappers/Python/data/resolution_chart.tiffbin0 -> 65670 bytes
-rw-r--r--Wrappers/Python/data/shapes.pngbin0 -> 19339 bytes
-rw-r--r--Wrappers/Python/data/test_show_data.py30
-rw-r--r--Wrappers/Python/demos/CGLS_examples/CGLS_Tikhonov.py106
-rw-r--r--Wrappers/Python/demos/CompareAlgorithms/CGLS_FISTA_PDHG_LeastSquares.py189
-rw-r--r--Wrappers/Python/demos/CompareAlgorithms/CGLS_PDHG_Tikhonov.py133
-rw-r--r--Wrappers/Python/demos/CompareAlgorithms/FISTA_vs_PDHG.py124
-rw-r--r--Wrappers/Python/demos/FISTA_examples/FISTA_CGLS.py215
-rw-r--r--Wrappers/Python/demos/FISTA_examples/FISTA_Tikhonov_Poisson_Denoising.py201
-rw-r--r--Wrappers/Python/demos/PDHG_examples/ColorbayDemo.py273
-rwxr-xr-xWrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TGV_Denoising.py282
-rwxr-xr-xWrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising.py280
-rw-r--r--Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_2D_time.py243
-rw-r--r--Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_Gaussian_3D.py147
-rw-r--r--Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Tomo2D.py173
-rw-r--r--Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_Tikhonov_Denoising.py258
-rwxr-xr-xWrappers/Python/demos/PDHG_examples/GatherAll/phantom.matbin0 -> 5583 bytes
-rw-r--r--Wrappers/Python/demos/PDHG_examples/IMATDemo.py339
-rw-r--r--Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_2D_time_denoising.py169
-rw-r--r--Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_TV_Tomo2D_time.py169
-rw-r--r--Wrappers/Python/demos/PDHG_examples/PDHG_TV_Color_Denoising.py115
-rw-r--r--Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_2D_time.py192
-rw-r--r--Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_Gaussian_3D.py181
-rw-r--r--Wrappers/Python/demos/PDHG_examples/Tomo/PDHG_Tikhonov_Tomo2D.py156
-rw-r--r--Wrappers/Python/demos/pdhg_TV_tomography2Dccpi.py238
-rw-r--r--Wrappers/Python/environment.yml11
-rw-r--r--Wrappers/Python/setup.py11
-rwxr-xr-xWrappers/Python/test/test_DataContainer.py83
-rwxr-xr-xWrappers/Python/test/test_DataProcessor.py24
-rwxr-xr-xWrappers/Python/test/test_Gradient.py15
-rwxr-xr-xWrappers/Python/test/test_NexusReader.py26
-rwxr-xr-xWrappers/Python/test/test_algorithms.py28
-rw-r--r--Wrappers/Python/test/test_functions.py2
-rwxr-xr-xWrappers/Python/test/test_run_test.py14
-rw-r--r--Wrappers/Python/wip/Demos/FISTA_vs_CGLS.py119
-rw-r--r--Wrappers/Python/wip/Demos/FISTA_vs_PDHG.py120
-rw-r--r--Wrappers/Python/wip/Demos/IMAT_Reconstruction/TV_WhiteBeam_reconstruction.py164
-rw-r--r--Wrappers/Python/wip/Demos/IMAT_Reconstruction/golden_angles.txt186
-rw-r--r--Wrappers/Python/wip/Demos/LeastSq_CGLS_FISTA_PDHG.py154
-rw-r--r--Wrappers/Python/wip/Demos/PDHG_TGV_Denoising_SaltPepper.py194
-rw-r--r--Wrappers/Python/wip/Demos/PDHG_TV_Denoising_Gaussian_DiagPrecond.py208
-rw-r--r--Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D.py245
-rw-r--r--Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D_time.py169
-rw-r--r--Wrappers/Python/wip/Demos/PDHG_Tikhonov_Tomo2D.py108
-rw-r--r--Wrappers/Python/wip/Demos/PDHG_vs_CGLS.py127
-rw-r--r--Wrappers/Python/wip/Demos/check_blockOperator_sum_row_cols.py89
-rw-r--r--Wrappers/Python/wip/Demos/check_precond.py182
-rw-r--r--Wrappers/Python/wip/compare_CGLS_algos.py28
-rw-r--r--Wrappers/Python/wip/demo_box_constraints_FISTA.py2
-rw-r--r--Wrappers/Python/wip/demo_colourbay.py2
-rw-r--r--Wrappers/Python/wip/fista_TV_denoising.py72
-rwxr-xr-xWrappers/Python/wip/fix_test.py208
-rw-r--r--Wrappers/Python/wip/old_demos/demo_colourbay.py137
-rw-r--r--Wrappers/Python/wip/old_demos/demo_compare_cvx.py306
-rwxr-xr-xWrappers/Python/wip/old_demos/demo_gradient_descent.py295
-rw-r--r--Wrappers/Python/wip/old_demos/demo_imat_multichan_RGLTK.py151
-rw-r--r--Wrappers/Python/wip/old_demos/demo_imat_whitebeam.py138
-rwxr-xr-xWrappers/Python/wip/old_demos/demo_memhandle.py193
-rw-r--r--Wrappers/Python/wip/old_demos/demo_test_sirt.py176
-rwxr-xr-xWrappers/Python/wip/old_demos/multifile_nexus.py307
-rwxr-xr-xWrappers/Python/wip/pdhg_TV_denoising.py124
-rw-r--r--Wrappers/Python/wip/pdhg_TV_denoising3D.py360
-rw-r--r--Wrappers/Python/wip/pdhg_TV_denoising_salt_pepper.py180
-rw-r--r--Wrappers/Python/wip/pdhg_TV_tomography2D.py156
-rw-r--r--Wrappers/Python/wip/pdhg_TV_tomography2D_time.py152
-rw-r--r--Wrappers/Python/wip/pdhg_tv_denoising_poisson.py168
-rw-r--r--Wrappers/Python/wip/test_pdhg_gap.py140
-rw-r--r--Wrappers/Python/wip/test_pdhg_profile/profile_pdhg_TV_denoising.py273
-rw-r--r--Wrappers/Python/wip/test_profile.py84
113 files changed, 9827 insertions, 3604 deletions
diff --git a/Wrappers/Python/ccpi/framework/BlockDataContainer.py b/Wrappers/Python/ccpi/framework/BlockDataContainer.py
index 386cd87..670e214 100755
--- a/Wrappers/Python/ccpi/framework/BlockDataContainer.py
+++ b/Wrappers/Python/ccpi/framework/BlockDataContainer.py
@@ -251,6 +251,18 @@ class BlockDataContainer(object):
elif isinstance(other, list) or isinstance(other, numpy.ndarray):
return type(self)(*[ el.maximum(ot, *args, **kwargs) for el,ot in zip(self.containers,other)], shape=self.shape)
return type(self)(*[ el.maximum(ot, *args, **kwargs) for el,ot in zip(self.containers,other.containers)], shape=self.shape)
+
+
+ def minimum(self,other, *args, **kwargs):
+ if not self.is_compatible(other):
+ raise ValueError('Incompatible for maximum')
+ out = kwargs.get('out', None)
+ if isinstance(other, Number):
+ return type(self)(*[ el.minimum(other, *args, **kwargs) for el in self.containers], shape=self.shape)
+ elif isinstance(other, list) or isinstance(other, numpy.ndarray):
+ return type(self)(*[ el.minimum(ot, *args, **kwargs) for el,ot in zip(self.containers,other)], shape=self.shape)
+ return type(self)(*[ el.minimum(ot, *args, **kwargs) for el,ot in zip(self.containers,other.containers)], shape=self.shape)
+
## unary operations
def abs(self, *args, **kwargs):
@@ -270,6 +282,7 @@ class BlockDataContainer(object):
def squared_norm(self):
y = numpy.asarray([el.squared_norm() for el in self.containers])
return y.sum()
+
def norm(self):
return numpy.sqrt(self.squared_norm())
@@ -442,9 +455,12 @@ class BlockDataContainer(object):
'''Inline truedivision'''
return self.__idiv__(other)
+ def dot(self, other):
+#
+ tmp = [ self.containers[i].dot(other.containers[i]) for i in range(self.shape[0])]
+ return sum(tmp)
-
-
+
if __name__ == '__main__':
@@ -456,6 +472,7 @@ if __name__ == '__main__':
BG = BlockGeometry(ig, ig)
U = BG.allocate('random_int')
+ V = BG.allocate('random_int')
print ("test sum BDC " )
@@ -468,10 +485,10 @@ if __name__ == '__main__':
z1 = sum(U**2).sqrt().as_array()
numpy.testing.assert_array_equal(z, z1)
-
-
z2 = U.pnorm(2)
+ zzz = U.dot(V)
+
diff --git a/Wrappers/Python/ccpi/framework/BlockGeometry.py b/Wrappers/Python/ccpi/framework/BlockGeometry.py
index 5dd6750..e58035b 100755
--- a/Wrappers/Python/ccpi/framework/BlockGeometry.py
+++ b/Wrappers/Python/ccpi/framework/BlockGeometry.py
@@ -10,6 +10,12 @@ from ccpi.framework import BlockDataContainer
#from ccpi.optimisation.operators import Operator, LinearOperator
class BlockGeometry(object):
+
+ RANDOM = 'random'
+ RANDOM_INT = 'random_int'
+
+
+
'''Class to hold Geometry as column vector'''
#__array_priority__ = 1
def __init__(self, *args, **kwargs):
@@ -31,7 +37,50 @@ class BlockGeometry(object):
'''returns the Geometry in the BlockGeometry located at position index'''
return self.geometries[index]
- def allocate(self, value=0, dimension_labels=None):
+ def allocate(self, value=0, dimension_labels=None, **kwargs):
+
+ symmetry = kwargs.get('symmetry',False)
containers = [geom.allocate(value) for geom in self.geometries]
+
+ if symmetry == True:
+
+ # for 2x2
+ # [ ig11, ig12\
+ # ig21, ig22]
+
+ # Row-wise Order
+
+ if len(containers)==4:
+ containers[1]=containers[2]
+
+ # for 3x3
+ # [ ig11, ig12, ig13\
+ # ig21, ig22, ig23\
+ # ig31, ig32, ig33]
+
+ elif len(containers)==9:
+ containers[1]=containers[3]
+ containers[2]=containers[6]
+ containers[5]=containers[7]
+
+ # for 4x4
+ # [ ig11, ig12, ig13, ig14\
+ # ig21, ig22, ig23, ig24\
+ # ig31, ig32, ig33, ig34
+ # ig41, ig42, ig43, ig44]
+
+ elif len(containers) == 16:
+ containers[1]=containers[4]
+ containers[2]=containers[8]
+ containers[3]=containers[12]
+ containers[6]=containers[9]
+ containers[7]=containers[10]
+ containers[11]=containers[15]
+
+
+
+
return BlockDataContainer(*containers)
+
+
diff --git a/Wrappers/Python/ccpi/framework/TestData.py b/Wrappers/Python/ccpi/framework/TestData.py
new file mode 100755
index 0000000..cfad7a3
--- /dev/null
+++ b/Wrappers/Python/ccpi/framework/TestData.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+from ccpi.framework import ImageData, ImageGeometry
+import numpy
+from PIL import Image
+import os
+import os.path
+
+data_dir = os.path.abspath(os.path.join(
+ os.path.dirname(__file__),
+ '../data/')
+)
+
+class TestData(object):
+ BOAT = 'boat.tiff'
+ CAMERA = 'camera.png'
+ PEPPERS = 'peppers.tiff'
+ RESOLUTION_CHART = 'resolution_chart.tiff'
+ SIMPLE_PHANTOM_2D = 'simple_jakobs_phantom'
+ SHAPES = 'shapes.png'
+
+ def __init__(self, **kwargs):
+ self.data_dir = kwargs.get('data_dir', data_dir)
+
+ def load(self, which, size=(512,512), scale=(0,1), **kwargs):
+ if which not in [TestData.BOAT, TestData.CAMERA,
+ TestData.PEPPERS, TestData.RESOLUTION_CHART,
+ TestData.SIMPLE_PHANTOM_2D, TestData.SHAPES]:
+ raise ValueError('Unknown TestData {}.'.format(which))
+ if which == TestData.SIMPLE_PHANTOM_2D:
+ N = size[0]
+ M = size[1]
+ sdata = numpy.zeros((N,M))
+ sdata[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+ sdata[round(M/8):round(7*M/8),round(3*M/8):round(5*M/8)] = 1
+ ig = ImageGeometry(voxel_num_x = N, voxel_num_y = M, dimension_labels=[ImageGeometry.HORIZONTAL_X, ImageGeometry.HORIZONTAL_Y])
+ data = ig.allocate()
+ data.fill(sdata)
+
+ elif which == TestData.SHAPES:
+
+ tmp = numpy.array(Image.open(os.path.join(self.data_dir, which)).convert('L'))
+ N = 200
+ M = 300
+ ig = ImageGeometry(voxel_num_x = N, voxel_num_y = M, dimension_labels=[ImageGeometry.HORIZONTAL_X, ImageGeometry.HORIZONTAL_Y])
+ data = ig.allocate()
+ data.fill(tmp/numpy.max(tmp))
+
+ else:
+ tmp = Image.open(os.path.join(self.data_dir, which))
+ print (tmp)
+ bands = tmp.getbands()
+ if len(bands) > 1:
+ ig = ImageGeometry(voxel_num_x=size[0], voxel_num_y=size[1], channels=len(bands),
+ dimension_labels=[ImageGeometry.HORIZONTAL_X, ImageGeometry.HORIZONTAL_Y, ImageGeometry.CHANNEL])
+ data = ig.allocate()
+ else:
+ ig = ImageGeometry(voxel_num_x = size[0], voxel_num_y = size[1], dimension_labels=[ImageGeometry.HORIZONTAL_X, ImageGeometry.HORIZONTAL_Y])
+ data = ig.allocate()
+ data.fill(numpy.array(tmp.resize((size[1],size[0]))))
+ if scale is not None:
+ dmax = data.as_array().max()
+ dmin = data.as_array().min()
+ # scale 0,1
+ data = (data -dmin) / (dmax - dmin)
+ if scale != (0,1):
+ #data = (data-dmin)/(dmax-dmin) * (scale[1]-scale[0]) +scale[0])
+ data *= (scale[1]-scale[0])
+ data += scale[0]
+ print ("data.geometry", data.geometry)
+ return data
+
+ def camera(**kwargs):
+
+ tmp = Image.open(os.path.join(data_dir, 'camera.png'))
+
+ size = kwargs.get('size',(512, 512))
+
+ data = numpy.array(tmp.resize(size))
+
+ data = data/data.max()
+
+ return ImageData(data)
+
+
+ def boat(**kwargs):
+
+ tmp = Image.open(os.path.join(data_dir, 'boat.tiff'))
+
+ size = kwargs.get('size',(512, 512))
+
+ data = numpy.array(tmp.resize(size))
+
+ data = data/data.max()
+
+ return ImageData(data)
+
+
+ def peppers(**kwargs):
+
+ tmp = Image.open(os.path.join(data_dir, 'peppers.tiff'))
+
+ size = kwargs.get('size',(512, 512))
+
+ data = numpy.array(tmp.resize(size))
+
+ data = data/data.max()
+
+ return ImageData(data)
+
+ def shapes(**kwargs):
+
+ tmp = Image.open(os.path.join(data_dir, 'shapes.png')).convert('LA')
+
+ size = kwargs.get('size',(300, 200))
+
+ data = numpy.array(tmp.resize(size))
+
+ data = data/data.max()
+
+ return ImageData(data)
+
diff --git a/Wrappers/Python/ccpi/framework/Vector.py b/Wrappers/Python/ccpi/framework/Vector.py
new file mode 100755
index 0000000..542cb7a
--- /dev/null
+++ b/Wrappers/Python/ccpi/framework/Vector.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import numpy
+import sys
+from datetime import timedelta, datetime
+import warnings
+from functools import reduce
+from numbers import Number
+from ccpi.framework import DataContainer
+
+class VectorData(DataContainer):
+ def __init__(self, array=None, **kwargs):
+ self.geometry = kwargs.get('geometry', None)
+ self.dtype = kwargs.get('dtype', numpy.float32)
+
+ if self.geometry is None:
+ if array is None:
+ raise ValueError('Please specify either a geometry or an array')
+ else:
+ if len(array.shape) > 1:
+ raise ValueError('Incompatible size: expected 1D got {}'.format(array.shape))
+ out = array
+ self.geometry = VectorGeometry(array.shape[0])
+ self.length = self.geometry.length
+ else:
+ self.length = self.geometry.length
+
+ if array is None:
+ out = numpy.zeros((self.length,), dtype=self.dtype)
+ else:
+ if self.length == array.shape[0]:
+ out = array
+ else:
+ raise ValueError('Incompatible size: expecting {} got {}'.format((self.length,), array.shape))
+ deep_copy = True
+ super(VectorData, self).__init__(out, deep_copy, None)
+
+class VectorGeometry(object):
+ RANDOM = 'random'
+ RANDOM_INT = 'random_int'
+
+ def __init__(self,
+ length):
+
+ self.length = length
+ self.shape = (length, )
+
+
+ def clone(self):
+ '''returns a copy of VectorGeometry'''
+ return VectorGeometry(self.length)
+
+ def allocate(self, value=0, **kwargs):
+ '''allocates an VectorData according to the size expressed in the instance'''
+ self.dtype = kwargs.get('dtype', numpy.float32)
+ out = VectorData(geometry=self, dtype=self.dtype)
+ if isinstance(value, Number):
+ if value != 0:
+ out += value
+ else:
+ if value == VectorGeometry.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 == VectorGeometry.RANDOM_INT:
+ seed = kwargs.get('seed', None)
+ if seed is not None:
+ numpy.random.seed(seed)
+ max_value = kwargs.get('max_value', 100)
+ out.fill(numpy.random.randint(max_value,size=self.shape))
+ else:
+ raise ValueError('Value {} unknown'.format(value))
+ return out
diff --git a/Wrappers/Python/ccpi/framework/__init__.py b/Wrappers/Python/ccpi/framework/__init__.py
index 229edb5..3de27ed 100755
--- a/Wrappers/Python/ccpi/framework/__init__.py
+++ b/Wrappers/Python/ccpi/framework/__init__.py
@@ -1,26 +1,28 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Tue Mar 5 16:00:18 2019
-
-@author: ofn77899
-"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
-import numpy
-import sys
-from datetime import timedelta, datetime
-import warnings
-from functools import reduce
-
-
-from .framework import DataContainer
-from .framework import ImageData, AcquisitionData
-from .framework import ImageGeometry, AcquisitionGeometry
-from .framework import find_key, message
-from .framework import DataProcessor
-from .framework import AX, PixelByPixelDataProcessor, CastDataContainer
-from .BlockDataContainer import BlockDataContainer
-from .BlockGeometry import BlockGeometry
+# -*- coding: utf-8 -*-
+"""
+Created on Tue Mar 5 16:00:18 2019
+
+@author: ofn77899
+"""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import numpy
+import sys
+from datetime import timedelta, datetime
+import warnings
+from functools import reduce
+
+
+from .framework import DataContainer
+from .framework import ImageData, AcquisitionData
+from .framework import ImageGeometry, AcquisitionGeometry
+from .framework import find_key, message
+from .framework import DataProcessor
+from .framework import AX, PixelByPixelDataProcessor, CastDataContainer
+from .BlockDataContainer import BlockDataContainer
+from .BlockGeometry import BlockGeometry
+from .TestData import TestData
+from .Vector import VectorGeometry, VectorData
diff --git a/Wrappers/Python/ccpi/framework/framework.py b/Wrappers/Python/ccpi/framework/framework.py
index 8ee2c75..caea1e1 100755
--- a/Wrappers/Python/ccpi/framework/framework.py
+++ b/Wrappers/Python/ccpi/framework/framework.py
@@ -29,7 +29,6 @@ import warnings
from functools import reduce
from numbers import Number
-
def find_key(dic, val):
"""return the key of dictionary dic given the value"""
return [k for k, v in dic.items() if v == val][0]
@@ -63,7 +62,8 @@ class ImageGeometry(object):
center_x=0,
center_y=0,
center_z=0,
- channels=1):
+ channels=1,
+ **kwargs):
self.voxel_num_x = voxel_num_x
self.voxel_num_y = voxel_num_y
@@ -80,25 +80,44 @@ class ImageGeometry(object):
if self.channels > 1:
if self.voxel_num_z>1:
self.length = 4
- self.shape = (self.channels, self.voxel_num_z, self.voxel_num_y, self.voxel_num_x)
+ shape = (self.channels, self.voxel_num_z, self.voxel_num_y, self.voxel_num_x)
dim_labels = [ImageGeometry.CHANNEL, ImageGeometry.VERTICAL,
ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X]
else:
self.length = 3
- self.shape = (self.channels, self.voxel_num_y, self.voxel_num_x)
+ shape = (self.channels, self.voxel_num_y, self.voxel_num_x)
dim_labels = [ImageGeometry.CHANNEL, ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X]
else:
if self.voxel_num_z>1:
self.length = 3
- self.shape = (self.voxel_num_z, self.voxel_num_y, self.voxel_num_x)
+ shape = (self.voxel_num_z, self.voxel_num_y, self.voxel_num_x)
dim_labels = [ImageGeometry.VERTICAL, ImageGeometry.HORIZONTAL_Y,
ImageGeometry.HORIZONTAL_X]
else:
self.length = 2
- self.shape = (self.voxel_num_y, self.voxel_num_x)
+ shape = (self.voxel_num_y, self.voxel_num_x)
dim_labels = [ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X]
- self.dimension_labels = dim_labels
+ labels = kwargs.get('dimension_labels', None)
+ if labels is None:
+ self.shape = shape
+ self.dimension_labels = dim_labels
+ else:
+ order = self.get_order_by_label(labels, dim_labels)
+ if order != [0,1,2]:
+ # resort
+ self.shape = tuple([shape[i] for i in order])
+ self.dimension_labels = labels
+
+ def get_order_by_label(self, dimension_labels, default_dimension_labels):
+ order = []
+ for i, el in enumerate(dimension_labels):
+ for j, ek in enumerate(default_dimension_labels):
+ if el == ek:
+ order.append(j)
+ break
+ return order
+
def get_min_x(self):
return self.center_x - 0.5*self.voxel_num_x*self.voxel_size_x
@@ -146,7 +165,10 @@ class ImageGeometry(object):
return repres
def allocate(self, value=0, dimension_labels=None, **kwargs):
'''allocates an ImageData according to the size expressed in the instance'''
- out = ImageData(geometry=self)
+ if dimension_labels is None:
+ out = ImageData(geometry=self, dimension_labels=self.dimension_labels)
+ else:
+ out = ImageData(geometry=self, dimension_labels=dimension_labels)
if isinstance(value, Number):
if value != 0:
out += value
@@ -164,9 +186,7 @@ class ImageGeometry(object):
out.fill(numpy.random.randint(max_value,size=self.shape))
else:
raise ValueError('Value {} unknown'.format(value))
- if dimension_labels is not None:
- if dimension_labels != self.dimension_labels:
- return out.subset(dimensions=dimension_labels)
+
return out
# The following methods return 2 members of the class, therefore I
# don't think we need to implement them.
@@ -247,6 +267,8 @@ class AcquisitionGeometry(object):
self.channels = channels
self.angle_unit=kwargs.get(AcquisitionGeometry.ANGLE_UNIT,
AcquisitionGeometry.DEGREE)
+
+ # default labels
if channels > 1:
if pixel_num_v > 1:
shape = (channels, num_of_angles , pixel_num_v, pixel_num_h)
@@ -265,9 +287,31 @@ class AcquisitionGeometry(object):
else:
shape = (num_of_angles, pixel_num_h)
dim_labels = [AcquisitionGeometry.ANGLE, AcquisitionGeometry.HORIZONTAL]
- self.shape = shape
+
+ labels = kwargs.get('dimension_labels', None)
+ if labels is None:
+ self.shape = shape
+ self.dimension_labels = dim_labels
+ else:
+ if len(labels) != len(dim_labels):
+ raise ValueError('Wrong number of labels. Expected {} got {}'.format(len(dim_labels), len(labels)))
+ order = self.get_order_by_label(labels, dim_labels)
+ if order != [0,1,2]:
+ # resort
+ self.shape = tuple([shape[i] for i in order])
+ self.dimension_labels = labels
+
+ def get_order_by_label(self, dimension_labels, default_dimension_labels):
+ order = []
+ for i, el in enumerate(dimension_labels):
+ for j, ek in enumerate(default_dimension_labels):
+ if el == ek:
+ order.append(j)
+ break
+ return order
+
+
- self.dimension_labels = dim_labels
def clone(self):
'''returns a copy of the AcquisitionGeometry'''
@@ -295,7 +339,10 @@ class AcquisitionGeometry(object):
return repres
def allocate(self, value=0, dimension_labels=None):
'''allocates an AcquisitionData according to the size expressed in the instance'''
- out = AcquisitionData(geometry=self)
+ if dimension_labels is None:
+ out = AcquisitionData(geometry=self, dimension_labels=self.dimension_labels)
+ else:
+ out = AcquisitionData(geometry=self, dimension_labels=dimension_labels)
if isinstance(value, Number):
if value != 0:
out += value
@@ -313,9 +360,7 @@ class AcquisitionGeometry(object):
out.fill(numpy.random.randint(max_value,size=self.shape))
else:
raise ValueError('Value {} unknown'.format(value))
- if dimension_labels is not None:
- if dimension_labels != self.dimension_labels:
- return out.subset(dimensions=dimension_labels)
+
return out
class DataContainer(object):
@@ -636,6 +681,7 @@ class DataContainer(object):
def pixel_wise_binary(self, pwop, x2, *args, **kwargs):
out = kwargs.get('out', None)
+
if out is None:
if isinstance(x2, (int, float, complex)):
out = pwop(self.as_array() , x2 , *args, **kwargs )
@@ -661,8 +707,12 @@ class DataContainer(object):
# geometry=self.geometry)
return out
else:
- raise ValueError(message(type(self),"Wrong size for data memory: ", out.shape,self.shape))
- elif issubclass(type(out), DataContainer) and isinstance(x2, (int,float,complex)):
+ raise ValueError(message(type(self),"Wrong size for data memory: out {} x2 {} expected {}".format( out.shape,x2.shape ,self.shape)))
+ elif issubclass(type(out), DataContainer) and \
+ isinstance(x2, (int,float,complex, numpy.int, numpy.int8, \
+ numpy.int16, numpy.int32, numpy.int64,\
+ numpy.float, numpy.float16, numpy.float32,\
+ numpy.float64, numpy.complex)):
if self.check_dimensions(out):
kwargs['out']=out.as_array()
pwop(self.as_array(), x2, *args, **kwargs )
@@ -765,12 +815,15 @@ class DataContainer(object):
def norm(self):
'''return the euclidean norm of the DataContainer viewed as a vector'''
return numpy.sqrt(self.squared_norm())
+
+
def dot(self, other, *args, **kwargs):
'''return the inner product of 2 DataContainers viewed as vectors'''
method = kwargs.get('method', 'numpy')
if method not in ['numpy','reduce']:
raise ValueError('dot: specified method not valid. Expecting numpy or reduce got {} '.format(
method))
+
if self.shape == other.shape:
# return (self*other).sum()
if method == 'numpy':
@@ -787,6 +840,7 @@ class DataContainer(object):
else:
raise ValueError('Shapes are not aligned: {} != {}'.format(self.shape, other.shape))
+
@@ -804,7 +858,7 @@ class ImageData(DataContainer):
self.geometry = kwargs.get('geometry', None)
if array is None:
if self.geometry is not None:
- shape, dimension_labels = self.get_shape_labels(self.geometry)
+ shape, dimension_labels = self.get_shape_labels(self.geometry, dimension_labels)
array = numpy.zeros( shape , dtype=numpy.float32)
super(ImageData, self).__init__(array, deep_copy,
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/Algorithm.py b/Wrappers/Python/ccpi/optimisation/algorithms/Algorithm.py
index 9f54c1e..c62d0ea 100755
--- a/Wrappers/Python/ccpi/optimisation/algorithms/Algorithm.py
+++ b/Wrappers/Python/ccpi/optimisation/algorithms/Algorithm.py
@@ -51,6 +51,7 @@ class Algorithm(object):
self.__max_iteration = kwargs.get('max_iteration', 0)
self.__loss = []
self.memopt = False
+ self.configured = False
self.timing = []
self.update_objective_interval = kwargs.get('update_objective_interval', 1)
def set_up(self, *args, **kwargs):
@@ -86,6 +87,8 @@ class Algorithm(object):
raise StopIteration()
else:
time0 = time.time()
+ if not self.configured:
+ raise ValueError('Algorithm not configured correctly. Please run set_up.')
self.update()
self.timing.append( time.time() - time0 )
if self.iteration % self.update_objective_interval == 0:
@@ -152,14 +155,13 @@ class Algorithm(object):
print (self.verbose_header())
if (self.iteration -1) % self.update_objective_interval == 0:
if verbose:
- #print ("Iteration {:>7} max: {:>7}, = {}".format(self.iteration-1,
- # self.max_iteration, self.get_last_objective()) )
print (self.verbose_output())
if callback is not None:
callback(self.iteration -1, self.get_last_objective(), self.x)
i += 1
if i == iterations:
break
+
def verbose_output(self):
'''Creates a nice tabulated output'''
timing = self.timing[-self.update_objective_interval-1:-1]
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/CGLS.py b/Wrappers/Python/ccpi/optimisation/algorithms/CGLS.py
index e65bc89..15acc31 100755
--- a/Wrappers/Python/ccpi/optimisation/algorithms/CGLS.py
+++ b/Wrappers/Python/ccpi/optimisation/algorithms/CGLS.py
@@ -23,6 +23,9 @@ Created on Thu Feb 21 11:11:23 2019
"""
from ccpi.optimisation.algorithms import Algorithm
+from ccpi.optimisation.functions import Norm2Sq
+import numpy
+
class CGLS(Algorithm):
'''Conjugate Gradient Least Squares algorithm
@@ -47,20 +50,26 @@ class CGLS(Algorithm):
def set_up(self, x_init, operator , data ):
self.r = data.copy()
- self.x = x_init.copy()
+ self.x = x_init * 0
self.operator = operator
self.d = operator.adjoint(self.r)
self.normr2 = self.d.squared_norm()
+
+ self.s = self.operator.domain_geometry().allocate()
#if isinstance(self.normr2, Iterable):
# self.normr2 = sum(self.normr2)
#self.normr2 = numpy.sqrt(self.normr2)
#print ("set_up" , self.normr2)
+ n = Norm2Sq(operator, self.data)
+ self.loss.append(n(x_init))
+ self.configured = True
def update(self):
-
+ self.update_new()
+ def update_old(self):
Ad = self.operator.direct(self.d)
#norm = (Ad*Ad).sum()
#if isinstance(norm, Iterable):
@@ -82,5 +91,44 @@ class CGLS(Algorithm):
self.normr2 = normr2_new
self.d = s + beta*self.d
+ def update_new(self):
+
+ Ad = self.operator.direct(self.d)
+ norm = Ad.squared_norm()
+ if norm == 0.:
+ print ('norm = 0, cannot update solution')
+ print ("self.d norm", self.d.squared_norm(), self.d.as_array())
+ raise StopIteration()
+ alpha = self.normr2/norm
+ if alpha == 0.:
+ print ('alpha = 0, cannot update solution')
+ raise StopIteration()
+ self.d *= alpha
+ Ad *= alpha
+ self.r -= Ad
+
+ self.x += self.d
+
+ self.operator.adjoint(self.r, out=self.s)
+ s = self.s
+
+ normr2_new = s.squared_norm()
+
+ beta = normr2_new/self.normr2
+ self.normr2 = normr2_new
+ self.d *= (beta/alpha)
+ self.d += s
+
def update_objective(self):
- self.loss.append(self.r.squared_norm())
+ a = self.r.squared_norm()
+ if a is numpy.nan:
+ raise StopIteration()
+ self.loss.append(a)
+
+# def should_stop(self):
+# if self.iteration > 0:
+# x = self.get_last_objective()
+# a = x > 0
+# return self.max_iteration_stop_cryterion() or (not a)
+# else:
+# return False
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/FBPD.py b/Wrappers/Python/ccpi/optimisation/algorithms/FBPD.py
deleted file mode 100644
index aa07359..0000000
--- a/Wrappers/Python/ccpi/optimisation/algorithms/FBPD.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# -*- coding: utf-8 -*-
-# This work is part of the Core Imaging Library developed by
-# Visual Analytics and Imaging System Group of the Science Technology
-# Facilities Council, STFC
-
-# Copyright 2019 Edoardo Pasca
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-Created on Thu Feb 21 11:09:03 2019
-
-@author: ofn77899
-"""
-
-from ccpi.optimisation.algorithms import Algorithm
-from ccpi.optimisation.functions import ZeroFunction
-
-class FBPD(Algorithm):
- '''FBPD Algorithm
-
- Parameters:
- x_init: initial guess
- f: constraint
- g: data fidelity
- h: regularizer
- opt: additional algorithm
- '''
- constraint = None
- data_fidelity = None
- regulariser = None
- def __init__(self, **kwargs):
- pass
- def set_up(self, x_init, operator=None, constraint=None, data_fidelity=None,\
- regulariser=None, opt=None):
-
- # default inputs
- if constraint is None:
- self.constraint = ZeroFun()
- else:
- self.constraint = constraint
- if data_fidelity is None:
- data_fidelity = ZeroFun()
- else:
- self.data_fidelity = data_fidelity
- if regulariser is None:
- self.regulariser = ZeroFun()
- else:
- self.regulariser = regulariser
-
- # algorithmic parameters
-
-
- # step-sizes
- self.tau = 2 / (self.data_fidelity.L + 2)
- self.sigma = (1/self.tau - self.data_fidelity.L/2) / self.regulariser.L
-
- self.inv_sigma = 1/self.sigma
-
- # initialization
- self.x = x_init
- self.y = operator.direct(self.x)
-
-
- def update(self):
-
- # primal forward-backward step
- x_old = self.x
- self.x = self.x - self.tau * ( self.data_fidelity.grad(self.x) + self.operator.adjoint(self.y) )
- self.x = self.constraint.prox(self.x, self.tau);
-
- # dual forward-backward step
- self.y = self.y + self.sigma * self.operator.direct(2*self.x - x_old);
- self.y = self.y - self.sigma * self.regulariser.prox(self.inv_sigma*self.y, self.inv_sigma);
-
- # time and criterion
- self.loss = self.constraint(self.x) + self.data_fidelity(self.x) + self.regulariser(self.operator.direct(self.x))
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/FISTA.py b/Wrappers/Python/ccpi/optimisation/algorithms/FISTA.py
index 8ea2b6c..c8fd0d4 100755
--- a/Wrappers/Python/ccpi/optimisation/algorithms/FISTA.py
+++ b/Wrappers/Python/ccpi/optimisation/algorithms/FISTA.py
@@ -20,102 +20,65 @@ class FISTA(Algorithm):
x_init: initial guess
f: data fidelity
g: regularizer
- h:
- opt: additional algorithm
+ opt: additional options
'''
-
+
+
def __init__(self, **kwargs):
'''initialisation can be done at creation time if all
proper variables are passed or later with set_up'''
super(FISTA, self).__init__()
- self.f = None
- self.g = None
+ self.f = kwargs.get('f', None)
+ self.g = kwargs.get('g', ZeroFunction())
+ self.x_init = kwargs.get('x_init',None)
self.invL = None
self.t_old = 1
- args = ['x_init', 'f', 'g', 'opt']
- for k,v in kwargs.items():
- if k in args:
- args.pop(args.index(k))
- if len(args) == 0:
- return self.set_up(kwargs['x_init'],
- f=kwargs['f'],
- g=kwargs['g'],
- opt=kwargs['opt'])
+ if self.x_init is not None and \
+ self.f is not None and self.g is not None:
+ print ("FISTA set_up called from creator")
+ self.set_up(self.x_init, self.f, self.g)
+
- def set_up(self, x_init, f=None, g=None, opt=None):
+ def set_up(self, x_init, f, g, opt=None, **kwargs):
- # default inputs
- if f is None:
- self.f = ZeroFunction()
- else:
- self.f = f
- if g is None:
- g = ZeroFunction()
- self.g = g
- else:
- self.g = g
+ self.f = f
+ self.g = g
# algorithmic parameters
if opt is None:
- opt = {'tol': 1e-4, 'memopt':False}
-
- self.tol = opt['tol'] if 'tol' in opt.keys() else 1e-4
- memopt = opt['memopt'] if 'memopt' in opt.keys() else False
- self.memopt = memopt
-
- # initialization
- if memopt:
- self.y = x_init.clone()
- self.x_old = x_init.clone()
- self.x = x_init.clone()
- self.u = x_init.clone()
- else:
- self.x_old = x_init.copy()
- self.y = x_init.copy()
-
- #timing = numpy.zeros(max_iter)
- #criter = numpy.zeros(max_iter)
+ opt = {'tol': 1e-4}
-
+ self.y = x_init.copy()
+ self.x_old = x_init.copy()
+ self.x = x_init.copy()
+ self.u = x_init.copy()
+
+
self.invL = 1/f.L
self.t_old = 1
+ self.update_objective()
+ self.configured = True
def update(self):
- # algorithm loop
- #for it in range(0, max_iter):
-
- if self.memopt:
- # u = y - invL*f.grad(y)
- # store the result in x_old
- self.f.gradient(self.y, out=self.u)
- self.u.__imul__( -self.invL )
- self.u.__iadd__( self.y )
- # x = g.prox(u,invL)
- self.g.proximal(self.u, self.invL, out=self.x)
-
- self.t = 0.5*(1 + numpy.sqrt(1 + 4*(self.t_old**2)))
-
- # y = x + (t_old-1)/t*(x-x_old)
- self.x.subtract(self.x_old, out=self.y)
- self.y.__imul__ ((self.t_old-1)/self.t)
- self.y.__iadd__( self.x )
-
- self.x_old.fill(self.x)
- self.t_old = self.t
-
-
- else:
- u = self.y - self.invL*self.f.gradient(self.y)
-
- self.x = self.g.proximal(u,self.invL)
-
- self.t = 0.5*(1 + numpy.sqrt(1 + 4*(self.t_old**2)))
-
- self.y = self.x + (self.t_old-1)/self.t*(self.x-self.x_old)
-
- self.x_old = self.x.copy()
- self.t_old = self.t
+
+ self.f.gradient(self.y, out=self.u)
+ self.u.__imul__( -self.invL )
+ self.u.__iadd__( self.y )
+ # x = g.prox(u,invL)
+ self.g.proximal(self.u, self.invL, out=self.x)
+
+ self.t = 0.5*(1 + numpy.sqrt(1 + 4*(self.t_old**2)))
+
+# self.x.subtract(self.x_old, out=self.y)
+ self.y = self.x - self.x_old
+ self.y.__imul__ ((self.t_old-1)/self.t)
+ self.y.__iadd__( self.x )
+
+ self.x_old.fill(self.x)
+ self.t_old = self.t
def update_objective(self):
- self.loss.append( self.f(self.x) + self.g(self.x) ) \ No newline at end of file
+ self.loss.append( self.f(self.x) + self.g(self.x) )
+
+
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/GradientDescent.py b/Wrappers/Python/ccpi/optimisation/algorithms/GradientDescent.py
index 14763c5..34bf954 100755
--- a/Wrappers/Python/ccpi/optimisation/algorithms/GradientDescent.py
+++ b/Wrappers/Python/ccpi/optimisation/algorithms/GradientDescent.py
@@ -40,7 +40,7 @@ class GradientDescent(Algorithm):
if k in args:
args.pop(args.index(k))
if len(args) == 0:
- return self.set_up(x_init=kwargs['x_init'],
+ self.set_up(x_init=kwargs['x_init'],
objective_function=kwargs['objective_function'],
rate=kwargs['rate'])
@@ -61,6 +61,7 @@ class GradientDescent(Algorithm):
self.memopt = False
if self.memopt:
self.x_update = x_init.copy()
+ self.configured = True
def update(self):
'''Single iteration'''
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/PDHG.py b/Wrappers/Python/ccpi/optimisation/algorithms/PDHG.py
index d1b5351..3afd8b0 100644
--- a/Wrappers/Python/ccpi/optimisation/algorithms/PDHG.py
+++ b/Wrappers/Python/ccpi/optimisation/algorithms/PDHG.py
@@ -18,93 +18,84 @@ class PDHG(Algorithm):
'''Primal Dual Hybrid Gradient'''
def __init__(self, **kwargs):
- super(PDHG, self).__init__()
+ super(PDHG, self).__init__(max_iteration=kwargs.get('max_iteration',0))
self.f = kwargs.get('f', None)
self.operator = kwargs.get('operator', None)
self.g = kwargs.get('g', None)
self.tau = kwargs.get('tau', None)
- self.sigma = kwargs.get('sigma', None)
- self.memopt = kwargs.get('memopt', False)
-
+ self.sigma = kwargs.get('sigma', 1.)
+
+
if self.f is not None and self.operator is not None and \
self.g is not None:
+ if self.tau is None:
+ # Compute operator Norm
+ normK = self.operator.norm()
+ # Primal & dual stepsizes
+ self.tau = 1/(self.sigma*normK**2)
print ("Calling from creator")
self.set_up(self.f,
+ self.g,
self.operator,
- self.g,
self.tau,
self.sigma)
def set_up(self, f, g, operator, tau = None, sigma = None, opt = None, **kwargs):
# algorithmic parameters
-
+ self.operator = operator
+ self.f = f
+ self.g = g
+ self.tau = tau
+ self.sigma = sigma
+ self.opt = opt
if sigma is None and tau is None:
raise ValueError('Need sigma*tau||K||^2<1')
-
-
+
self.x_old = self.operator.domain_geometry().allocate()
- self.y_old = self.operator.range_geometry().allocate()
-
- self.xbar = self.x_old.copy()
-
+ self.x_tmp = self.x_old.copy()
self.x = self.x_old.copy()
+
+ self.y_old = self.operator.range_geometry().allocate()
+ self.y_tmp = self.y_old.copy()
self.y = self.y_old.copy()
- if self.memopt:
- self.y_tmp = self.y_old.copy()
- self.x_tmp = self.x_old.copy()
- #y = y_tmp
-
+
+ self.xbar = self.x_old.copy()
+
# relaxation parameter
self.theta = 1
+ self.update_objective()
+ self.configured = True
def update(self):
- if self.memopt:
- # Gradient descent, Dual problem solution
- # self.y_old += self.sigma * self.operator.direct(self.xbar)
- self.operator.direct(self.xbar, out=self.y_tmp)
- self.y_tmp *= self.sigma
- self.y_old += self.y_tmp
-
- #self.y = self.f.proximal_conjugate(self.y_old, self.sigma)
- self.f.proximal_conjugate(self.y_old, self.sigma, out=self.y)
-
- # Gradient ascent, Primal problem solution
- self.operator.adjoint(self.y, out=self.x_tmp)
- self.x_tmp *= self.tau
- self.x_old -= self.x_tmp
-
- self.g.proximal(self.x_old, self.tau, out=self.x)
-
- #Update
- self.x.subtract(self.x_old, out=self.xbar)
- self.xbar *= self.theta
- self.xbar += self.x
+
+ # Gradient descent, Dual problem solution
+ self.operator.direct(self.xbar, out=self.y_tmp)
+ self.y_tmp *= self.sigma
+ self.y_tmp += self.y_old
- self.x_old.fill(self.x)
- self.y_old.fill(self.y)
+ #self.y = self.f.proximal_conjugate(self.y_old, self.sigma)
+ self.f.proximal_conjugate(self.y_tmp, self.sigma, out=self.y)
+
+ # Gradient ascent, Primal problem solution
+ self.operator.adjoint(self.y, out=self.x_tmp)
+ self.x_tmp *= -1*self.tau
+ self.x_tmp += self.x_old
- else:
- # Gradient descent, Dual problem solution
- self.y_old += self.sigma * self.operator.direct(self.xbar)
- self.y = self.f.proximal_conjugate(self.y_old, self.sigma)
- # Gradient ascent, Primal problem solution
- self.x_old -= self.tau * self.operator.adjoint(self.y)
- self.x = self.g.proximal(self.x_old, self.tau)
+ self.g.proximal(self.x_tmp, self.tau, out=self.x)
- #Update
- #xbar = x + theta * (x - x_old)
- self.xbar.fill(self.x)
- self.xbar -= self.x_old
- self.xbar *= self.theta
- self.xbar += self.x
+ #Update
+ self.x.subtract(self.x_old, out=self.xbar)
+ self.xbar *= self.theta
+ self.xbar += self.x
- self.x_old = self.x
- self.y_old = self.y
+ self.x_old.fill(self.x)
+ self.y_old.fill(self.y)
def update_objective(self):
+
p1 = self.f(self.operator.direct(self.x)) + self.g(self.x)
- d1 = -(self.f.convex_conjugate(self.y) + self.g(-1*self.operator.adjoint(self.y)))
+ d1 = -(self.f.convex_conjugate(self.y) + self.g.convex_conjugate(-1*self.operator.adjoint(self.y)))
self.loss.append([p1,d1,p1-d1])
@@ -148,63 +139,44 @@ def PDHG_old(f, g, operator, tau = None, sigma = None, opt = None, **kwargs):
for i in range(niter):
+
- if not memopt:
-
- y_old += sigma * operator.direct(xbar)
- y = f.proximal_conjugate(y_old, sigma)
-
- x_old -= tau*operator.adjoint(y)
- x = g.proximal(x_old, tau)
-
- x.subtract(x_old, out=xbar)
- xbar *= theta
- xbar += x
-
- x_old.fill(x)
- y_old.fill(y)
-
-
-# if i%100==0:
-#
-# p1 = f(operator.direct(x)) + g(x)
-# d1 = - ( f.convex_conjugate(y) + g(-1*operator.adjoint(y)) )
-# primal.append(p1)
-# dual.append(d1)
-# pdgap.append(p1-d1)
-# print(p1, d1, p1-d1)
-
-
- else:
-
+ if memopt:
operator.direct(xbar, out = y_tmp)
y_tmp *= sigma
- y_tmp += y_old
- f.proximal_conjugate(y_tmp, sigma, out=y)
-
+ y_tmp += y_old
+ else:
+ y_tmp = y_old + sigma * operator.direct(xbar)
+
+ f.proximal_conjugate(y_tmp, sigma, out=y)
+
+ if memopt:
operator.adjoint(y, out = x_tmp)
- x_tmp *= -tau
+ x_tmp *= -1*tau
x_tmp += x_old
-
- g.proximal(x_tmp, tau, out = x)
+ else:
+ x_tmp = x_old - tau*operator.adjoint(y)
- x.subtract(x_old, out=xbar)
- xbar *= theta
- xbar += x
-
- x_old.fill(x)
- y_old.fill(y)
+ g.proximal(x_tmp, tau, out=x)
+
+ x.subtract(x_old, out=xbar)
+ xbar *= theta
+ xbar += x
+
+ x_old.fill(x)
+ y_old.fill(y)
+
+ if i%10==0:
-# if i%100==0:
-#
-# p1 = f(operator.direct(x)) + g(x)
-# d1 = - ( f.convex_conjugate(y) + g(-1*operator.adjoint(y)) )
-# primal.append(p1)
-# dual.append(d1)
-# pdgap.append(p1-d1)
-# print(p1, d1, p1-d1)
+ p1 = f(operator.direct(x)) + g(x)
+ d1 = - ( f.convex_conjugate(y) + g.convex_conjugate(-1*operator.adjoint(y)) )
+ primal.append(p1)
+ dual.append(d1)
+ pdgap.append(p1-d1)
+ print(p1, d1, p1-d1)
+
t_end = time.time()
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/SIRT.py b/Wrappers/Python/ccpi/optimisation/algorithms/SIRT.py
index 30584d4..c73d323 100644
--- a/Wrappers/Python/ccpi/optimisation/algorithms/SIRT.py
+++ b/Wrappers/Python/ccpi/optimisation/algorithms/SIRT.py
@@ -59,6 +59,7 @@ class SIRT(Algorithm):
# Set up scaling matrices D and M.
self.M = 1/self.operator.direct(self.operator.domain_geometry().allocate(value=1.0))
self.D = 1/self.operator.adjoint(self.operator.range_geometry().allocate(value=1.0))
+ self.configured = True
def update(self):
@@ -67,8 +68,9 @@ class SIRT(Algorithm):
self.x += self.relax_par * (self.D*self.operator.adjoint(self.M*self.r))
- if self.constraint != None:
- self.x = self.constraint.prox(self.x,None)
+ if self.constraint is not None:
+ self.x = self.constraint.proximal(self.x,None)
+ # self.constraint.proximal(self.x,None, out=self.x)
def update_objective(self):
- self.loss.append(self.r.squared_norm()) \ No newline at end of file
+ self.loss.append(self.r.squared_norm())
diff --git a/Wrappers/Python/ccpi/optimisation/algorithms/__init__.py b/Wrappers/Python/ccpi/optimisation/algorithms/__init__.py
index 2dbacfc..8f255f3 100644
--- a/Wrappers/Python/ccpi/optimisation/algorithms/__init__.py
+++ b/Wrappers/Python/ccpi/optimisation/algorithms/__init__.py
@@ -27,7 +27,5 @@ from .CGLS import CGLS
from .SIRT import SIRT
from .GradientDescent import GradientDescent
from .FISTA import FISTA
-from .FBPD import FBPD
from .PDHG import PDHG
-from .PDHG import PDHG_old
diff --git a/Wrappers/Python/ccpi/optimisation/algs.py b/Wrappers/Python/ccpi/optimisation/algs.py
deleted file mode 100755
index f5ba85e..0000000
--- a/Wrappers/Python/ccpi/optimisation/algs.py
+++ /dev/null
@@ -1,307 +0,0 @@
-# -*- coding: utf-8 -*-
-# This work is part of the Core Imaging Library developed by
-# Visual Analytics and Imaging System Group of the Science Technology
-# Facilities Council, STFC
-
-# Copyright 2018 Jakob Jorgensen, Daniil Kazantsev and Edoardo Pasca
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import numpy
-import time
-
-
-
-
-def FISTA(x_init, f=None, g=None, opt=None):
- '''Fast Iterative Shrinkage-Thresholding Algorithm
-
- Beck, A. and Teboulle, M., 2009. A fast iterative shrinkage-thresholding
- algorithm for linear inverse problems.
- SIAM journal on imaging sciences,2(1), pp.183-202.
-
- Parameters:
- x_init: initial guess
- f: data fidelity
- g: regularizer
- h:
- opt: additional algorithm
- '''
- # default inputs
- if f is None: f = ZeroFun()
- if g is None: g = ZeroFun()
-
- # algorithmic parameters
- if opt is None:
- opt = {'tol': 1e-4, 'iter': 1000, 'memopt':False}
-
- max_iter = opt['iter'] if 'iter' in opt.keys() else 1000
- tol = opt['tol'] if 'tol' in opt.keys() else 1e-4
- memopt = opt['memopt'] if 'memopt' in opt.keys() else False
-
-
- # initialization
- if memopt:
- y = x_init.clone()
- x_old = x_init.clone()
- x = x_init.clone()
- u = x_init.clone()
- else:
- x_old = x_init
- y = x_init;
-
- timing = numpy.zeros(max_iter)
- criter = numpy.zeros(max_iter)
-
- invL = 1/f.L
-
- t_old = 1
-
-# c = f(x_init) + g(x_init)
-
- # algorithm loop
- for it in range(0, max_iter):
-
- time0 = time.time()
- if memopt:
- # u = y - invL*f.grad(y)
- # store the result in x_old
- f.gradient(y, out=u)
- u.__imul__( -invL )
- u.__iadd__( y )
- # x = g.prox(u,invL)
- g.proximal(u, invL, out=x)
-
- t = 0.5*(1 + numpy.sqrt(1 + 4*(t_old**2)))
-
- # y = x + (t_old-1)/t*(x-x_old)
- x.subtract(x_old, out=y)
- y.__imul__ ((t_old-1)/t)
- y.__iadd__( x )
-
- x_old.fill(x)
- t_old = t
-
-
- else:
- u = y - invL*f.gradient(y)
-
- x = g.proximal(u,invL)
-
- t = 0.5*(1 + numpy.sqrt(1 + 4*(t_old**2)))
-
- y = x + (t_old-1)/t*(x-x_old)
-
- x_old = x.copy()
- t_old = t
-
- # time and criterion
-# timing[it] = time.time() - time0
-# criter[it] = f(x) + g(x);
-
- # stopping rule
- #if np.linalg.norm(x - x_old) < tol * np.linalg.norm(x_old) and it > 10:
- # break
-
- #print(it, 'out of', 10, 'iterations', end='\r');
-
- #criter = criter[0:it+1];
-# timing = numpy.cumsum(timing[0:it+1]);
-
- return x #, it, timing, criter
-
-def FBPD(x_init, operator=None, constraint=None, data_fidelity=None,\
- regulariser=None, opt=None):
- '''FBPD Algorithm
-
- Parameters:
- x_init: initial guess
- f: constraint
- g: data fidelity
- h: regularizer
- opt: additional algorithm
- '''
- # default inputs
- if constraint is None: constraint = ZeroFun()
- if data_fidelity is None: data_fidelity = ZeroFun()
- if regulariser is None: regulariser = ZeroFun()
-
- # algorithmic parameters
- if opt is None:
- opt = {'tol': 1e-4, 'iter': 1000}
- else:
- try:
- max_iter = opt['iter']
- except KeyError as ke:
- opt[ke] = 1000
- try:
- opt['tol'] = 1000
- except KeyError as ke:
- opt[ke] = 1e-4
- tol = opt['tol']
- max_iter = opt['iter']
- memopt = opt['memopts'] if 'memopts' in opt.keys() else False
-
- # step-sizes
- tau = 2 / (data_fidelity.L + 2)
- sigma = (1/tau - data_fidelity.L/2) / regulariser.L
- inv_sigma = 1/sigma
-
- # initialization
- x = x_init
- y = operator.direct(x);
-
- timing = numpy.zeros(max_iter)
- criter = numpy.zeros(max_iter)
-
-
-
-
- # algorithm loop
- for it in range(0, max_iter):
-
- t = time.time()
-
- # primal forward-backward step
- x_old = x;
- x = x - tau * ( data_fidelity.grad(x) + operator.adjoint(y) );
- x = constraint.prox(x, tau);
-
- # dual forward-backward step
- y = y + sigma * operator.direct(2*x - x_old);
- y = y - sigma * regulariser.prox(inv_sigma*y, inv_sigma);
-
- # time and criterion
- timing[it] = time.time() - t
- criter[it] = constraint(x) + data_fidelity(x) + regulariser(operator.direct(x))
-
- # stopping rule
- #if np.linalg.norm(x - x_old) < tol * np.linalg.norm(x_old) and it > 10:
- # break
-
- criter = criter[0:it+1]
- timing = numpy.cumsum(timing[0:it+1])
-
- return x, it, timing, criter
-
-def CGLS(x_init, operator , data , opt=None):
- '''Conjugate Gradient Least Squares algorithm
-
- Parameters:
- x_init: initial guess
- operator: operator for forward/backward projections
- data: data to operate on
- opt: additional algorithm
- '''
-
- if opt is None:
- opt = {'tol': 1e-4, 'iter': 1000}
- else:
- try:
- max_iter = opt['iter']
- except KeyError as ke:
- opt[ke] = 1000
- try:
- opt['tol'] = 1000
- except KeyError as ke:
- opt[ke] = 1e-4
- tol = opt['tol']
- max_iter = opt['iter']
-
- r = data.copy()
- x = x_init.copy()
-
- d = operator.adjoint(r)
-
- normr2 = (d**2).sum()
-
- timing = numpy.zeros(max_iter)
- criter = numpy.zeros(max_iter)
-
- # algorithm loop
- for it in range(0, max_iter):
-
- t = time.time()
-
- Ad = operator.direct(d)
- alpha = normr2/( (Ad**2).sum() )
- x = x + alpha*d
- r = r - alpha*Ad
- s = operator.adjoint(r)
-
- normr2_new = (s**2).sum()
- beta = normr2_new/normr2
- normr2 = normr2_new
- d = s + beta*d
-
- # time and criterion
- timing[it] = time.time() - t
- criter[it] = (r**2).sum()
-
- return x, it, timing, criter
-
-def SIRT(x_init, operator , data , opt=None, constraint=None):
- '''Simultaneous Iterative Reconstruction Technique
-
- Parameters:
- x_init: initial guess
- operator: operator for forward/backward projections
- data: data to operate on
- opt: additional algorithm
- constraint: func of Indicator type specifying convex constraint.
- '''
-
- if opt is None:
- opt = {'tol': 1e-4, 'iter': 1000}
- else:
- try:
- max_iter = opt['iter']
- except KeyError as ke:
- opt[ke] = 1000
- try:
- opt['tol'] = 1000
- except KeyError as ke:
- opt[ke] = 1e-4
- tol = opt['tol']
- max_iter = opt['iter']
-
- x = x_init.clone()
-
- timing = numpy.zeros(max_iter)
- criter = numpy.zeros(max_iter)
-
- # Relaxation parameter must be strictly between 0 and 2. For now fix at 1.0
- relax_par = 1.0
-
- # Set up scaling matrices D and M.
- M = 1/operator.direct(operator.domain_geometry().allocate(value=1.0))
- D = 1/operator.adjoint(operator.range_geometry().allocate(value=1.0))
-
- # algorithm loop
- for it in range(0, max_iter):
- t = time.time()
- r = data - operator.direct(x)
-
- x = x + relax_par * (D*operator.adjoint(M*r))
-
- if constraint != None:
- x = constraint.prox(x,None)
-
- timing[it] = time.time() - t
- if it > 0:
- criter[it-1] = (r**2).sum()
-
- r = data - operator.direct(x)
- criter[it] = (r**2).sum()
- return x, it, timing, criter
-
diff --git a/Wrappers/Python/ccpi/optimisation/funcs.py b/Wrappers/Python/ccpi/optimisation/funcs.py
deleted file mode 100755
index b2b9791..0000000
--- a/Wrappers/Python/ccpi/optimisation/funcs.py
+++ /dev/null
@@ -1,265 +0,0 @@
-# -*- coding: utf-8 -*-
-# This work is part of the Core Imaging Library developed by
-# Visual Analytics and Imaging System Group of the Science Technology
-# Facilities Council, STFC
-
-# Copyright 2018 Jakob Jorgensen, Daniil Kazantsev and Edoardo Pasca
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import numpy
-from ccpi.framework import DataContainer
-import warnings
-from ccpi.optimisation.functions import Function
-def isSizeCorrect(data1 ,data2):
- if issubclass(type(data1), DataContainer) and \
- issubclass(type(data2), DataContainer):
- # check dimensionality
- if data1.check_dimensions(data2):
- return True
- elif issubclass(type(data1) , numpy.ndarray) and \
- issubclass(type(data2) , numpy.ndarray):
- return data1.shape == data2.shape
- else:
- raise ValueError("{0}: getting two incompatible types: {1} {2}"\
- .format('Function', type(data1), type(data2)))
- return False
-class Norm2(Function):
-
- def __init__(self,
- gamma=1.0,
- direction=None):
- super(Norm2, self).__init__()
- self.gamma = gamma;
- self.direction = direction;
-
- def __call__(self, x, out=None):
-
- if out is None:
- xx = numpy.sqrt(numpy.sum(numpy.square(x.as_array()), self.direction,
- keepdims=True))
- else:
- if isSizeCorrect(out, x):
- # check dimensionality
- if issubclass(type(out), DataContainer):
- arr = out.as_array()
- numpy.square(x.as_array(), out=arr)
- xx = numpy.sqrt(numpy.sum(arr, self.direction, keepdims=True))
-
- elif issubclass(type(out) , numpy.ndarray):
- numpy.square(x.as_array(), out=out)
- xx = numpy.sqrt(numpy.sum(out, self.direction, keepdims=True))
- else:
- raise ValueError ('Wrong size: x{0} out{1}'.format(x.shape,out.shape) )
-
- p = numpy.sum(self.gamma*xx)
-
- return p
-
- def prox(self, x, tau):
-
- xx = numpy.sqrt(numpy.sum( numpy.square(x.as_array()), self.direction,
- keepdims=True ))
- xx = numpy.maximum(0, 1 - tau*self.gamma / xx)
- p = x.as_array() * xx
-
- return type(x)(p,geometry=x.geometry)
- def proximal(self, x, tau, out=None):
- if out is None:
- return self.prox(x,tau)
- else:
- if isSizeCorrect(out, x):
- # check dimensionality
- if issubclass(type(out), DataContainer):
- numpy.square(x.as_array(), out = out.as_array())
- xx = numpy.sqrt(numpy.sum( out.as_array() , self.direction,
- keepdims=True ))
- xx = numpy.maximum(0, 1 - tau*self.gamma / xx)
- x.multiply(xx, out= out.as_array())
-
-
- elif issubclass(type(out) , numpy.ndarray):
- numpy.square(x.as_array(), out=out)
- xx = numpy.sqrt(numpy.sum(out, self.direction, keepdims=True))
-
- xx = numpy.maximum(0, 1 - tau*self.gamma / xx)
- x.multiply(xx, out= out)
- else:
- raise ValueError ('Wrong size: x{0} out{1}'.format(x.shape,out.shape) )
-
-
-
-
-# Define a class for squared 2-norm
-class Norm2sq(Function):
- '''
- f(x) = c*||A*x-b||_2^2
-
- which has
-
- grad[f](x) = 2*c*A^T*(A*x-b)
-
- and Lipschitz constant
-
- L = 2*c*||A||_2^2 = 2*s1(A)^2
-
- where s1(A) is the largest singular value of A.
-
- '''
-
- def __init__(self,A,b,c=1.0,memopt=False):
- super(Norm2sq, self).__init__()
-
- self.A = A # Should be an operator, default identity
- self.b = b # Default zero DataSet?
- self.c = c # Default 1.
- if memopt:
- try:
- self.range_tmp = A.range_geometry().allocate()
- self.domain_tmp = A.domain_geometry().allocate()
- self.memopt = True
- except NameError as ne:
- warnings.warn(str(ne))
- self.memopt = False
- except NotImplementedError as nie:
- print (nie)
- warnings.warn(str(nie))
- self.memopt = False
- else:
- self.memopt = False
-
- # Compute the Lipschitz parameter from the operator if possible
- # Leave it initialised to None otherwise
- try:
- self.L = 2.0*self.c*(self.A.norm()**2)
- except AttributeError as ae:
- pass
- except NotImplementedError as noe:
- pass
-
- #def grad(self,x):
- # return self.gradient(x, out=None)
-
- def __call__(self,x):
- #return self.c* np.sum(np.square((self.A.direct(x) - self.b).ravel()))
- #if out is None:
- # return self.c*( ( (self.A.direct(x)-self.b)**2).sum() )
- #else:
- y = self.A.direct(x)
- y.__isub__(self.b)
- #y.__imul__(y)
- #return y.sum() * self.c
- try:
- return y.squared_norm() * self.c
- except AttributeError as ae:
- # added for compatibility with SIRF
- return (y.norm()**2) * self.c
-
- def gradient(self, x, out = None):
- if self.memopt:
- #return 2.0*self.c*self.A.adjoint( self.A.direct(x) - self.b )
-
- self.A.direct(x, out=self.range_tmp)
- self.range_tmp -= self.b
- self.A.adjoint(self.range_tmp, out=out)
- #self.direct_placehold.multiply(2.0*self.c, out=out)
- out *= (self.c * 2.0)
- else:
- return (2.0*self.c)*self.A.adjoint( self.A.direct(x) - self.b )
-
-
-
-# Box constraints indicator function. Calling returns 0 if argument is within
-# the box. The prox operator is projection onto the box. Only implements one
-# scalar lower and one upper as constraint on all elements. Should generalise
-# to vectors to allow different constraints one elements.
-class IndicatorBox(Function):
-
- def __init__(self,lower=-numpy.inf,upper=numpy.inf):
- # Do nothing
- super(IndicatorBox, self).__init__()
- self.lower = lower
- self.upper = upper
-
-
- def __call__(self,x):
-
- if (numpy.all(x.array>=self.lower) and
- numpy.all(x.array <= self.upper) ):
- val = 0
- else:
- val = numpy.inf
- return val
-
- def prox(self,x,tau=None):
- return (x.maximum(self.lower)).minimum(self.upper)
-
- def proximal(self, x, tau, out=None):
- if out is None:
- return self.prox(x, tau)
- else:
- if not x.shape == out.shape:
- raise ValueError('Norm1 Incompatible size:',
- x.shape, out.shape)
- #(x.abs() - tau*self.gamma).maximum(0) * x.sign()
- x.abs(out = out)
- out.__isub__(tau*self.gamma)
- out.maximum(0, out=out)
- if self.sign_x is None or not x.shape == self.sign_x.shape:
- self.sign_x = x.sign()
- else:
- x.sign(out=self.sign_x)
-
- out.__imul__( self.sign_x )
-
-# A more interesting example, least squares plus 1-norm minimization.
-# Define class to represent 1-norm including prox function
-class Norm1(Function):
-
- def __init__(self,gamma):
- super(Norm1, self).__init__()
- self.gamma = gamma
- self.L = 1
- self.sign_x = None
-
- def __call__(self,x,out=None):
- if out is None:
- return self.gamma*(x.abs().sum())
- else:
- if not x.shape == out.shape:
- raise ValueError('Norm1 Incompatible size:',
- x.shape, out.shape)
- x.abs(out=out)
- return out.sum() * self.gamma
-
- def prox(self,x,tau):
- return (x.abs() - tau*self.gamma).maximum(0) * x.sign()
-
- def proximal(self, x, tau, out=None):
- if out is None:
- return self.prox(x, tau)
- else:
- if isSizeCorrect(x,out):
- # check dimensionality
- if issubclass(type(out), DataContainer):
- v = (x.abs() - tau*self.gamma).maximum(0)
- x.sign(out=out)
- out *= v
- #out.fill(self.prox(x,tau))
- elif issubclass(type(out) , numpy.ndarray):
- v = (x.abs() - tau*self.gamma).maximum(0)
- out[:] = x.sign()
- out *= v
- #out[:] = self.prox(x,tau)
- else:
- raise ValueError ('Wrong size: x{0} out{1}'.format(x.shape,out.shape) )
diff --git a/Wrappers/Python/ccpi/optimisation/functions/BlockFunction.py b/Wrappers/Python/ccpi/optimisation/functions/BlockFunction.py
index bf627a5..3765685 100644
--- a/Wrappers/Python/ccpi/optimisation/functions/BlockFunction.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/BlockFunction.py
@@ -20,11 +20,13 @@ class BlockFunction(Function):
'''
def __init__(self, *functions):
-
+
+ super(BlockFunction, self).__init__()
self.functions = functions
self.length = len(self.functions)
- super(BlockFunction, self).__init__()
+
+
def __call__(self, x):
@@ -93,16 +95,28 @@ class BlockFunction(Function):
'''
+
+ if out is None:
- out = [None]*self.length
- if isinstance(tau, Number):
- for i in range(self.length):
- out[i] = self.functions[i].proximal(x.get_item(i), tau)
+ out = [None]*self.length
+ if isinstance(tau, Number):
+ for i in range(self.length):
+ out[i] = self.functions[i].proximal(x.get_item(i), tau)
+ else:
+ for i in range(self.length):
+ out[i] = self.functions[i].proximal(x.get_item(i), tau.get_item(i))
+
+ return BlockDataContainer(*out)
+
else:
- for i in range(self.length):
- out[i] = self.functions[i].proximal(x.get_item(i), tau.get_item(i))
-
- return BlockDataContainer(*out)
+ if isinstance(tau, Number):
+ for i in range(self.length):
+ self.functions[i].proximal(x.get_item(i), tau, out[i])
+ else:
+ for i in range(self.length):
+ self.functions[i].proximal(x.get_item(i), tau.get_item(i), out[i])
+
+
def gradient(self,x, out=None):
diff --git a/Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition.py b/Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition.py
index 70511bb..a2445cd 100644
--- a/Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition.py
@@ -19,16 +19,13 @@ class FunctionOperatorComposition(Function):
'''
- def __init__(self, operator, function):
+ def __init__(self, function, operator):
super(FunctionOperatorComposition, self).__init__()
+
self.function = function
self.operator = operator
- alpha = 1
-
- if isinstance (function, ScaledFunction):
- alpha = function.scalar
- self.L = 2 * alpha * operator.norm()**2
+ self.L = function.L * operator.norm()**2
def __call__(self, x):
@@ -39,47 +36,75 @@ class FunctionOperatorComposition(Function):
'''
- return self.function(self.operator.direct(x))
+ return self.function(self.operator.direct(x))
+
+ def gradient(self, x, out=None):
+#
+ ''' Gradient takes into account the Operator'''
+ if out is None:
+ return self.operator.adjoint(self.function.gradient(self.operator.direct(x)))
+ else:
+ tmp = self.operator.range_geometry().allocate()
+ self.operator.direct(x, out=tmp)
+ self.function.gradient(tmp, out=tmp)
+ self.operator.adjoint(tmp, out=out)
- #TODO do not know if we need it
- def call_adjoint(self, x):
- return self.function(self.operator.adjoint(x))
+
+if __name__ == '__main__':
- def convex_conjugate(self, x):
+ from ccpi.framework import ImageGeometry, AcquisitionGeometry
+ from ccpi.optimisation.operators import Gradient
+ from ccpi.optimisation.functions import L2NormSquared
+ from ccpi.astra.ops import AstraProjectorSimple
+ import numpy as np
- ''' convex_conjugate does not take into account the Operator'''
- return self.function.convex_conjugate(x)
+ M, N= 50, 50
+ ig = ImageGeometry(voxel_num_x=M, voxel_num_y = N)
+
+ detectors = N
+ angles_num = N
+ det_w = 1.0
+
+ angles = np.linspace(0, np.pi, angles_num, endpoint=False)
+ ag = AcquisitionGeometry('parallel',
+ '2D',
+ angles,
+ detectors,det_w)
+
+
+ Aop = AstraProjectorSimple(ig, ag, 'cpu')
- def proximal(self, x, tau, out=None):
+ u = ig.allocate('random_int', seed=15)
+ u1 = ig.allocate('random_int', seed=10)
+ b = Aop.direct(u1)
+
- '''proximal does not take into account the Operator'''
- if out is None:
- return self.function.proximal(x, tau)
- else:
- self.function.proximal(x, tau, out=out)
-
-
- def proximal_conjugate(self, x, tau, out=None):
+# G = Gradient(ig)
+ alpha = 0.5
+
+ f1 = alpha * L2NormSquared(b=b)
- ''' proximal conjugate does not take into account the Operator'''
- if out is None:
- return self.function.proximal_conjugate(x, tau)
- else:
- self.function.proximal_conjugate(x, tau, out=out)
+ f_comp = FunctionOperatorComposition(f1, Aop)
+
+ print(f_comp(u))
+
+
+ z1 = Aop.direct(u)
+ tmp = 0.5 * ((z1 - b)**2).sum()
+
+
+ print(tmp)
+
+
+
+
+
+
+
+
- def gradient(self, x, out=None):
-
- ''' Gradient takes into account the Operator'''
- if out is None:
- return self.operator.adjoint(
- self.function.gradient(self.operator.direct(x))
- )
- else:
- self.operator.adjoint(
- self.function.gradient(self.operator.direct(x),
- out=out)
- )
+
\ No newline at end of file
diff --git a/Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition_old.py b/Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition_old.py
new file mode 100644
index 0000000..70511bb
--- /dev/null
+++ b/Wrappers/Python/ccpi/optimisation/functions/FunctionOperatorComposition_old.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Mar 8 09:55:36 2019
+
+@author: evangelos
+"""
+
+from ccpi.optimisation.functions import Function
+from ccpi.optimisation.functions import ScaledFunction
+
+
+class FunctionOperatorComposition(Function):
+
+ ''' Function composition with Operator, i.e., f(Ax)
+
+ A: operator
+ f: function
+
+ '''
+
+ def __init__(self, operator, function):
+
+ super(FunctionOperatorComposition, self).__init__()
+ self.function = function
+ self.operator = operator
+ alpha = 1
+
+ if isinstance (function, ScaledFunction):
+ alpha = function.scalar
+ self.L = 2 * alpha * operator.norm()**2
+
+
+ def __call__(self, x):
+
+ ''' Evaluate FunctionOperatorComposition at x
+
+ returns f(Ax)
+
+ '''
+
+ return self.function(self.operator.direct(x))
+
+ #TODO do not know if we need it
+ def call_adjoint(self, x):
+
+ return self.function(self.operator.adjoint(x))
+
+
+ def convex_conjugate(self, x):
+
+ ''' convex_conjugate does not take into account the Operator'''
+ return self.function.convex_conjugate(x)
+
+ def proximal(self, x, tau, out=None):
+
+ '''proximal does not take into account the Operator'''
+ if out is None:
+ return self.function.proximal(x, tau)
+ else:
+ self.function.proximal(x, tau, out=out)
+
+
+ def proximal_conjugate(self, x, tau, out=None):
+
+ ''' proximal conjugate does not take into account the Operator'''
+ if out is None:
+ return self.function.proximal_conjugate(x, tau)
+ else:
+ self.function.proximal_conjugate(x, tau, out=out)
+
+ def gradient(self, x, out=None):
+
+ ''' Gradient takes into account the Operator'''
+ if out is None:
+ return self.operator.adjoint(
+ self.function.gradient(self.operator.direct(x))
+ )
+ else:
+ self.operator.adjoint(
+ self.function.gradient(self.operator.direct(x),
+ out=out)
+ )
+
+ \ No newline at end of file
diff --git a/Wrappers/Python/ccpi/optimisation/functions/IndicatorBox.py b/Wrappers/Python/ccpi/optimisation/functions/IndicatorBox.py
index df8dc89..7fec65e 100755
--- a/Wrappers/Python/ccpi/optimisation/functions/IndicatorBox.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/IndicatorBox.py
@@ -1,23 +1,28 @@
-# -*- coding: utf-8 -*-
-# This work is part of the Core Imaging Library developed by
-# Visual Analytics and Imaging System Group of the Science Technology
-# Facilities Council, STFC
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
-# Copyright 2018-2019 Jakob Jorgensen, Daniil Kazantsev and Edoardo Pasca
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
from ccpi.optimisation.functions import Function
import numpy
+from ccpi.framework import ImageData
class IndicatorBox(Function):
'''Box constraints indicator function.
@@ -35,7 +40,7 @@ class IndicatorBox(Function):
def __call__(self,x):
-
+
if (numpy.all(x.array>=self.lower) and
numpy.all(x.array <= self.upper) ):
val = 0
@@ -43,23 +48,87 @@ class IndicatorBox(Function):
val = numpy.inf
return val
- def prox(self,x,tau=None):
- return (x.maximum(self.lower)).minimum(self.upper)
+ def gradient(self,x):
+ return ValueError('Not Differentiable')
+ def convex_conjugate(self,x):
+ # support function sup <x, z>, z \in [lower, upper]
+ # ????
+ return x.maximum(0).sum()
+
def proximal(self, x, tau, out=None):
+
if out is None:
- return self.prox(x, tau)
+ return (x.maximum(self.lower)).minimum(self.upper)
+ else:
+ x.maximum(self.lower, out=out)
+ out.minimum(self.upper, out=out)
+
+ def proximal_conjugate(self, x, tau, out=None):
+
+ if out is None:
+
+ return x - tau * self.proximal(x/tau, tau)
+
else:
- if not x.shape == out.shape:
- raise ValueError('Norm1 Incompatible size:',
- x.shape, out.shape)
- #(x.abs() - tau*self.gamma).maximum(0) * x.sign()
- x.abs(out = out)
- out.__isub__(tau*self.gamma)
- out.maximum(0, out=out)
- if self.sign_x is None or not x.shape == self.sign_x.shape:
- self.sign_x = x.sign()
- else:
- x.sign(out=self.sign_x)
-
- out.__imul__( self.sign_x )
+
+ self.proximal(x/tau, tau, out=out)
+ out *= -1*tau
+ out += x
+
+
+
+if __name__ == '__main__':
+
+ from ccpi.framework import ImageGeometry, BlockDataContainer
+
+ N, M = 2,3
+ ig = ImageGeometry(voxel_num_x = N, voxel_num_y = M)
+
+ u = ig.allocate('random_int')
+ tau = 2
+
+ f = IndicatorBox(2, 3)
+
+ lower = 10
+ upper = 30
+
+ z1 = f.proximal(u, tau)
+
+ z2 = f.proximal_conjugate(u/tau, 1/tau)
+
+ z = z1 + tau * z2
+
+ numpy.testing.assert_array_equal(z.as_array(), u.as_array())
+
+ out1 = ig.allocate()
+ out2 = ig.allocate()
+
+ f.proximal(u, tau, out=out1)
+ f.proximal_conjugate(u/tau, 1/tau, out = out2)
+
+ p = out1 + tau * out2
+
+ numpy.testing.assert_array_equal(p.as_array(), u.as_array())
+
+ d = f.convex_conjugate(u)
+ print(d)
+
+
+
+ # what about n-dimensional Block
+ #uB = BlockDataContainer(u,u,u)
+ #lowerB = BlockDataContainer(1,2,3)
+ #upperB = BlockDataContainer(10,21,30)
+
+ #fB = IndicatorBox(lowerB, upperB)
+
+ #z1B = fB.proximal(uB, tau)
+
+
+
+
+
+
+
+ \ No newline at end of file
diff --git a/Wrappers/Python/ccpi/optimisation/functions/KullbackLeibler.py b/Wrappers/Python/ccpi/optimisation/functions/KullbackLeibler.py
index d4370a8..6920829 100644
--- a/Wrappers/Python/ccpi/optimisation/functions/KullbackLeibler.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/KullbackLeibler.py
@@ -20,40 +20,42 @@
import numpy
from ccpi.optimisation.functions import Function
from ccpi.optimisation.functions.ScaledFunction import ScaledFunction
-from ccpi.framework import ImageData, ImageGeometry
import functools
+import scipy.special
class KullbackLeibler(Function):
- ''' Assume that data > 0
+ '''
+
+ KL_div(x, y + back) = int x * log(x/(y+back)) - x + (y+back)
+
+ Assumption: y>=0
+ back>=0
'''
- def __init__(self,data, **kwargs):
+ def __init__(self, data, **kwargs):
super(KullbackLeibler, self).__init__()
- self.b = data
- self.bnoise = kwargs.get('bnoise', 0)
-
-
+ self.b = data
+ self.bnoise = 0
+
+
def __call__(self, x):
- # TODO check
- self.sum_value = x + self.bnoise
- if (self.sum_value.as_array()<0).any():
- self.sum_value = numpy.inf
+ '''
- if self.sum_value==numpy.inf:
- return numpy.inf
- else:
- tmp = self.sum_value.copy()
- #tmp.fill( numpy.log(tmp.as_array()) )
- self.log(tmp)
- return (x - self.b * tmp ).sum()
-
-# return numpy.sum( x.as_array() - self.b.as_array() * numpy.log(self.sum_value.as_array()))
+ x - y * log( x + bnoise) + y * log(y) - y + bnoise
+
+
+ '''
+
+ ind = x.as_array()>0
+ tmp = scipy.special.kl_div(self.b.as_array()[ind], x.as_array()[ind])
+ return numpy.sum(tmp)
+
def log(self, datacontainer):
'''calculates the in-place log of the datacontainer'''
@@ -61,13 +63,14 @@ class KullbackLeibler(Function):
datacontainer.as_array().ravel(), True):
raise ValueError('KullbackLeibler. Cannot calculate log of negative number')
datacontainer.fill( numpy.log(datacontainer.as_array()) )
+
def gradient(self, x, out=None):
- #TODO Division check
if out is None:
return 1 - self.b/(x + self.bnoise)
else:
+
x.add(self.bnoise, out=out)
self.b.divide(out, out=out)
out.subtract(1, out=out)
@@ -75,16 +78,15 @@ class KullbackLeibler(Function):
def convex_conjugate(self, x):
- tmp = self.b/( 1 - x )
- self.log(tmp)
- return (self.b * ( tmp - 1 ) - self.bnoise * (x - 1)).sum()
-# return self.b * ( ImageData(numpy.log(self.b/(1-x)) - 1 )) - self.bnoise * (x - 1)
-
+ xlogy = - scipy.special.xlogy(self.b.as_array(), 1 - x.as_array())
+ return numpy.sum(xlogy)
+
def proximal(self, x, tau, out=None):
if out is None:
return 0.5 *( (x - self.bnoise - tau) + ( (x + self.bnoise - tau)**2 + 4*tau*self.b ) .sqrt() )
else:
+
tmp = 0.5 *( (x - self.bnoise - tau) +
( (x + self.bnoise - tau)**2 + 4*tau*self.b ) .sqrt()
)
@@ -101,28 +103,29 @@ class KullbackLeibler(Function):
out += tmp
out *= 0.5
-
-
-
+
def proximal_conjugate(self, x, tau, out=None):
if out is None:
z = x + tau * self.bnoise
- return (z + 1) - ((z-1)**2 + 4 * tau * self.b).sqrt()
+ return 0.5*((z + 1) - ((z-1)**2 + 4 * tau * self.b).sqrt())
else:
- z_m = x + tau * self.bnoise - 1
- self.b.multiply(4*tau, out=out)
- z_m.multiply(z_m, out=z_m)
- out += z_m
+
+ #tmp = x + tau * self.bnoise
+ tmp = tau * self.bnoise
+ tmp += x
+ tmp -= 1
+
+ self.b.multiply(4*tau, out=out)
+
+ out.add((tmp)**2, out=out)
out.sqrt(out=out)
- # z = z_m + 2
- z_m.sqrt(out=z_m)
- z_m += 2
out *= -1
- out += z_m
-
-
+ tmp += 2
+ out += tmp
+ out *= 0.5
+
def __rmul__(self, scalar):
''' Multiplication of L2NormSquared with a scalar
@@ -131,25 +134,98 @@ class KullbackLeibler(Function):
'''
- return ScaledFunction(self, scalar)
+ return ScaledFunction(self, scalar)
+
+
+if __name__ == '__main__':
+
+ from ccpi.framework import ImageGeometry
+ import numpy
+
+ M, N = 2,3
+ ig = ImageGeometry(voxel_num_x=M, voxel_num_y = N)
+ u = ig.allocate('random_int')
+ b = ig.allocate('random_int')
+ u.as_array()[1,1]=0
+ u.as_array()[2,0]=0
+ b.as_array()[1,1]=0
+ b.as_array()[2,0]=0
+
+ f = KullbackLeibler(b)
+
+
+# longest = reduce(lambda x, y: len(x) if len(x) > len(y) else len(y), strings)
+
+
+# tmp = functools.reduce(lambda x, y: \
+# 0 if x==0 and not numpy.isnan(y) else x * numpy.log(y), \
+# zip(b.as_array().ravel(), u.as_array().ravel()),0)
+
+
+# np.multiply.reduce(X, 0)
+
+
+# sf = reduce(lambda x,y: x + y[0]*y[1],
+# zip(self.as_array().ravel(),
+# other.as_array().ravel()),
+# 0)
+#cdef inline number_t xlogy(number_t x, number_t y) nogil:
+# if x == 0 and not zisnan(y):
+# return 0
+# else:
+# return x * zlog(y)
+
+# if npy_isnan(x):
+# return x
+# elif x > 0:
+# return -x * log(x)
+# elif x == 0:
+# return 0
+# else:
+# return -inf
+
+# cdef inline double kl_div(double x, double y) nogil:
+# if npy_isnan(x) or npy_isnan(y):
+# return nan
+# elif x > 0 and y > 0:
+# return x * log(x / y) - x + y
+# elif x == 0 and y >= 0:
+# return y
+# else:
+# return inf
+
+
+
+
+# def xlogy(self, dc1, dc2):
+# return numpy.sum(numpy.where(dc1.as_array() != 0, dc2.as_array() * numpy.log(dc2.as_array() / dc1.as_array()), 0))
+
+
+# f.xlog(u, b)
+
-if __name__ == '__main__':
- N, M = 2,3
- ig = ImageGeometry(N, M)
- data = ImageData(numpy.random.randint(-10, 100, size=(M, N)))
- x = ImageData(numpy.random.randint(-10, 100, size=(M, N)))
+# tmp1 = b.as_array()
+# tmp2 = u.as_array()
+#
+# zz = scipy.special.xlogy(tmp1, tmp2)
+#
+# print(np.sum(zz))
- bnoise = ImageData(numpy.random.randint(-100, 100, size=(M, N)))
- f = KullbackLeibler(data, bnoise=bnoise)
- print(f.sum_value)
+# ww = f.xlogy(b, u)
- print(f(x))
-
+# print(ww)
+
+
+#cdef inline double kl_div(double x, double y) nogil:
+
+
+
+
diff --git a/Wrappers/Python/ccpi/optimisation/functions/L1Norm.py b/Wrappers/Python/ccpi/optimisation/functions/L1Norm.py
index 4e53f2c..37c2016 100644
--- a/Wrappers/Python/ccpi/optimisation/functions/L1Norm.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/L1Norm.py
@@ -1,21 +1,23 @@
-# -*- coding: utf-8 -*-
-# This work is part of the Core Imaging Library developed by
-# Visual Analytics and Imaging System Group of the Science Technology
-# Facilities Council, STFC
-
-# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
from ccpi.optimisation.functions import Function
@@ -58,7 +60,7 @@ class L1Norm(Function):
y = 0
if self.b is not None:
- y = 0 + (self.b * x).sum()
+ y = 0 + self.b.dot(x)
return y
def proximal(self, x, tau, out=None):
@@ -93,98 +95,24 @@ class L1Norm(Function):
return ScaledFunction(self, scalar)
-#import numpy as np
-##from ccpi.optimisation.funcs import Function
-#from ccpi.optimisation.functions import Function
-#from ccpi.framework import DataContainer, ImageData
-#
-#
-############################# L1NORM FUNCTIONS #############################
-#class SimpleL1Norm(Function):
-#
-# def __init__(self, alpha=1):
-#
-# super(SimpleL1Norm, self).__init__()
-# self.alpha = alpha
-#
-# def __call__(self, x):
-# return self.alpha * x.abs().sum()
-#
-# def gradient(self,x):
-# return ValueError('Not Differentiable')
-#
-# def convex_conjugate(self,x):
-# return 0
-#
-# def proximal(self, x, tau):
-# ''' Soft Threshold'''
-# return x.sign() * (x.abs() - tau * self.alpha).maximum(0)
-#
-# def proximal_conjugate(self, x, tau):
-# return x.divide((x.abs()/self.alpha).maximum(1.0))
-
-#class L1Norm(SimpleL1Norm):
-#
-# def __init__(self, alpha=1, **kwargs):
-#
-# super(L1Norm, self).__init__()
-# self.alpha = alpha
-# self.b = kwargs.get('b',None)
-#
-# def __call__(self, x):
-#
-# if self.b is None:
-# return SimpleL1Norm.__call__(self, x)
-# else:
-# return SimpleL1Norm.__call__(self, x - self.b)
-#
-# def gradient(self, x):
-# return ValueError('Not Differentiable')
-#
-# def convex_conjugate(self,x):
-# if self.b is None:
-# return SimpleL1Norm.convex_conjugate(self, x)
-# else:
-# return SimpleL1Norm.convex_conjugate(self, x) + (self.b * x).sum()
-#
-# def proximal(self, x, tau):
-#
-# if self.b is None:
-# return SimpleL1Norm.proximal(self, x, tau)
-# else:
-# return self.b + SimpleL1Norm.proximal(self, x - self.b , tau)
-#
-# def proximal_conjugate(self, x, tau):
-#
-# if self.b is None:
-# return SimpleL1Norm.proximal_conjugate(self, x, tau)
-# else:
-# return SimpleL1Norm.proximal_conjugate(self, x - tau*self.b, tau)
-#
-
-###############################################################################
-
-
-
-
if __name__ == '__main__':
from ccpi.framework import ImageGeometry
import numpy
- N, M = 40,40
+ N, M = 400,400
ig = ImageGeometry(N, M)
scalar = 10
- b = ig.allocate('random_int')
- u = ig.allocate('random_int')
+ b = ig.allocate('random')
+ u = ig.allocate('random')
f = L1Norm()
f_scaled = scalar * L1Norm()
-
+
f_b = L1Norm(b=b)
f_scaled_b = scalar * L1Norm(b=b)
- # call
-
+ # call
+
a1 = f(u)
a2 = f_scaled(u)
numpy.testing.assert_equal(scalar * a1, a2)
diff --git a/Wrappers/Python/ccpi/optimisation/functions/L2NormSquared.py b/Wrappers/Python/ccpi/optimisation/functions/L2NormSquared.py
index 2bb4ca7..2f05119 100644
--- a/Wrappers/Python/ccpi/optimisation/functions/L2NormSquared.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/L2NormSquared.py
@@ -19,6 +19,7 @@
from ccpi.optimisation.functions import Function
from ccpi.optimisation.functions.ScaledFunction import ScaledFunction
+from ccpi.optimisation.functions import FunctionOperatorComposition
class L2NormSquared(Function):
@@ -71,11 +72,11 @@ class L2NormSquared(Function):
def convex_conjugate(self, x):
''' Evaluate convex conjugate of L2NormSquared at x: f^{*}(x)'''
-
+
tmp = 0
if self.b is not None:
- tmp = (x * self.b).sum()
+ tmp = x.dot(self.b) #(x * self.b).sum()
return (1./4.) * x.squared_norm() + tmp
@@ -94,27 +95,17 @@ class L2NormSquared(Function):
return x/(1+2*tau)
else:
tmp = x.subtract(self.b)
- #tmp -= self.b
tmp /= (1+2*tau)
tmp += self.b
return tmp
-# return (x-self.b)/(1+2*tau) + self.b
-
-# if self.b is not None:
-# out=x
-# if self.b is not None:
-# out -= self.b
-# out /= (1+2*tau)
-# if self.b is not None:
-# out += self.b
-# return out
+
else:
- out.fill(x)
- if self.b is not None:
- out -= self.b
- out /= (1+2*tau)
if self.b is not None:
- out += self.b
+ x.subtract(self.b, out=out)
+ out /= (1+2*tau)
+ out += self.b
+ else:
+ x.divide((1+2*tau), out=out)
def proximal_conjugate(self, x, tau, out=None):
@@ -145,7 +136,13 @@ class L2NormSquared(Function):
'''
- return ScaledFunction(self, scalar)
+ return ScaledFunction(self, scalar)
+
+
+ def composition(self, operator):
+
+ return FunctionOperatorComposition(operator)
+
if __name__ == '__main__':
@@ -154,7 +151,7 @@ if __name__ == '__main__':
import numpy
# TESTS for L2 and scalar * L2
- M, N, K = 2,3,5
+ M, N, K = 20,30,50
ig = ImageGeometry(voxel_num_x=M, voxel_num_y = N, voxel_num_z = K)
u = ig.allocate('random_int')
b = ig.allocate('random_int')
@@ -181,7 +178,7 @@ if __name__ == '__main__':
#check convex conjuagate with data
d1 = f1.convex_conjugate(u)
- d2 = (1/4) * u.squared_norm() + (u*b).sum()
+ d2 = (1/4) * u.squared_norm() + u.dot(b)
numpy.testing.assert_equal(d1, d2)
# check proximal no data
@@ -287,17 +284,3 @@ if __name__ == '__main__':
numpy.testing.assert_array_almost_equal(res1.as_array(), \
res2.as_array(), decimal=4)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Wrappers/Python/ccpi/optimisation/functions/MixedL21Norm.py b/Wrappers/Python/ccpi/optimisation/functions/MixedL21Norm.py
index 7e6b6e7..e8f6da4 100755
--- a/Wrappers/Python/ccpi/optimisation/functions/MixedL21Norm.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/MixedL21Norm.py
@@ -43,19 +43,9 @@ class MixedL21Norm(Function):
'''
if not isinstance(x, BlockDataContainer):
raise ValueError('__call__ expected BlockDataContainer, got {}'.format(type(x)))
-
- if self.SymTensor:
-
- #TODO fix this case
- param = [1]*x.shape[0]
- param[-1] = 2
- tmp = [param[i]*(x[i] ** 2) for i in range(x.shape[0])]
- res = sum(tmp).sqrt().sum()
-
- else:
-
- tmp = [ el**2 for el in x.containers ]
- res = sum(tmp).sqrt().sum()
+
+ tmp = [ el**2 for el in x.containers ]
+ res = sum(tmp).sqrt().sum()
return res
@@ -67,7 +57,12 @@ class MixedL21Norm(Function):
''' This is the Indicator function of ||\cdot||_{2, \infty}
which is either 0 if ||x||_{2, \infty} or \infty
'''
+
return 0.0
+
+ #tmp = [ el**2 for el in x.containers ]
+ #print(sum(tmp).sqrt().as_array().max())
+ #return sum(tmp).sqrt().as_array().max()
def proximal(self, x, tau, out=None):
@@ -80,34 +75,24 @@ class MixedL21Norm(Function):
def proximal_conjugate(self, x, tau, out=None):
- if self.SymTensor:
-
- param = [1]*x.shape[0]
- param[-1] = 2
- tmp = [param[i]*(x[i] ** 2) for i in range(x.shape[0])]
- frac = [x[i]/(sum(tmp).sqrt()).maximum(1.0) for i in range(x.shape[0])]
- res = BlockDataContainer(*frac)
-
- return res
-
- else:
- if out is None:
- tmp = [ el*el for el in x.containers]
- res = sum(tmp).sqrt().maximum(1.0)
- frac = [el/res for el in x.containers]
- return BlockDataContainer(*frac)
-
- #TODO this is slow, why???
+ if out is None:
+ tmp = [ el*el for el in x.containers]
+ res = sum(tmp).sqrt().maximum(1.0)
+ frac = [el/res for el in x.containers]
+ return BlockDataContainer(*frac)
+
+
+ #TODO this is slow, why???
# return x.divide(x.pnorm().maximum(1.0))
- else:
-
- res1 = functools.reduce(lambda a,b: a + b*b, x.containers, x.get_item(0) * 0 )
- res = res1.sqrt().maximum(1.0)
- x.divide(res, out=out)
-
+ else:
+
+ res1 = functools.reduce(lambda a,b: a + b*b, x.containers, x.get_item(0) * 0 )
+ res = res1.sqrt().maximum(1.0)
+ x.divide(res, out=out)
+
# x.divide(sum([el*el for el in x.containers]).sqrt().maximum(1.0), out=out)
- #TODO this is slow, why ???
+ #TODO this is slow, why ???
# x.divide(x.pnorm().maximum(1.0), out=out)
diff --git a/Wrappers/Python/ccpi/optimisation/functions/Norm2Sq.py b/Wrappers/Python/ccpi/optimisation/functions/Norm2Sq.py
index 0b6bb0b..8e77f56 100755
--- a/Wrappers/Python/ccpi/optimisation/functions/Norm2Sq.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/Norm2Sq.py
@@ -88,7 +88,6 @@ class Norm2Sq(Function):
def gradient(self, x, out = None):
if self.memopt:
#return 2.0*self.c*self.A.adjoint( self.A.direct(x) - self.b )
-
self.A.direct(x, out=self.range_tmp)
self.range_tmp -= self.b
self.A.adjoint(self.range_tmp, out=out)
diff --git a/Wrappers/Python/ccpi/optimisation/functions/ScaledFunction.py b/Wrappers/Python/ccpi/optimisation/functions/ScaledFunction.py
index 7caeab2..8bf502a 100755
--- a/Wrappers/Python/ccpi/optimisation/functions/ScaledFunction.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/ScaledFunction.py
@@ -18,6 +18,7 @@
# limitations under the License.
from numbers import Number
import numpy
+import warnings
class ScaledFunction(object):
@@ -59,7 +60,8 @@ class ScaledFunction(object):
if out is None:
return self.scalar * self.function.gradient(x)
else:
- out.fill( self.scalar * self.function.gradient(x) )
+ self.function.gradient(x, out=out)
+ out *= self.scalar
def proximal(self, x, tau, out=None):
'''This returns the proximal operator for the function at x, tau
@@ -88,7 +90,7 @@ class ScaledFunction(object):
'''Alias of proximal(x, tau, None)'''
warnings.warn('''This method will disappear in following
versions of the CIL. Use proximal instead''', DeprecationWarning)
- return self.proximal(x, out=None)
+ return self.proximal(x, tau, out=None)
diff --git a/Wrappers/Python/ccpi/optimisation/functions/ZeroFunction.py b/Wrappers/Python/ccpi/optimisation/functions/ZeroFunction.py
index cce519a..a019815 100644
--- a/Wrappers/Python/ccpi/optimisation/functions/ZeroFunction.py
+++ b/Wrappers/Python/ccpi/optimisation/functions/ZeroFunction.py
@@ -39,14 +39,8 @@ class ZeroFunction(Function):
indicator function for the set = {0}
So 0 if x=0, or inf if x neq 0
'''
+ return x.maximum(0).sum()
- if x.shape[0]==1:
- return x.maximum(0).sum()
- else:
- if isinstance(x, BlockDataContainer):
- return x.get_item(0).maximum(0).sum() + x.get_item(1).maximum(0).sum()
- else:
- return x.maximum(0).sum() + x.maximum(0).sum()
def proximal(self, x, tau, out=None):
if out is None:
diff --git a/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py b/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py
index 1577f06..cbdc420 100755
--- a/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/BlockOperator.py
@@ -104,7 +104,7 @@ class BlockOperator(Operator):
index = row*self.shape[1]+col
return self.operators[index]
- def calculate_norm(self, **kwargs):
+ def norm(self, **kwargs):
norm = [op.norm(**kwargs)**2 for op in self.operators]
return numpy.sqrt(sum(norm))
@@ -266,15 +266,30 @@ class BlockOperator(Operator):
# column BlockOperator
return self.get_item(0,0).domain_geometry()
else:
- shape = (self.shape[0], 1)
- return BlockGeometry(*[el.domain_geometry() for el in self.operators],
- shape=shape)
+ # get the geometries column wise
+ # we need only the geometries from the first row
+ # since it is compatible from __init__
+ tmp = []
+ for i in range(self.shape[1]):
+ tmp.append(self.get_item(0,i).domain_geometry())
+ return BlockGeometry(*tmp)
+
+ #shape = (self.shape[0], 1)
+ #return BlockGeometry(*[el.domain_geometry() for el in self.operators],
+ # shape=self.shape)
def range_geometry(self):
'''returns the range of the BlockOperator'''
- shape = (self.shape[1], 1)
- return BlockGeometry(*[el.range_geometry() for el in self.operators],
- shape=shape)
+
+ tmp = []
+ for i in range(self.shape[0]):
+ tmp.append(self.get_item(i,0).range_geometry())
+ return BlockGeometry(*tmp)
+
+
+ #shape = (self.shape[1], 1)
+ #return BlockGeometry(*[el.range_geometry() for el in self.operators],
+ # shape=shape)
def sum_abs_row(self):
@@ -312,7 +327,8 @@ class BlockOperator(Operator):
if __name__ == '__main__':
from ccpi.framework import ImageGeometry
- from ccpi.optimisation.operators import Gradient, Identity, SparseFiniteDiff
+ from ccpi.optimisation.operators import Gradient, Identity, \
+ SparseFiniteDiff, SymmetrizedGradient, ZeroOperator
M, N = 4, 3
@@ -363,4 +379,39 @@ if __name__ == '__main__':
+ ###########################################################################
+ # Block Operator for TGV reconstruction
+
+ M, N = 2,3
+ ig = ImageGeometry(M, N)
+ ag = ig
+
+ op11 = Gradient(ig)
+ op12 = Identity(op11.range_geometry())
+
+ op22 = SymmetrizedGradient(op11.domain_geometry())
+
+ op21 = ZeroOperator(ig, op22.range_geometry())
+
+
+ op31 = Identity(ig, ag)
+ op32 = ZeroOperator(op22.domain_geometry(), ag)
+
+ operator = BlockOperator(op11, -1*op12, op21, op22, op31, op32, shape=(3,2) )
+
+ z1 = operator.domain_geometry()
+ z2 = operator.range_geometry()
+
+ print(z1.shape)
+ print(z2.shape)
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator.py b/Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator.py
index 05278ef..876f45f 100644
--- a/Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator.py
@@ -41,7 +41,7 @@ class FiniteDiff(LinearOperator):
#self.voxel_size = kwargs.get('voxel_size',1)
# this wrongly assumes a homogeneous voxel size
- self.voxel_size = self.gm_domain.voxel_size_x
+# self.voxel_size = self.gm_domain.voxel_size_x
def direct(self, x, out=None):
diff --git a/Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator_old.py b/Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator_old.py
deleted file mode 100644
index 387fb4b..0000000
--- a/Wrappers/Python/ccpi/optimisation/operators/FiniteDifferenceOperator_old.py
+++ /dev/null
@@ -1,374 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Mar 1 22:51:17 2019
-
-@author: evangelos
-"""
-
-from ccpi.optimisation.operators import LinearOperator
-from ccpi.optimisation.ops import PowerMethodNonsquare
-from ccpi.framework import ImageData, BlockDataContainer
-import numpy as np
-
-class FiniteDiff(LinearOperator):
-
- # Works for Neum/Symmetric & periodic boundary conditions
- # TODO add central differences???
- # TODO not very well optimised, too many conditions
- # TODO add discretisation step, should get that from imageGeometry
-
- # Grad_order = ['channels', 'direction_z', 'direction_y', 'direction_x']
- # Grad_order = ['channels', 'direction_y', 'direction_x']
- # Grad_order = ['direction_z', 'direction_y', 'direction_x']
- # Grad_order = ['channels', 'direction_z', 'direction_y', 'direction_x']
-
- def __init__(self, gm_domain, gm_range=None, direction=0, bnd_cond = 'Neumann'):
- ''''''
- super(FiniteDiff, self).__init__()
- '''FIXME: domain and range should be geometries'''
- self.gm_domain = gm_domain
- self.gm_range = gm_range
-
- self.direction = direction
- self.bnd_cond = bnd_cond
-
- # Domain Geometry = Range Geometry if not stated
- if self.gm_range is None:
- self.gm_range = self.gm_domain
- # check direction and "length" of geometry
- if self.direction + 1 > len(self.gm_domain.shape):
- raise ValueError('Gradient directions more than geometry domain')
-
- #self.voxel_size = kwargs.get('voxel_size',1)
- # this wrongly assumes a homogeneous voxel size
- self.voxel_size = self.gm_domain.voxel_size_x
-
-
- def direct(self, x, out=None):
-
- x_asarr = x.as_array()
- x_sz = len(x.shape)
-
- if out is None:
- out = np.zeros_like(x_asarr)
- fd_arr = out
- else:
- fd_arr = out.as_array()
-# fd_arr[:]=0
-
-# if out is None:
-# out = self.gm_domain.allocate().as_array()
-#
-# fd_arr = out.as_array()
-# fd_arr = self.gm_domain.allocate().as_array()
-
- ######################## Direct for 2D ###############################
- if x_sz == 2:
-
- if self.direction == 1:
-
- np.subtract( x_asarr[:,1:], x_asarr[:,0:-1], out = fd_arr[:,0:-1] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,0], x_asarr[:,-1], out = fd_arr[:,-1] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 0:
-
- np.subtract( x_asarr[1:], x_asarr[0:-1], out = fd_arr[0:-1,:] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[0,:], x_asarr[-1,:], out = fd_arr[-1,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- ######################## Direct for 3D ###############################
- elif x_sz == 3:
-
- if self.direction == 0:
-
- np.subtract( x_asarr[1:,:,:], x_asarr[0:-1,:,:], out = fd_arr[0:-1,:,:] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[0,:,:], x_asarr[-1,:,:], out = fd_arr[-1,:,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 1:
-
- np.subtract( x_asarr[:,1:,:], x_asarr[:,0:-1,:], out = fd_arr[:,0:-1,:] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,0,:], x_asarr[:,-1,:], out = fd_arr[:,-1,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
-
- if self.direction == 2:
-
- np.subtract( x_asarr[:,:,1:], x_asarr[:,:,0:-1], out = fd_arr[:,:,0:-1] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,:,0], x_asarr[:,:,-1], out = fd_arr[:,:,-1] )
- else:
- raise ValueError('No valid boundary conditions')
-
- ######################## Direct for 4D ###############################
- elif x_sz == 4:
-
- if self.direction == 0:
- np.subtract( x_asarr[1:,:,:,:], x_asarr[0:-1,:,:,:], out = fd_arr[0:-1,:,:,:] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[0,:,:,:], x_asarr[-1,:,:,:], out = fd_arr[-1,:,:,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 1:
- np.subtract( x_asarr[:,1:,:,:], x_asarr[:,0:-1,:,:], out = fd_arr[:,0:-1,:,:] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,0,:,:], x_asarr[:,-1,:,:], out = fd_arr[:,-1,:,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 2:
- np.subtract( x_asarr[:,:,1:,:], x_asarr[:,:,0:-1,:], out = fd_arr[:,:,0:-1,:] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,:,0,:], x_asarr[:,:,-1,:], out = fd_arr[:,:,-1,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 3:
- np.subtract( x_asarr[:,:,:,1:], x_asarr[:,:,:,0:-1], out = fd_arr[:,:,:,0:-1] )
-
- if self.bnd_cond == 'Neumann':
- pass
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,:,:,0], x_asarr[:,:,:,-1], out = fd_arr[:,:,:,-1] )
- else:
- raise ValueError('No valid boundary conditions')
-
- else:
- raise NotImplementedError
-
-# res = out #/self.voxel_size
- return type(x)(out)
-
-
- def adjoint(self, x, out=None):
-
- x_asarr = x.as_array()
- #x_asarr = x
- x_sz = len(x.shape)
-
- if out is None:
- out = np.zeros_like(x_asarr)
- fd_arr = out
- else:
- fd_arr = out.as_array()
-
-# if out is None:
-# out = self.gm_domain.allocate().as_array()
-# fd_arr = out
-# else:
-# fd_arr = out.as_array()
-## fd_arr = self.gm_domain.allocate().as_array()
-
- ######################## Adjoint for 2D ###############################
- if x_sz == 2:
-
- if self.direction == 1:
-
- np.subtract( x_asarr[:,1:], x_asarr[:,0:-1], out = fd_arr[:,1:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[:,0], 0, out = fd_arr[:,0] )
- np.subtract( -x_asarr[:,-2], 0, out = fd_arr[:,-1] )
-
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,0], x_asarr[:,-1], out = fd_arr[:,0] )
-
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 0:
-
- np.subtract( x_asarr[1:,:], x_asarr[0:-1,:], out = fd_arr[1:,:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[0,:], 0, out = fd_arr[0,:] )
- np.subtract( -x_asarr[-2,:], 0, out = fd_arr[-1,:] )
-
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[0,:], x_asarr[-1,:], out = fd_arr[0,:] )
-
- else:
- raise ValueError('No valid boundary conditions')
-
- ######################## Adjoint for 3D ###############################
- elif x_sz == 3:
-
- if self.direction == 0:
-
- np.subtract( x_asarr[1:,:,:], x_asarr[0:-1,:,:], out = fd_arr[1:,:,:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[0,:,:], 0, out = fd_arr[0,:,:] )
- np.subtract( -x_asarr[-2,:,:], 0, out = fd_arr[-1,:,:] )
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[0,:,:], x_asarr[-1,:,:], out = fd_arr[0,:,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 1:
- np.subtract( x_asarr[:,1:,:], x_asarr[:,0:-1,:], out = fd_arr[:,1:,:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[:,0,:], 0, out = fd_arr[:,0,:] )
- np.subtract( -x_asarr[:,-2,:], 0, out = fd_arr[:,-1,:] )
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,0,:], x_asarr[:,-1,:], out = fd_arr[:,0,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 2:
- np.subtract( x_asarr[:,:,1:], x_asarr[:,:,0:-1], out = fd_arr[:,:,1:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[:,:,0], 0, out = fd_arr[:,:,0] )
- np.subtract( -x_asarr[:,:,-2], 0, out = fd_arr[:,:,-1] )
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,:,0], x_asarr[:,:,-1], out = fd_arr[:,:,0] )
- else:
- raise ValueError('No valid boundary conditions')
-
- ######################## Adjoint for 4D ###############################
- elif x_sz == 4:
-
- if self.direction == 0:
- np.subtract( x_asarr[1:,:,:,:], x_asarr[0:-1,:,:,:], out = fd_arr[1:,:,:,:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[0,:,:,:], 0, out = fd_arr[0,:,:,:] )
- np.subtract( -x_asarr[-2,:,:,:], 0, out = fd_arr[-1,:,:,:] )
-
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[0,:,:,:], x_asarr[-1,:,:,:], out = fd_arr[0,:,:,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 1:
- np.subtract( x_asarr[:,1:,:,:], x_asarr[:,0:-1,:,:], out = fd_arr[:,1:,:,:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[:,0,:,:], 0, out = fd_arr[:,0,:,:] )
- np.subtract( -x_asarr[:,-2,:,:], 0, out = fd_arr[:,-1,:,:] )
-
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,0,:,:], x_asarr[:,-1,:,:], out = fd_arr[:,0,:,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
-
- if self.direction == 2:
- np.subtract( x_asarr[:,:,1:,:], x_asarr[:,:,0:-1,:], out = fd_arr[:,:,1:,:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[:,:,0,:], 0, out = fd_arr[:,:,0,:] )
- np.subtract( -x_asarr[:,:,-2,:], 0, out = fd_arr[:,:,-1,:] )
-
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,:,0,:], x_asarr[:,:,-1,:], out = fd_arr[:,:,0,:] )
- else:
- raise ValueError('No valid boundary conditions')
-
- if self.direction == 3:
- np.subtract( x_asarr[:,:,:,1:], x_asarr[:,:,:,0:-1], out = fd_arr[:,:,:,1:] )
-
- if self.bnd_cond == 'Neumann':
- np.subtract( x_asarr[:,:,:,0], 0, out = fd_arr[:,:,:,0] )
- np.subtract( -x_asarr[:,:,:,-2], 0, out = fd_arr[:,:,:,-1] )
-
- elif self.bnd_cond == 'Periodic':
- np.subtract( x_asarr[:,:,:,0], x_asarr[:,:,:,-1], out = fd_arr[:,:,:,0] )
- else:
- raise ValueError('No valid boundary conditions')
-
- else:
- raise NotImplementedError
-
- out *= -1 #/self.voxel_size
- return type(x)(out)
-
- def range_geometry(self):
- '''Returns the range geometry'''
- return self.gm_range
-
- def domain_geometry(self):
- '''Returns the domain geometry'''
- return self.gm_domain
-
- def norm(self):
- x0 = self.gm_domain.allocate()
- x0.fill( np.random.random_sample(x0.shape) )
- self.s1, sall, svec = PowerMethodNonsquare(self, 25, x0)
- return self.s1
-
-
-if __name__ == '__main__':
-
- from ccpi.framework import ImageGeometry
- import numpy
-
- N, M = 2, 3
-
- ig = ImageGeometry(N, M)
-
-
- FD = FiniteDiff(ig, direction = 0, bnd_cond = 'Neumann')
- u = FD.domain_geometry().allocate('random_int')
-
-
- res = FD.domain_geometry().allocate()
- FD.direct(u, out=res)
-
- z = FD.direct(u)
- print(z.as_array(), res.as_array())
-
- for i in range(10):
-
- z1 = FD.direct(u)
- FD.direct(u, out=res)
- numpy.testing.assert_array_almost_equal(z1.as_array(), \
- res.as_array(), decimal=4)
-
-
-
-
-
-
-# w = G.range_geometry().allocate('random_int')
-
-
-
- \ No newline at end of file
diff --git a/Wrappers/Python/ccpi/optimisation/operators/GradientOperator.py b/Wrappers/Python/ccpi/optimisation/operators/GradientOperator.py
index 8aa7f5b..cd58b7d 100644
--- a/Wrappers/Python/ccpi/optimisation/operators/GradientOperator.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/GradientOperator.py
@@ -14,23 +14,47 @@ from ccpi.optimisation.operators import FiniteDiff, SparseFiniteDiff
#%%
class Gradient(LinearOperator):
-
+ CORRELATION_SPACE = "Space"
+ CORRELATION_SPACECHANNEL = "SpaceChannels"
+ # Grad_order = ['channels', 'direction_z', 'direction_y', 'direction_x']
+ # Grad_order = ['channels', 'direction_y', 'direction_x']
+ # Grad_order = ['direction_z', 'direction_y', 'direction_x']
+ # Grad_order = ['channels', 'direction_z', 'direction_y', 'direction_x']
def __init__(self, gm_domain, bnd_cond = 'Neumann', **kwargs):
super(Gradient, self).__init__()
self.gm_domain = gm_domain # Domain of Grad Operator
- self.correlation = kwargs.get('correlation','Space')
+ self.correlation = kwargs.get('correlation',Gradient.CORRELATION_SPACE)
- if self.correlation=='Space':
+ if self.correlation==Gradient.CORRELATION_SPACE:
if self.gm_domain.channels>1:
- self.gm_range = BlockGeometry(*[self.gm_domain for _ in range(self.gm_domain.length-1)] )
- self.ind = numpy.arange(1,self.gm_domain.length)
- else:
+ self.gm_range = BlockGeometry(*[self.gm_domain for _ in range(self.gm_domain.length-1)] )
+ if self.gm_domain.length == 4:
+ # 3D + Channel
+ # expected Grad_order = ['channels', 'direction_z', 'direction_y', 'direction_x']
+ expected_order = [ImageGeometry.CHANNEL, ImageGeometry.VERTICAL, ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X]
+ else:
+ # 2D + Channel
+ # expected Grad_order = ['channels', 'direction_y', 'direction_x']
+ expected_order = [ImageGeometry.CHANNEL, ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X]
+ order = self.gm_domain.get_order_by_label(self.gm_domain.dimension_labels, expected_order)
+ self.ind = order[1:]
+ #self.ind = numpy.arange(1,self.gm_domain.length)
+ else:
+ # no channel info
self.gm_range = BlockGeometry(*[self.gm_domain for _ in range(self.gm_domain.length) ] )
- self.ind = numpy.arange(self.gm_domain.length)
- elif self.correlation=='SpaceChannels':
+ if self.gm_domain.length == 3:
+ # 3D
+ # expected Grad_order = ['direction_z', 'direction_y', 'direction_x']
+ expected_order = [ImageGeometry.VERTICAL, ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X]
+ else:
+ # 2D
+ expected_order = [ImageGeometry.HORIZONTAL_Y, ImageGeometry.HORIZONTAL_X]
+ self.ind = self.gm_domain.get_order_by_label(self.gm_domain.dimension_labels, expected_order)
+ # self.ind = numpy.arange(self.gm_domain.length)
+ elif self.correlation==Gradient.CORRELATION_SPACECHANNEL:
if self.gm_domain.channels>1:
self.gm_range = BlockGeometry(*[self.gm_domain for _ in range(self.gm_domain.length)])
self.ind = range(self.gm_domain.length)
@@ -85,7 +109,6 @@ class Gradient(LinearOperator):
def range_geometry(self):
return self.gm_range
-
def __rmul__(self, scalar):
return ScaledOperator(self, scalar)
@@ -106,7 +129,7 @@ class Gradient(LinearOperator):
return BlockDataContainer(*mat)
- def sum_abs_row(self):
+ def sum_abs_col(self):
tmp = self.gm_range.allocate()
res = self.gm_domain.allocate()
@@ -115,7 +138,7 @@ class Gradient(LinearOperator):
res += spMat.sum_abs_row()
return res
- def sum_abs_col(self):
+ def sum_abs_row(self):
tmp = self.gm_range.allocate()
res = []
@@ -131,14 +154,21 @@ if __name__ == '__main__':
from ccpi.optimisation.operators import Identity, BlockOperator
- M, N = 2, 3
+ M, N = 20, 30
ig = ImageGeometry(M, N)
arr = ig.allocate('random_int' )
# check direct of Gradient and sparse matrix
G = Gradient(ig)
+ norm1 = G.norm(iterations=300)
+ print ("should be sqrt(8) {} {}".format(numpy.sqrt(8), norm1))
G_sp = G.matrix()
+ ig4 = ImageGeometry(M,N, channels=3)
+ G4 = Gradient(ig4, correlation=Gradient.CORRELATION_SPACECHANNEL)
+ norm4 = G4.norm(iterations=300)
+ print ("should be sqrt(12) {} {}".format(numpy.sqrt(12), norm4))
+
res1 = G.direct(arr)
res1y = numpy.reshape(G_sp[0].toarray().dot(arr.as_array().flatten('F')), ig.shape, 'F')
diff --git a/Wrappers/Python/ccpi/optimisation/operators/IdentityOperator.py b/Wrappers/Python/ccpi/optimisation/operators/IdentityOperator.py
index f084504..8f35373 100644
--- a/Wrappers/Python/ccpi/optimisation/operators/IdentityOperator.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/IdentityOperator.py
@@ -50,7 +50,7 @@ class Identity(LinearOperator):
def sum_abs_row(self):
- return self.gm_domain.allocate(1)#ImageData(np.array(np.reshape(abs(self.matrix()).sum(axis=0), self.gm_domain.shape, 'F')))
+ return self.gm_range.allocate(1)#ImageData(np.array(np.reshape(abs(self.matrix()).sum(axis=0), self.gm_domain.shape, 'F')))
def sum_abs_col(self):
diff --git a/Wrappers/Python/ccpi/optimisation/operators/LinearOperatorMatrix.py b/Wrappers/Python/ccpi/optimisation/operators/LinearOperatorMatrix.py
index 8cc4c71..90ef938 100644
--- a/Wrappers/Python/ccpi/optimisation/operators/LinearOperatorMatrix.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/LinearOperatorMatrix.py
@@ -2,14 +2,17 @@ import numpy
from scipy.sparse.linalg import svds
from ccpi.framework import DataContainer
from ccpi.framework import AcquisitionData
-from ccpi.framework import ImageData
-from ccpi.framework import ImageGeometry
+from ccpi.framework import VectorData
+from ccpi.framework import VectorGeometry
from ccpi.framework import AcquisitionGeometry
from numbers import Number
from ccpi.optimisation.operators import LinearOperator
class LinearOperatorMatrix(LinearOperator):
def __init__(self,A):
self.A = A
+ M_A, N_A = self.A.shape
+ self.gm_domain = VectorGeometry(N_A)
+ self.gm_range = VectorGeometry(M_A)
self.s1 = None # Largest singular value, initially unknown
super(LinearOperatorMatrix, self).__init__()
@@ -18,15 +21,13 @@ class LinearOperatorMatrix(LinearOperator):
return type(x)(numpy.dot(self.A,x.as_array()))
else:
numpy.dot(self.A, x.as_array(), out=out.as_array())
-
-
+
def adjoint(self,x, out=None):
if out is None:
return type(x)(numpy.dot(self.A.transpose(),x.as_array()))
else:
numpy.dot(self.A.transpose(),x.as_array(), out=out.as_array())
-
-
+
def size(self):
return self.A.shape
@@ -34,3 +35,7 @@ class LinearOperatorMatrix(LinearOperator):
# If unknown, compute and store. If known, simply return it.
return svds(self.A,1,return_singular_vectors=False)[0]
+ def domain_geometry(self):
+ return self.gm_domain
+ def range_geometry(self):
+ return self.gm_range
diff --git a/Wrappers/Python/ccpi/optimisation/operators/SparseFiniteDiff.py b/Wrappers/Python/ccpi/optimisation/operators/SparseFiniteDiff.py
index eb51cc3..cb76dce 100644
--- a/Wrappers/Python/ccpi/optimisation/operators/SparseFiniteDiff.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/SparseFiniteDiff.py
@@ -65,13 +65,13 @@ class SparseFiniteDiff(object):
def sum_abs_row(self):
res = np.array(np.reshape(abs(self.matrix()).sum(axis=0), self.gm_domain.shape, 'F'))
- res[res==0]=1
+ #res[res==0]=0
return ImageData(res)
def sum_abs_col(self):
res = np.array(np.reshape(abs(self.matrix()).sum(axis=1), self.gm_domain.shape, 'F') )
- res[res==0]=1
+ #res[res==0]=0
return ImageData(res)
if __name__ == '__main__':
diff --git a/Wrappers/Python/ccpi/optimisation/operators/SymmetrizedGradientOperator.py b/Wrappers/Python/ccpi/optimisation/operators/SymmetrizedGradientOperator.py
index 1897040..205f7e1 100644
--- a/Wrappers/Python/ccpi/optimisation/operators/SymmetrizedGradientOperator.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/SymmetrizedGradientOperator.py
@@ -14,6 +14,12 @@ from ccpi.optimisation.operators import FiniteDiff, SparseFiniteDiff
class SymmetrizedGradient(Gradient):
+ ''' Symmetrized Gradient, denoted by E: V --> W
+ where V is the Range of the Gradient Operator
+ and W is the Range of the Symmetrized Gradient.
+ '''
+
+
def __init__(self, gm_domain, bnd_cond = 'Neumann', **kwargs):
super(SymmetrizedGradient, self).__init__(gm_domain, bnd_cond, **kwargs)
@@ -21,135 +27,207 @@ class SymmetrizedGradient(Gradient):
'''
Domain of SymGrad is the Range of Gradient
'''
+
self.gm_domain = self.gm_range
self.bnd_cond = bnd_cond
self.channels = self.gm_range.get_item(0).channels
- if self.correlation=='Space':
- if self.channels>1:
- pass
- else:
-# # 2D image ---> Dx v1, Dyv2, Dx
- tmp = self.gm_domain.geometries + (self.gm_domain.get_item(0),)
- self.gm_range = BlockGeometry(*tmp )
- self.ind1 = range(self.gm_domain.get_item(0).length)
- self.ind2 = range(self.gm_domain.get_item(0).length-1, -1, -1)
-# self.order = myorder = [0,1,2 3]
+ tmp_gm = len(self.gm_domain.geometries)*self.gm_domain.geometries
+
+ self.gm_range = BlockGeometry(*tmp_gm)
+
+ self.FD = FiniteDiff(self.gm_domain, direction = 0, bnd_cond = self.bnd_cond)
+
+ if self.gm_domain.shape[0]==2:
+ self.order_ind = [0,2,1,3]
+ else:
+ self.order_ind = [0,3,6,1,4,7,2,5,8]
- elif self.correlation=='SpaceChannels':
- if self.channels>1:
- pass
- else:
- raise ValueError('No channels to correlate')
-
def direct(self, x, out=None):
-# tmp = numpy.zeros(self.gm_range)
-# tmp[0] = FiniteDiff(self.gm_domain[1:], direction = 1, bnd_cond = self.bnd_cond).adjoint(x.as_array()[0])
-# tmp[1] = FiniteDiff(self.gm_domain[1:], direction = 0, bnd_cond = self.bnd_cond).adjoint(x.as_array()[1])
-# tmp[2] = 0.5 * (FiniteDiff(self.gm_domain[1:], direction = 0, bnd_cond = self.bnd_cond).adjoint(x.as_array()[0]) +
-# FiniteDiff(self.gm_domain[1:], direction = 1, bnd_cond = self.bnd_cond).adjoint(x.as_array()[1]) )
-#
-# return type(x)(tmp)
-
- tmp = [[None]*2]*2
- for i in range(2):
- for j in range(2):
- tmp[i][j]=FiniteDiff(self.gm_domain.get_item(0), direction = i, bnd_cond = self.bnd_cond).adjoint(x.get_item(j))
- tmp = numpy.array(tmp)
- z = 0.5 * (tmp.T + tmp)
- z = z.to
-
- return BlockDataContainer(*z.tolist())
-
-
+ if out is None:
+
+ tmp = []
+ for i in range(self.gm_domain.shape[0]):
+ for j in range(x.shape[0]):
+ self.FD.direction = i
+ tmp.append(self.FD.adjoint(x.get_item(j)))
+
+ tmp1 = [tmp[i] for i in self.order_ind]
+
+ res = [0.5 * sum(x) for x in zip(tmp, tmp1)]
+
+ return BlockDataContainer(*res)
+
+ else:
+
+ ind = 0
+ for i in range(self.gm_domain.shape[0]):
+ for j in range(x.shape[0]):
+ self.FD.direction = i
+ self.FD.adjoint(x.get_item(j), out=out[ind])
+ ind+=1
+ out1 = BlockDataContainer(*[out[i] for i in self.order_ind])
+ out.fill( 0.5 * (out + out1) )
+
+
def adjoint(self, x, out=None):
- pass
- res = []
- for i in range(2):
- tmp = ImageData(np.zeros(x.get_item(0)))
- for j in range(2):
- tmp += FiniteDiff(self.gm_domain.get_item(0), direction = i, bnd_cond = self.bnd_cond).direct(x.get_item(j))
- res.append(tmp)
- return res
+ if out is None:
+
+ tmp = [None]*self.gm_domain.shape[0]
+ i = 0
+
+ for k in range(self.gm_domain.shape[0]):
+ tmp1 = 0
+ for j in range(self.gm_domain.shape[0]):
+ self.FD.direction = j
+ tmp1 += self.FD.direct(x[i])
+ i+=1
+ tmp[k] = tmp1
+ return BlockDataContainer(*tmp)
+
- def domain_dim(self):
+ else:
+
+ tmp = self.gm_domain.allocate()
+ i = 0
+ for k in range(self.gm_domain.shape[0]):
+ tmp1 = 0
+ for j in range(self.gm_domain.shape[0]):
+ self.FD.direction = j
+ self.FD.direct(x[i], out=tmp[j])
+ i+=1
+ tmp1+=tmp[j]
+ out[k].fill(tmp1)
+# tmp = self.adjoint(x)
+# out.fill(tmp)
+
+
+ def domain_geometry(self):
return self.gm_domain
- def range_dim(self):
+ def range_geometry(self):
return self.gm_range
if __name__ == '__main__':
###########################################################################
- ## Symmetrized Gradient
+ ## Symmetrized Gradient Tests
from ccpi.framework import DataContainer
from ccpi.optimisation.operators import Gradient, BlockOperator, FiniteDiff
import numpy as np
N, M = 2, 3
K = 2
+ C = 2
ig1 = ImageGeometry(N, M)
- ig2 = ImageGeometry(N, M, channels=K)
+ ig2 = ImageGeometry(N, M, channels=C)
E1 = SymmetrizedGradient(ig1, correlation = 'Space', bnd_cond='Neumann')
- E2 = SymmetrizedGradient(ig2, correlation = 'SpaceChannels', bnd_cond='Periodic')
- print(E1.domain_geometry().shape)
- print(E2.domain_geometry().shape)
+ try:
+ E1 = SymmetrizedGradient(ig1, correlation = 'SpaceChannels', bnd_cond='Neumann')
+ except:
+ print("No Channels to correlate")
+
+ E2 = SymmetrizedGradient(ig2, correlation = 'SpaceChannels', bnd_cond='Neumann')
+
+ print(E1.domain_geometry().shape, E1.range_geometry().shape)
+ print(E2.domain_geometry().shape, E2.range_geometry().shape)
+
+ #check Linear operator property
+
+ u1 = E1.domain_geometry().allocate('random_int')
+ u2 = E2.domain_geometry().allocate('random_int')
+
+ # Need to allocate random_int at the Range of SymGradient
+
+ #a1 = ig1.allocate('random_int')
+ #a2 = ig1.allocate('random_int')
+ #a3 = ig1.allocate('random_int')
+
+ #a4 = ig1.allocate('random_int')
+ #a5 = ig1.allocate('random_int')
+ #a6 = ig1.allocate('random_int')
+
+ # TODO allocate has to create this symmetry by default!!!!!
+ #w1 = BlockDataContainer(*[a1, a2, \
+ # a2, a3])
+ w1 = E1.range_geometry().allocate('random_int',symmetry=True)
+
+ LHS = (E1.direct(u1) * w1).sum()
+ RHS = (u1 * E1.adjoint(w1)).sum()
+
+ numpy.testing.assert_equal(LHS, RHS)
- u1 = E1.gm_domain.allocate('random_int')
u2 = E2.gm_domain.allocate('random_int')
-
- res = E1.direct(u1)
+ #aa1 = ig2.allocate('random_int')
+ #aa2 = ig2.allocate('random_int')
+ #aa3 = ig2.allocate('random_int')
+ #aa4 = ig2.allocate('random_int')
+ #aa5 = ig2.allocate('random_int')
+ #aa6 = ig2.allocate('random_int')
- res1 = E1.adjoint(res)
+ #w2 = BlockDataContainer(*[aa1, aa2, aa3, \
+ # aa2, aa4, aa5, \
+ # aa3, aa5, aa6])
+ w2 = E2.range_geometry().allocate('random_int',symmetry=True)
+
-# Dx = FiniteDiff(ig1, direction = 1, bnd_cond = 'Neumann')
-# Dy = FiniteDiff(ig1, direction = 0, bnd_cond = 'Neumann')
-#
-# B = BlockOperator(Dy, Dx)
-# V = BlockDataContainer(u1,u2)
-#
-# res = B.adjoint(V)
+ LHS1 = (E2.direct(u2) * w2).sum()
+ RHS1 = (u2 * E2.adjoint(w2)).sum()
-# ig = (N,M)
-# ig2 = (2,) + ig
-# ig3 = (3,) + ig
-# u1 = ig.allocate('random_int')
-# w1 = E.gm_range.allocate('random_int')
-# DataContainer(np.random.randint(10, size=ig3))
+ numpy.testing.assert_equal(LHS1, RHS1)
+ out = E1.range_geometry().allocate()
+ E1.direct(u1, out=out)
+ a1 = E1.direct(u1)
+ numpy.testing.assert_array_equal(a1[0].as_array(), out[0].as_array())
+ numpy.testing.assert_array_equal(a1[1].as_array(), out[1].as_array())
+ numpy.testing.assert_array_equal(a1[2].as_array(), out[2].as_array())
+ numpy.testing.assert_array_equal(a1[3].as_array(), out[3].as_array())
-# d1 = E.direct(u1)
-# d2 = E.adjoint(w1)
+ out1 = E1.domain_geometry().allocate()
+ E1.adjoint(w1, out=out1)
+ b1 = E1.adjoint(w1)
-# LHS = (d1.as_array()[0]*w1.as_array()[0] + \
-# d1.as_array()[1]*w1.as_array()[1] + \
-# 2*d1.as_array()[2]*w1.as_array()[2]).sum()
-#
-# RHS = (u1.as_array()[0]*d2.as_array()[0] + \
-# u1.as_array()[1]*d2.as_array()[1]).sum()
-#
-#
-# print(LHS, RHS, E.norm())
-#
+ LHS_out = (out * w1).sum()
+ RHS_out = (u1 * out1).sum()
+ print(LHS_out, RHS_out)
-#
+ out2 = E2.range_geometry().allocate()
+ E2.direct(u2, out=out2)
+ a2 = E2.direct(u2)
+
+ out21 = E2.domain_geometry().allocate()
+ E2.adjoint(w2, out=out21)
+ b2 = E2.adjoint(w2)
+ LHS_out = (out2 * w2).sum()
+ RHS_out = (u2 * out21).sum()
+ print(LHS_out, RHS_out)
+ out = E1.range_geometry().allocate()
+ E1.direct(u1, out=out)
+ E1.adjoint(out, out=out1)
+ print(E1.norm())
+ print(E2.norm())
- \ No newline at end of file
+
+#
+#
+#
+#
diff --git a/Wrappers/Python/ccpi/optimisation/operators/ZeroOperator.py b/Wrappers/Python/ccpi/optimisation/operators/ZeroOperator.py
index a2a94b7..67808de 100644
--- a/Wrappers/Python/ccpi/optimisation/operators/ZeroOperator.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/ZeroOperator.py
@@ -8,32 +8,37 @@ Created on Wed Mar 6 19:25:53 2019
import numpy as np
from ccpi.framework import ImageData
-from ccpi.optimisation.operators import Operator
+from ccpi.optimisation.operators import LinearOperator
-class ZeroOp(Operator):
+class ZeroOperator(LinearOperator):
- def __init__(self, gm_domain, gm_range):
+ def __init__(self, gm_domain, gm_range=None):
+
+ super(ZeroOperator, self).__init__()
+
self.gm_domain = gm_domain
- self.gm_range = gm_range
- super(ZeroOp, self).__init__()
+ self.gm_range = gm_range
+ if self.gm_range is None:
+ self.gm_range = self.gm_domain
+
def direct(self,x,out=None):
if out is None:
- return ImageData(np.zeros(self.gm_range))
+ return self.gm_range.allocate()
else:
- return ImageData(np.zeros(self.gm_range))
+ out.fill(self.gm_range.allocate())
def adjoint(self,x, out=None):
if out is None:
- return ImageData(np.zeros(self.gm_domain))
+ return self.gm_domain.allocate()
else:
- return ImageData(np.zeros(self.gm_domain))
+ out.fill(self.gm_domain.allocate())
def calculate_norm(self, **kwargs):
return 0.
- def domain_dim(self):
+ def domain_geometry(self):
return self.gm_domain
- def range_dim(self):
+ def range_geometry(self):
return self.gm_range \ No newline at end of file
diff --git a/Wrappers/Python/ccpi/optimisation/operators/__init__.py b/Wrappers/Python/ccpi/optimisation/operators/__init__.py
index 811adf6..23222d4 100755
--- a/Wrappers/Python/ccpi/optimisation/operators/__init__.py
+++ b/Wrappers/Python/ccpi/optimisation/operators/__init__.py
@@ -18,6 +18,6 @@ from .FiniteDifferenceOperator import FiniteDiff
from .GradientOperator import Gradient
from .SymmetrizedGradientOperator import SymmetrizedGradient
from .IdentityOperator import Identity
-from .ZeroOperator import ZeroOp
+from .ZeroOperator import ZeroOperator
from .LinearOperatorMatrix import LinearOperatorMatrix
diff --git a/Wrappers/Python/ccpi/processors.py b/Wrappers/Python/ccpi/processors/CenterOfRotationFinder.py
index ccef410..936dc05 100755
--- a/Wrappers/Python/ccpi/processors.py
+++ b/Wrappers/Python/ccpi/processors/CenterOfRotationFinder.py
@@ -19,115 +19,9 @@
from ccpi.framework import DataProcessor, DataContainer, AcquisitionData,\
AcquisitionGeometry, ImageGeometry, ImageData
-from ccpi.reconstruction.parallelbeam import alg as pbalg
import numpy
from scipy import ndimage
-import matplotlib.pyplot as plt
-
-
-class Normalizer(DataProcessor):
- '''Normalization based on flat and dark
-
- This processor read in a AcquisitionData and normalises it based on
- the instrument reading with and without incident photons or neutrons.
-
- Input: AcquisitionData
- Parameter: 2D projection with flat field (or stack)
- 2D projection with dark field (or stack)
- Output: AcquisitionDataSetn
- '''
-
- def __init__(self, flat_field = None, dark_field = None, tolerance = 1e-5):
- kwargs = {
- 'flat_field' : flat_field,
- 'dark_field' : dark_field,
- # very small number. Used when there is a division by zero
- 'tolerance' : tolerance
- }
-
- #DataProcessor.__init__(self, **kwargs)
- super(Normalizer, self).__init__(**kwargs)
- if not flat_field is None:
- self.set_flat_field(flat_field)
- if not dark_field is None:
- self.set_dark_field(dark_field)
-
- def check_input(self, dataset):
- if dataset.number_of_dimensions == 3 or\
- dataset.number_of_dimensions == 2:
- return True
- else:
- raise ValueError("Expected input dimensions is 2 or 3, got {0}"\
- .format(dataset.number_of_dimensions))
-
- def set_dark_field(self, df):
- if type(df) is numpy.ndarray:
- if len(numpy.shape(df)) == 3:
- raise ValueError('Dark Field should be 2D')
- elif len(numpy.shape(df)) == 2:
- self.dark_field = df
- elif issubclass(type(df), DataContainer):
- self.dark_field = self.set_dark_field(df.as_array())
-
- def set_flat_field(self, df):
- if type(df) is numpy.ndarray:
- if len(numpy.shape(df)) == 3:
- raise ValueError('Flat Field should be 2D')
- elif len(numpy.shape(df)) == 2:
- self.flat_field = df
- elif issubclass(type(df), DataContainer):
- self.flat_field = self.set_flat_field(df.as_array())
-
- @staticmethod
- def normalize_projection(projection, flat, dark, tolerance):
- a = (projection - dark)
- b = (flat-dark)
- with numpy.errstate(divide='ignore', invalid='ignore'):
- c = numpy.true_divide( a, b )
- c[ ~ numpy.isfinite( c )] = tolerance # set to not zero if 0/0
- return c
-
- @staticmethod
- def estimate_normalised_error(projection, flat, dark, delta_flat, delta_dark):
- '''returns the estimated relative error of the normalised projection
-
- n = (projection - dark) / (flat - dark)
- Dn/n = (flat-dark + projection-dark)/((flat-dark)*(projection-dark))*(Df/f + Dd/d)
- '''
- a = (projection - dark)
- b = (flat-dark)
- df = delta_flat / flat
- dd = delta_dark / dark
- rel_norm_error = (b + a) / (b * a) * (df + dd)
- return rel_norm_error
-
- def process(self, out=None):
-
- projections = self.get_input()
- dark = self.dark_field
- flat = self.flat_field
-
- if projections.number_of_dimensions == 3:
- if not (projections.shape[1:] == dark.shape and \
- projections.shape[1:] == flat.shape):
- raise ValueError('Flats/Dark and projections size do not match.')
-
-
- a = numpy.asarray(
- [ Normalizer.normalize_projection(
- projection, flat, dark, self.tolerance) \
- for projection in projections.as_array() ]
- )
- elif projections.number_of_dimensions == 2:
- a = Normalizer.normalize_projection(projections.as_array(),
- flat, dark, self.tolerance)
- y = type(projections)( a , True,
- dimension_labels=projections.dimension_labels,
- geometry=projections.geometry)
- return y
-
-
class CenterOfRotationFinder(DataProcessor):
'''Processor to find the center of rotation in a parallel beam experiment
diff --git a/Wrappers/Python/ccpi/processors/Normalizer.py b/Wrappers/Python/ccpi/processors/Normalizer.py
new file mode 100755
index 0000000..da65e5c
--- /dev/null
+++ b/Wrappers/Python/ccpi/processors/Normalizer.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018 Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+from ccpi.framework import DataProcessor, DataContainer, AcquisitionData,\
+ AcquisitionGeometry, ImageGeometry, ImageData
+import numpy
+
+class Normalizer(DataProcessor):
+ '''Normalization based on flat and dark
+
+ This processor read in a AcquisitionData and normalises it based on
+ the instrument reading with and without incident photons or neutrons.
+
+ Input: AcquisitionData
+ Parameter: 2D projection with flat field (or stack)
+ 2D projection with dark field (or stack)
+ Output: AcquisitionDataSetn
+ '''
+
+ def __init__(self, flat_field = None, dark_field = None, tolerance = 1e-5):
+ kwargs = {
+ 'flat_field' : flat_field,
+ 'dark_field' : dark_field,
+ # very small number. Used when there is a division by zero
+ 'tolerance' : tolerance
+ }
+
+ #DataProcessor.__init__(self, **kwargs)
+ super(Normalizer, self).__init__(**kwargs)
+ if not flat_field is None:
+ self.set_flat_field(flat_field)
+ if not dark_field is None:
+ self.set_dark_field(dark_field)
+
+ def check_input(self, dataset):
+ if dataset.number_of_dimensions == 3 or\
+ dataset.number_of_dimensions == 2:
+ return True
+ else:
+ raise ValueError("Expected input dimensions is 2 or 3, got {0}"\
+ .format(dataset.number_of_dimensions))
+
+ def set_dark_field(self, df):
+ if type(df) is numpy.ndarray:
+ if len(numpy.shape(df)) == 3:
+ raise ValueError('Dark Field should be 2D')
+ elif len(numpy.shape(df)) == 2:
+ self.dark_field = df
+ elif issubclass(type(df), DataContainer):
+ self.dark_field = self.set_dark_field(df.as_array())
+
+ def set_flat_field(self, df):
+ if type(df) is numpy.ndarray:
+ if len(numpy.shape(df)) == 3:
+ raise ValueError('Flat Field should be 2D')
+ elif len(numpy.shape(df)) == 2:
+ self.flat_field = df
+ elif issubclass(type(df), DataContainer):
+ self.flat_field = self.set_flat_field(df.as_array())
+
+ @staticmethod
+ def normalize_projection(projection, flat, dark, tolerance):
+ a = (projection - dark)
+ b = (flat-dark)
+ with numpy.errstate(divide='ignore', invalid='ignore'):
+ c = numpy.true_divide( a, b )
+ c[ ~ numpy.isfinite( c )] = tolerance # set to not zero if 0/0
+ return c
+
+ @staticmethod
+ def estimate_normalised_error(projection, flat, dark, delta_flat, delta_dark):
+ '''returns the estimated relative error of the normalised projection
+
+ n = (projection - dark) / (flat - dark)
+ Dn/n = (flat-dark + projection-dark)/((flat-dark)*(projection-dark))*(Df/f + Dd/d)
+ '''
+ a = (projection - dark)
+ b = (flat-dark)
+ df = delta_flat / flat
+ dd = delta_dark / dark
+ rel_norm_error = (b + a) / (b * a) * (df + dd)
+ return rel_norm_error
+
+ def process(self, out=None):
+
+ projections = self.get_input()
+ dark = self.dark_field
+ flat = self.flat_field
+
+ if projections.number_of_dimensions == 3:
+ if not (projections.shape[1:] == dark.shape and \
+ projections.shape[1:] == flat.shape):
+ raise ValueError('Flats/Dark and projections size do not match.')
+
+
+ a = numpy.asarray(
+ [ Normalizer.normalize_projection(
+ projection, flat, dark, self.tolerance) \
+ for projection in projections.as_array() ]
+ )
+ elif projections.number_of_dimensions == 2:
+ a = Normalizer.normalize_projection(projections.as_array(),
+ flat, dark, self.tolerance)
+ y = type(projections)( a , True,
+ dimension_labels=projections.dimension_labels,
+ geometry=projections.geometry)
+ return y
+ \ No newline at end of file
diff --git a/Wrappers/Python/conda-recipe/conda_build_config.yaml b/Wrappers/Python/conda-recipe/conda_build_config.yaml
index 30c8e9d..393ae18 100644
--- a/Wrappers/Python/conda-recipe/conda_build_config.yaml
+++ b/Wrappers/Python/conda-recipe/conda_build_config.yaml
@@ -4,5 +4,5 @@ python:
- 3.6
numpy:
# TODO investigage, as it doesn't currently build with cvxp, requires >1.14
+ - 1.11
- 1.12
- - 1.15
diff --git a/Wrappers/Python/conda-recipe/meta.yaml b/Wrappers/Python/conda-recipe/meta.yaml
index 163d326..9d03220 100644
--- a/Wrappers/Python/conda-recipe/meta.yaml
+++ b/Wrappers/Python/conda-recipe/meta.yaml
@@ -26,7 +26,6 @@ requirements:
build:
- {{ pin_compatible('numpy', max_pin='x.x') }}
- python
- - numpy
- setuptools
run:
@@ -34,7 +33,7 @@ requirements:
- python
- numpy
- scipy
- #- matplotlib
+ - matplotlib
- h5py
- pillow
diff --git a/Wrappers/Python/data/boat.tiff b/Wrappers/Python/data/boat.tiff
new file mode 100644
index 0000000..fc1205a
--- /dev/null
+++ b/Wrappers/Python/data/boat.tiff
Binary files differ
diff --git a/Wrappers/Python/data/camera.png b/Wrappers/Python/data/camera.png
new file mode 100644
index 0000000..49be869
--- /dev/null
+++ b/Wrappers/Python/data/camera.png
Binary files differ
diff --git a/Wrappers/Python/data/peppers.tiff b/Wrappers/Python/data/peppers.tiff
new file mode 100644
index 0000000..8c956f8
--- /dev/null
+++ b/Wrappers/Python/data/peppers.tiff
Binary files differ
diff --git a/Wrappers/Python/data/resolution_chart.tiff b/Wrappers/Python/data/resolution_chart.tiff
new file mode 100755
index 0000000..d09cef3
--- /dev/null
+++ b/Wrappers/Python/data/resolution_chart.tiff
Binary files differ
diff --git a/Wrappers/Python/data/shapes.png b/Wrappers/Python/data/shapes.png
new file mode 100644
index 0000000..dd4f680
--- /dev/null
+++ b/Wrappers/Python/data/shapes.png
Binary files differ
diff --git a/Wrappers/Python/data/test_show_data.py b/Wrappers/Python/data/test_show_data.py
new file mode 100644
index 0000000..7325c27
--- /dev/null
+++ b/Wrappers/Python/data/test_show_data.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Tue May 7 20:43:48 2019
+
+@author: evangelos
+"""
+
+from ccpi.data import camera, boat, peppers
+import matplotlib.pyplot as plt
+
+
+d = camera(size=(256,256))
+
+plt.imshow(d.as_array())
+plt.colorbar()
+plt.show()
+
+d1 = boat(size=(256,256))
+
+plt.imshow(d1.as_array())
+plt.colorbar()
+plt.show()
+
+
+d2 = peppers(size=(256,256))
+
+plt.imshow(d2.as_array())
+plt.colorbar()
+plt.show() \ No newline at end of file
diff --git a/Wrappers/Python/demos/CGLS_examples/CGLS_Tikhonov.py b/Wrappers/Python/demos/CGLS_examples/CGLS_Tikhonov.py
new file mode 100644
index 0000000..653e191
--- /dev/null
+++ b/Wrappers/Python/demos/CGLS_examples/CGLS_Tikhonov.py
@@ -0,0 +1,106 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+Compare solutions of PDHG & "Block CGLS" algorithms for
+
+
+Problem: min_x alpha * ||\grad x ||^{2}_{2} + || A x - g ||_{2}^{2}
+
+
+ A: Projection operator
+ g: Sinogram
+
+"""
+
+
+from ccpi.framework import AcquisitionGeometry, BlockDataContainer, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import CGLS
+from ccpi.optimisation.operators import BlockOperator, Gradient
+
+from ccpi.framework import TestData
+import os, sys
+from ccpi.astra.ops import AstraProjectorSimple
+
+# Load Data
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+N = 150
+M = 150
+data = loader.load(TestData.SIMPLE_PHANTOM_2D, size=(N,M), scale=(0,1))
+
+ig = data.geometry
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+ag = AcquisitionGeometry('parallel','2D', angles, detectors)
+
+device = input('Available device: GPU==1 / CPU==0 ')
+if device=='1':
+ dev = 'gpu'
+else:
+ dev = 'cpu'
+
+Aop = AstraProjectorSimple(ig, ag, dev)
+sin = Aop.direct(data)
+
+noisy_data = AcquisitionData( sin.as_array() + np.random.normal(0,3,ig.shape))
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,10))
+plt.subplot(2,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(2,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+# Setup and run the CGLS algorithm
+#alpha = 50
+#Grad = Gradient(ig)
+#
+## Form Tikhonov as a Block CGLS structure
+#op_CGLS = BlockOperator( Aop, alpha * Grad, shape=(2,1))
+#block_data = BlockDataContainer(noisy_data, Grad.range_geometry().allocate())
+#
+#x_init = ig.allocate()
+#cgls = CGLS(x_init=x_init, operator=op_CGLS, data=block_data)
+#cgls.max_iteration = 1000
+#cgls.update_objective_interval = 200
+#cgls.run(1000,verbose=False)
+
+#%%
+# Show results
+plt.figure(figsize=(5,5))
+plt.imshow(cgls.get_output().as_array())
+plt.title('CGLS reconstruction')
+plt.colorbar()
+plt.show()
+
+
diff --git a/Wrappers/Python/demos/CompareAlgorithms/CGLS_FISTA_PDHG_LeastSquares.py b/Wrappers/Python/demos/CompareAlgorithms/CGLS_FISTA_PDHG_LeastSquares.py
new file mode 100644
index 0000000..672d4bc
--- /dev/null
+++ b/Wrappers/Python/demos/CompareAlgorithms/CGLS_FISTA_PDHG_LeastSquares.py
@@ -0,0 +1,189 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+Compare solutions of FISTA & PDHG
+ & CGLS & Astra Built-in algorithms for Least Squares
+
+
+Problem: min_x || A x - g ||_{2}^{2}
+
+ A: Projection operator
+ g: Sinogram
+
+"""
+
+
+from ccpi.framework import ImageData, TestData, AcquisitionGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG, CGLS, FISTA
+
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, FunctionOperatorComposition
+from ccpi.astra.ops import AstraProjectorSimple
+import astra
+import os, sys
+
+
+# Load Data
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+
+N = 50
+M = 50
+data = loader.load(TestData.SIMPLE_PHANTOM_2D, size=(N,M), scale=(0,1))
+ig = data.geometry
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+device = input('Available device: GPU==1 / CPU==0 ')
+ag = AcquisitionGeometry('parallel','2D', angles, detectors)
+if device=='1':
+ dev = 'gpu'
+else:
+ dev = 'cpu'
+
+Aop = AstraProjectorSimple(ig, ag, dev)
+sin = Aop.direct(data)
+
+noisy_data = sin
+
+###############################################################################
+# Setup and run Astra CGLS algorithm
+vol_geom = astra.create_vol_geom(N, N)
+proj_geom = astra.create_proj_geom('parallel', 1.0, detectors, angles)
+proj_id = astra.create_projector('linear', proj_geom, vol_geom)
+
+# Create a sinogram from a phantom
+sinogram_id, sinogram = astra.create_sino(data.as_array(), proj_id)
+
+# Create a data object for the reconstruction
+rec_id = astra.data2d.create('-vol', vol_geom)
+
+cgls_astra = astra.astra_dict('CGLS')
+cgls_astra['ReconstructionDataId'] = rec_id
+cgls_astra['ProjectionDataId'] = sinogram_id
+cgls_astra['ProjectorId'] = proj_id
+
+# Create the algorithm object from the configuration structure
+alg_id = astra.algorithm.create(cgls_astra)
+
+astra.algorithm.run(alg_id, 1000)
+
+recon_cgls_astra = astra.data2d.get(rec_id)
+
+###############################################################################
+# Setup and run the CGLS algorithm
+x_init = ig.allocate()
+cgls = CGLS(x_init=x_init, operator=Aop, data=noisy_data)
+cgls.max_iteration = 1000
+cgls.update_objective_interval = 200
+cgls.run(1000, verbose=False)
+
+###############################################################################
+# Setup and run the PDHG algorithm
+operator = Aop
+f = L2NormSquared(b = noisy_data)
+g = ZeroFunction()
+
+## Compute operator Norm
+normK = operator.norm()
+
+## Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 1000
+pdhg.update_objective_interval = 200
+pdhg.run(1000, verbose=True)
+
+###############################################################################
+# Setup and run the FISTA algorithm
+fidelity = FunctionOperatorComposition(L2NormSquared(b=noisy_data), Aop)
+regularizer = ZeroFunction()
+
+fista = FISTA(x_init=x_init , f=fidelity, g=regularizer)
+fista.max_iteration = 1000
+fista.update_objective_interval = 200
+fista.run(1000, verbose=True)
+
+#%% Show results
+
+plt.figure(figsize=(10,10))
+plt.suptitle('Reconstructions ', fontsize=16)
+
+plt.subplot(2,2,1)
+plt.imshow(cgls.get_output().as_array())
+plt.colorbar()
+plt.title('CGLS reconstruction')
+
+plt.subplot(2,2,2)
+plt.imshow(fista.get_output().as_array())
+plt.colorbar()
+plt.title('FISTA reconstruction')
+
+plt.subplot(2,2,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.colorbar()
+plt.title('PDHG reconstruction')
+
+plt.subplot(2,2,4)
+plt.imshow(recon_cgls_astra)
+plt.colorbar()
+plt.title('CGLS astra')
+
+diff1 = pdhg.get_output() - cgls.get_output()
+diff2 = fista.get_output() - cgls.get_output()
+diff3 = ImageData(recon_cgls_astra) - cgls.get_output()
+
+plt.figure(figsize=(15,15))
+
+plt.subplot(3,1,1)
+plt.imshow(diff1.abs().as_array())
+plt.title('Diff PDHG vs CGLS')
+plt.colorbar()
+
+plt.subplot(3,1,2)
+plt.imshow(diff2.abs().as_array())
+plt.title('Diff FISTA vs CGLS')
+plt.colorbar()
+
+plt.subplot(3,1,3)
+plt.imshow(diff3.abs().as_array())
+plt.title('Diff CLGS astra vs CGLS')
+plt.colorbar()
+
+
+#%%
+
+print('Primal Objective (FISTA) {} '.format(fista.objective[-1]))
+print('Primal Objective (CGLS) {} '.format(cgls.objective[-1]))
+print('Primal Objective (PDHG) {} '.format(pdhg.objective[-1][0]))
+
+
+true_obj = (Aop.direct(cglsd.get_output())-noisy_data).squared_norm()
+print('True objective {}'.format(true_obj))
+
+
diff --git a/Wrappers/Python/demos/CompareAlgorithms/CGLS_PDHG_Tikhonov.py b/Wrappers/Python/demos/CompareAlgorithms/CGLS_PDHG_Tikhonov.py
new file mode 100644
index 0000000..9b6d10f
--- /dev/null
+++ b/Wrappers/Python/demos/CompareAlgorithms/CGLS_PDHG_Tikhonov.py
@@ -0,0 +1,133 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+Compare solutions of PDHG & "Block CGLS" algorithms for
+
+
+Problem: min_x alpha * ||\grad x ||^{2}_{2} + || A x - g ||_{2}^{2}
+
+
+ A: Projection operator
+ g: Sinogram
+
+"""
+
+
+from ccpi.framework import AcquisitionGeometry, BlockDataContainer, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG, CGLS
+from ccpi.optimisation.operators import BlockOperator, Gradient
+
+from ccpi.optimisation.functions import ZeroFunction, BlockFunction, L2NormSquared
+from ccpi.astra.ops import AstraProjectorSimple
+from ccpi.framework import TestData
+import os, sys
+
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+
+# Create Ground truth phantom and Sinogram
+N = 150
+M = 150
+data = loader.load(TestData.SIMPLE_PHANTOM_2D, size=(N,M), scale=(0,1))
+ig = data.geometry
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+ag = AcquisitionGeometry('parallel','2D', angles, detectors)
+
+device = input('Available device: GPU==1 / CPU==0 ')
+if device=='1':
+ dev = 'gpu'
+else:
+ dev = 'cpu'
+
+Aop = AstraProjectorSimple(ig, ag, dev)
+sin = Aop.direct(data)
+
+noisy_data = AcquisitionData( sin.as_array() + np.random.normal(0,3,ig.shape))
+
+# Setup and run the CGLS algorithm
+alpha = 50
+Grad = Gradient(ig)
+
+# Form Tikhonov as a Block CGLS structure
+op_CGLS = BlockOperator( Aop, alpha * Grad, shape=(2,1))
+block_data = BlockDataContainer(noisy_data, Grad.range_geometry().allocate())
+
+x_init = ig.allocate()
+cgls = CGLS(x_init=x_init, operator=op_CGLS, data=block_data)
+cgls.max_iteration = 1000
+cgls.update_objective_interval = 200
+cgls.run(1000,verbose=False)
+
+
+#Setup and run the PDHG algorithm
+
+# Create BlockOperator
+op_PDHG = BlockOperator(Grad, Aop, shape=(2,1) )
+# Create functions
+f1 = 0.5 * alpha**2 * L2NormSquared()
+f2 = 0.5 * L2NormSquared(b = noisy_data)
+f = BlockFunction(f1, f2)
+g = ZeroFunction()
+
+## Compute operator Norm
+normK = op_PDHG.norm()
+
+## Primal & dual stepsizes
+sigma = 10
+tau = 1/(sigma*normK**2)
+
+pdhg = PDHG(f=f,g=g,operator=op_PDHG, tau=tau, sigma=sigma)
+pdhg.max_iteration = 1000
+pdhg.update_objective_interval = 200
+pdhg.run(1000, verbose=False)
+
+# Show results
+plt.figure(figsize=(10,10))
+
+plt.subplot(2,1,1)
+plt.imshow(cgls.get_output().as_array())
+plt.title('CGLS reconstruction')
+
+plt.subplot(2,1,2)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('PDHG reconstruction')
+
+plt.show()
+
+diff1 = pdhg.get_output() - cgls.get_output()
+
+plt.imshow(diff1.abs().as_array())
+plt.title('Diff PDHG vs CGLS')
+plt.colorbar()
+plt.show()
+
+plt.plot(np.linspace(0,N,M), pdhg.get_output().as_array()[int(N/2),:], label = 'PDHG')
+plt.plot(np.linspace(0,N,M), cgls.get_output().as_array()[int(N/2),:], label = 'CGLS')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
diff --git a/Wrappers/Python/demos/CompareAlgorithms/FISTA_vs_PDHG.py b/Wrappers/Python/demos/CompareAlgorithms/FISTA_vs_PDHG.py
new file mode 100644
index 0000000..c24ebac
--- /dev/null
+++ b/Wrappers/Python/demos/CompareAlgorithms/FISTA_vs_PDHG.py
@@ -0,0 +1,124 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+
+"""
+Compare solutions of FISTA & PDHG algorithms
+
+
+Problem: min_x alpha * ||\grad x ||^{2}_{2} + || x - g ||_{1}
+
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: Noisy Data with Salt & Pepper Noise
+
+"""
+
+from ccpi.framework import ImageData, ImageGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import FISTA, PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient, Identity
+from ccpi.optimisation.functions import L2NormSquared, L1Norm, \
+ FunctionOperatorComposition, BlockFunction, ZeroFunction
+
+from skimage.util import random_noise
+
+
+# Create Ground truth and Noisy data
+N = 100
+
+data = np.zeros((N,N))
+data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+data = ImageData(data)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+ag = ig
+
+n1 = random_noise(data.as_array(), mode = 's&p', salt_vs_pepper = 0.9, amount=0.2)
+noisy_data = ImageData(n1)
+
+# Regularisation Parameter
+alpha = 5
+
+###############################################################################
+# Setup and run the FISTA algorithm
+operator = Gradient(ig)
+fidelity = L1Norm(b=noisy_data)
+regulariser = FunctionOperatorComposition(alpha * L2NormSquared(), operator)
+
+x_init = ig.allocate()
+opt = {'memopt':True}
+fista = FISTA(x_init=x_init , f=regulariser, g=fidelity, opt=opt)
+fista.max_iteration = 2000
+fista.update_objective_interval = 50
+fista.run(2000, verbose=False)
+###############################################################################
+
+
+###############################################################################
+# Setup and run the PDHG algorithm
+op1 = Gradient(ig)
+op2 = Identity(ig, ag)
+
+operator = BlockOperator(op1, op2, shape=(2,1) )
+f = BlockFunction(alpha * L2NormSquared(), fidelity)
+g = ZeroFunction()
+
+normK = operator.norm()
+
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 200
+pdhg.run(2000, verbose=False)
+###############################################################################
+
+# Show results
+
+plt.figure(figsize=(10,10))
+
+plt.subplot(2,1,1)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('PDHG reconstruction')
+
+plt.subplot(2,1,2)
+plt.imshow(fista.get_output().as_array())
+plt.title('FISTA reconstruction')
+
+plt.show()
+
+diff1 = pdhg.get_output() - fista.get_output()
+
+plt.imshow(diff1.abs().as_array())
+plt.title('Diff PDHG vs FISTA')
+plt.colorbar()
+plt.show()
+
+
diff --git a/Wrappers/Python/demos/FISTA_examples/FISTA_CGLS.py b/Wrappers/Python/demos/FISTA_examples/FISTA_CGLS.py
new file mode 100644
index 0000000..1a96a2d
--- /dev/null
+++ b/Wrappers/Python/demos/FISTA_examples/FISTA_CGLS.py
@@ -0,0 +1,215 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+from ccpi.framework import AcquisitionGeometry
+from ccpi.optimisation.algorithms import FISTA
+from ccpi.optimisation.functions import IndicatorBox, ZeroFunction, \
+ L2NormSquared, FunctionOperatorComposition
+from ccpi.astra.operators import AstraProjectorSimple
+
+import numpy as np
+import matplotlib.pyplot as plt
+from ccpi.framework import TestData
+import os, sys
+from ccpi.optimisation.funcs import Norm2sq
+
+# Load Data
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+
+N = 50
+M = 50
+data = loader.load(TestData.SIMPLE_PHANTOM_2D, size=(N,M), scale=(0,1))
+
+ig = data.geometry
+
+# Show Ground Truth
+plt.figure(figsize=(5,5))
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.show()
+
+#%%
+# Set up AcquisitionGeometry object to hold the parameters of the measurement
+# setup geometry: # Number of angles, the actual angles from 0 to
+# pi for parallel beam and 0 to 2pi for fanbeam, set the width of a detector
+# pixel relative to an object pixel, the number of detector pixels, and the
+# source-origin and origin-detector distance (here the origin-detector distance
+# set to 0 to simulate a "virtual detector" with same detector pixel size as
+# object pixel size).
+
+#device = input('Available device: GPU==1 / CPU==0 ')
+
+#if device=='1':
+# dev = 'gpu'
+#else:
+# dev = 'cpu'
+
+test_case = 1
+dev = 'cpu'
+
+if test_case==1:
+
+ detectors = N
+ angles_num = N
+ det_w = 1.0
+
+ angles = np.linspace(0, np.pi, angles_num, endpoint=False)
+ ag = AcquisitionGeometry('parallel',
+ '2D',
+ angles,
+ detectors,det_w)
+
+elif test_case==2:
+
+ SourceOrig = 200
+ OrigDetec = 0
+ angles = np.linspace(0,2*np.pi,angles_num)
+ ag = AcquisitionGeometry('cone',
+ '2D',
+ angles,
+ detectors,
+ det_w,
+ dist_source_center=SourceOrig,
+ dist_center_detector=OrigDetec)
+else:
+ NotImplemented
+
+# Set up Operator object combining the ImageGeometry and AcquisitionGeometry
+# wrapping calls to ASTRA as well as specifying whether to use CPU or GPU.
+Aop = AstraProjectorSimple(ig, ag, dev)
+
+# Forward and backprojection are available as methods direct and adjoint. Here
+# generate test data b and do simple backprojection to obtain z.
+sin = Aop.direct(data)
+back_proj = Aop.adjoint(sin)
+
+plt.figure(figsize=(5,5))
+plt.imshow(sin.array)
+plt.title('Simulated data')
+plt.show()
+
+plt.figure(figsize=(5,5))
+plt.imshow(back_proj.array)
+plt.title('Backprojected data')
+plt.show()
+
+#%%
+
+# Using the test data b, different reconstruction methods can now be set up as
+# demonstrated in the rest of this file. In general all methods need an initial
+# guess and some algorithm options to be set:
+
+f = FunctionOperatorComposition(L2NormSquared(b=sin), Aop)
+#f = Norm2sq(Aop, sin, c=0.5, memopt=True)
+g = ZeroFunction()
+
+x_init = ig.allocate()
+fista = FISTA(x_init=x_init, f=f, g=g)
+fista.max_iteration = 1000
+fista.update_objective_interval = 100
+fista.run(1000, verbose=True)
+
+plt.figure()
+plt.imshow(fista.get_output().as_array())
+plt.title('FISTA unconstrained')
+plt.colorbar()
+plt.show()
+
+
+# Run FISTA for least squares with lower/upper bound
+fista0 = FISTA(x_init=x_init, f=f, g=IndicatorBox(lower=0,upper=1))
+fista0.max_iteration = 1000
+fista0.update_objective_interval = 100
+fista0.run(1000, verbose=True)
+
+plt.figure()
+plt.imshow(fista0.get_output().as_array())
+plt.title('FISTA constrained in [0,1]')
+plt.colorbar()
+plt.show()
+
+#plt.figure()
+#plt.semilogy(fista0.objective)
+#plt.title('FISTA constrained in [0,1]')
+#plt.show()
+
+#%% Check with CVX solution
+
+import astra
+import numpy
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+if cvx_not_installable:
+
+ ##Construct problem
+ u = Variable(N*N)
+
+ # create matrix representation for Astra operator
+ vol_geom = astra.create_vol_geom(N, N)
+ proj_geom = astra.create_proj_geom('parallel', 1.0, detectors, angles)
+
+ proj_id = astra.create_projector('linear', proj_geom, vol_geom)
+
+ matrix_id = astra.projector.matrix(proj_id)
+
+ ProjMat = astra.matrix.get(matrix_id)
+
+ tmp = sin.as_array().ravel()
+
+ fidelity = 0.5 * sum_squares(ProjMat * u - tmp)
+
+ solver = MOSEK
+ obj = Minimize(fidelity)
+ constraints = [u>=0, u<=1]
+ prob = Problem(obj, constraints=constraints)
+ result = prob.solve(verbose = True, solver = solver)
+
+ diff_cvx = numpy.abs( fista0.get_output().as_array() - np.reshape(u.value, (N,M) ))
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(fista0.get_output().as_array())
+ plt.title('FISTA solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(np.reshape(u.value, (N, M)))
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,N,M), fista0.get_output().as_array()[int(N/2),:], label = 'FISTA')
+ plt.plot(np.linspace(0,N,M), np.reshape(u.value, (N,M) )[int(N/2),:], label = 'CVX')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (FISTA) {} '.format(fista0.loss[1])) \ No newline at end of file
diff --git a/Wrappers/Python/demos/FISTA_examples/FISTA_Tikhonov_Poisson_Denoising.py b/Wrappers/Python/demos/FISTA_examples/FISTA_Tikhonov_Poisson_Denoising.py
new file mode 100644
index 0000000..6007990
--- /dev/null
+++ b/Wrappers/Python/demos/FISTA_examples/FISTA_Tikhonov_Poisson_Denoising.py
@@ -0,0 +1,201 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+
+"Tikhonov regularization" for Poisson denoising using FISTA algorithm:
+
+Problem: min_x, x>0 \alpha * ||\nabla x||_{2}^{2} + \int x - g * log(x)
+
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: Noisy Data with Poisson Noise
+
+
+"""
+
+from ccpi.framework import ImageData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import FISTA
+
+from ccpi.optimisation.operators import Gradient
+from ccpi.optimisation.functions import KullbackLeibler, L2NormSquared, FunctionOperatorComposition
+
+from ccpi.framework import TestData
+import os, sys
+from skimage.util import random_noise
+
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+
+# Load Data
+N = 100
+M = 100
+data = loader.load(TestData.SIMPLE_PHANTOM_2D, size=(N,M), scale=(0,1))
+
+ig = data.geometry
+ag = ig
+
+# Create Noisy data with Poisson noise
+n1 = random_noise(data.as_array(), mode = 'poisson', seed = 10)
+noisy_data = ImageData(n1)
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,10))
+plt.subplot(2,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(2,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+# Regularisation Parameter
+alpha = 10
+
+# Setup and run the FISTA algorithm
+operator = Gradient(ig)
+fid = KullbackLeibler(noisy_data)
+
+def KL_Prox_PosCone(x, tau, out=None):
+
+ if out is None:
+ tmp = 0.5 *( (x - fid.bnoise - tau) + ( (x + fid.bnoise - tau)**2 + 4*tau*fid.b ) .sqrt() )
+ return tmp.maximum(0)
+ else:
+ tmp = 0.5 *( (x - fid.bnoise - tau) +
+ ( (x + fid.bnoise - tau)**2 + 4*tau*fid.b ) .sqrt()
+ )
+ x.add(fid.bnoise, out=out)
+ out -= tau
+ out *= out
+ tmp = fid.b * (4 * tau)
+ out.add(tmp, out=out)
+ out.sqrt(out=out)
+
+ x.subtract(fid.bnoise, out=tmp)
+ tmp -= tau
+
+ out += tmp
+
+ out *= 0.5
+
+ # ADD the constraint here
+ out.maximum(0, out=out)
+
+fid.proximal = KL_Prox_PosCone
+
+reg = FunctionOperatorComposition(alpha * L2NormSquared(), operator)
+
+x_init = ig.allocate()
+fista = FISTA(x_init=x_init , f=reg, g=fid)
+fista.max_iteration = 2000
+fista.update_objective_interval = 500
+fista.run(2000, verbose=True)
+
+# Show results
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(fista.get_output().as_array())
+plt.title('Reconstruction')
+plt.colorbar()
+plt.show()
+
+plt.plot(np.linspace(0,N,M), data.as_array()[int(N/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,N,M), fista.get_output().as_array()[int(N/2),:], label = 'Reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
+#%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+
+if cvx_not_installable:
+
+ ##Construct problem
+ u1 = Variable(ig.shape)
+ q = Variable()
+
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ regulariser = alpha * sum_squares(norm(vstack([DX.matrix() * vec(u1), DY.matrix() * vec(u1)]), 2, axis = 0))
+ fidelity = sum(kl_div(noisy_data.as_array(), u1))
+
+ constraints = [q>=fidelity, u1>=0]
+
+ solver = SCS
+ obj = Minimize( regulariser + q)
+ prob = Problem(obj, constraints)
+ result = prob.solve(verbose = True, solver = solver)
+
+ diff_cvx = numpy.abs( fista.get_output().as_array() - u1.value )
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(fista.get_output().as_array())
+ plt.title('FISTA solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(u1.value)
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,N,M), fista.get_output().as_array()[int(N/2),:], label = 'FISTA')
+ plt.plot(np.linspace(0,N,M), u1.value[int(N/2),:], label = 'CVX')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (FISTA) {} '.format(fista.loss[1]))
+
+
+
diff --git a/Wrappers/Python/demos/PDHG_examples/ColorbayDemo.py b/Wrappers/Python/demos/PDHG_examples/ColorbayDemo.py
new file mode 100644
index 0000000..e69060f
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/ColorbayDemo.py
@@ -0,0 +1,273 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+
+from ccpi.framework import ImageGeometry, ImageData, AcquisitionGeometry, AcquisitionData, BlockDataContainer
+
+import numpy as numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG, CGLS
+from ccpi.optimisation.algs import CGLS as CGLS_old
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.operators import AstraProjectorMC
+from scipy.io import loadmat
+import h5py
+
+#%%
+
+phantom = 'powder'
+
+if phantom == 'carbon':
+ pathname = '/media/newhd/shared/Data/ColourBay/spectral_data_sets/CarbonPd/'
+ filename = 'carbonPd_full_sinogram_stripes_removed.mat'
+ X = loadmat(pathname + filename)
+ X = numpy.transpose(X['SS'],(3,1,2,0))
+ X = X[80:100] # delete this to take all channels
+
+elif phantom == 'powder':
+ pathname = '/media/newhd/shared/DataProcessed/'
+ filename = 'S_180.mat'
+ path = pathname + filename
+ arrays = {}
+ f = h5py.File(path)
+ for k, v in f.items():
+ arrays[k] = numpy.array(v)
+ XX = arrays['S']
+ X = numpy.transpose(XX,(0,2,1,3))
+ X = X[100:120]
+
+
+#%% Setup Geometry of Colorbay
+
+num_channels = X.shape[0]
+num_pixels_h = X.shape[3]
+num_pixels_v = X.shape[2]
+num_angles = X.shape[1]
+
+# Display a single projection in a single channel
+plt.imshow(X[5,5,:,:])
+plt.title('Example of a projection image in one channel' )
+plt.show()
+
+# Set angles to use
+angles = numpy.linspace(-numpy.pi,numpy.pi,num_angles,endpoint=False)
+
+# Define full 3D acquisition geometry and data container.
+# Geometric info is taken from the txt-file in the same dir as the mat-file
+ag = AcquisitionGeometry('cone',
+ '3D',
+ angles,
+ pixel_num_h=num_pixels_h,
+ pixel_size_h=0.25,
+ pixel_num_v=num_pixels_v,
+ pixel_size_v=0.25,
+ dist_source_center=233.0,
+ dist_center_detector=245.0,
+ channels=num_channels)
+data = AcquisitionData(X, geometry=ag)
+
+# Reduce to central slice by extracting relevant parameters from data and its
+# geometry. Perhaps create function to extract central slice automatically?
+data2d = data.subset(vertical=40)
+ag2d = AcquisitionGeometry('cone',
+ '2D',
+ ag.angles,
+ pixel_num_h=ag.pixel_num_h,
+ pixel_size_h=ag.pixel_size_h,
+ pixel_num_v=1,
+ pixel_size_v=ag.pixel_size_h,
+ dist_source_center=ag.dist_source_center,
+ dist_center_detector=ag.dist_center_detector,
+ channels=ag.channels)
+data2d.geometry = ag2d
+
+# Set up 2D Image Geometry.
+# First need the geometric magnification to scale the voxel size relative
+# to the detector pixel size.
+mag = (ag.dist_source_center + ag.dist_center_detector)/ag.dist_source_center
+ig2d = ImageGeometry(voxel_num_x=ag2d.pixel_num_h,
+ voxel_num_y=ag2d.pixel_num_h,
+ voxel_size_x=ag2d.pixel_size_h/mag,
+ voxel_size_y=ag2d.pixel_size_h/mag,
+ channels=X.shape[0])
+
+# Create GPU multichannel projector/backprojector operator with ASTRA.
+Aall = AstraProjectorMC(ig2d,ag2d,'gpu')
+
+# Compute and simple backprojction and display one channel as image.
+Xbp = Aall.adjoint(data2d)
+plt.imshow(Xbp.subset(channel=5).array)
+plt.show()
+
+#%% CGLS
+
+def callback(iteration, objective, x):
+ plt.imshow(x.as_array()[5])
+ plt.colorbar()
+ plt.show()
+
+x_init = ig2d.allocate()
+cgls1 = CGLS(x_init=x_init, operator=Aall, data=data2d)
+cgls1.max_iteration = 100
+cgls1.update_objective_interval = 1
+cgls1.run(5,verbose=True, callback = callback)
+
+plt.imshow(cgls1.get_output().subset(channel=5).array)
+plt.title('CGLS')
+plt.show()
+
+#%% Tikhonov
+
+alpha = 2.5
+Grad = Gradient(ig2d, correlation=Gradient.CORRELATION_SPACE) # use also CORRELATION_SPACECHANNEL
+
+# Form Tikhonov as a Block CGLS structure
+op_CGLS = BlockOperator( Aall, alpha * Grad, shape=(2,1))
+block_data = BlockDataContainer(data2d, Grad.range_geometry().allocate())
+
+cgls2 = CGLS(x_init=x_init, operator=op_CGLS, data=block_data)
+cgls2.max_iteration = 100
+cgls2.update_objective_interval = 1
+
+cgls2.run(10,verbose=True, callback=callback)
+
+plt.imshow(cgls2.get_output().subset(channel=5).array)
+plt.title('Tikhonov')
+plt.show()
+
+#%% Total Variation
+
+# Regularisation Parameter
+#alpha_TV = 0.08 # for carbon
+alpha_TV = 0.08 # for powder
+
+# Create operators
+op1 = Gradient(ig2d, correlation=Gradient.CORRELATION_SPACE)
+op2 = Aall
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+f1 = alpha_TV * MixedL21Norm()
+f2 = 0.5 * L2NormSquared(b=data2d)
+f = BlockFunction(f1, f2)
+g = ZeroFunction()
+
+# Compute operator Norm
+#normK = 8.70320267279591 # For powder Run one time no need to compute again takes time
+normK = 14.60320657253632 # for carbon
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 100
+pdhg.run(1000, verbose =True, callback=callback)
+
+
+#%% Show sinograms
+channel_ind = [10,15,19]
+
+plt.figure(figsize=(15,15))
+
+plt.subplot(4,3,1)
+plt.imshow(data2d.subset(channel = channel_ind[0]).as_array())
+plt.title('Channel {}'.format(channel_ind[0]))
+plt.colorbar()
+
+plt.subplot(4,3,2)
+plt.imshow(data2d.subset(channel = channel_ind[1]).as_array())
+plt.title('Channel {}'.format(channel_ind[1]))
+plt.colorbar()
+
+plt.subplot(4,3,3)
+plt.imshow(data2d.subset(channel = channel_ind[2]).as_array())
+plt.title('Channel {}'.format(channel_ind[2]))
+plt.colorbar()
+
+###############################################################################
+# Show CGLS
+plt.subplot(4,3,4)
+plt.imshow(cgls1.get_output().subset(channel = channel_ind[0]).as_array())
+plt.colorbar()
+
+plt.subplot(4,3,5)
+plt.imshow(cgls1.get_output().subset(channel = channel_ind[1]).as_array())
+plt.colorbar()
+
+plt.subplot(4,3,6)
+plt.imshow(cgls1.get_output().subset(channel = channel_ind[2]).as_array())
+plt.colorbar()
+
+###############################################################################
+# Show Tikhonov
+
+plt.subplot(4,3,7)
+plt.imshow(cgls2.get_output().subset(channel = channel_ind[0]).as_array())
+plt.colorbar()
+
+plt.subplot(4,3,8)
+plt.imshow(cgls2.get_output().subset(channel = channel_ind[1]).as_array())
+plt.colorbar()
+
+plt.subplot(4,3,9)
+plt.imshow(cgls2.get_output().subset(channel = channel_ind[2]).as_array())
+plt.colorbar()
+
+
+###############################################################################
+# Show Total variation
+
+plt.subplot(4,3,10)
+plt.imshow(pdhg.get_output().subset(channel = channel_ind[0]).as_array())
+plt.colorbar()
+
+plt.subplot(4,3,11)
+plt.imshow(pdhg.get_output().subset(channel = channel_ind[1]).as_array())
+plt.colorbar()
+
+plt.subplot(4,3,12)
+plt.imshow(pdhg.get_output().subset(channel = channel_ind[2]).as_array())
+plt.colorbar()
+
+
+###############################################################################
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TGV_Denoising.py b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TGV_Denoising.py
new file mode 100755
index 0000000..9dbcf3e
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TGV_Denoising.py
@@ -0,0 +1,282 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+"""
+
+Total Generalised Variation (TGV) Denoising using PDHG algorithm:
+
+
+Problem: min_{u} \alpha * ||\nabla u - w||_{2,1} +
+ \beta * || E u ||_{2,1} +
+ Fidelity(u, g)
+
+ \nabla: Gradient operator
+ E: Symmetrized Gradient operator
+ \alpha: Regularization parameter
+ \beta: Regularization parameter
+
+ g: Noisy Data
+
+ Fidelity = 1) L2NormSquarred ( \frac{1}{2} * || u - g ||_{2}^{2} ) if Noise is Gaussian
+ 2) L1Norm ( ||u - g||_{1} )if Noise is Salt & Pepper
+ 3) Kullback Leibler (\int u - g * log(u) + Id_{u>0}) if Noise is Poisson
+
+
+ Method = 0 ( PDHG - split ) : K = [ \nabla, - Identity
+ ZeroOperator, E
+ Identity, ZeroOperator]
+
+
+ Method = 1 (PDHG - explicit ): K = [ \nabla, - Identity
+ ZeroOperator, E ]
+
+ Default: TGV denoising
+ noise = Gaussian
+ Fidelity = L2NormSquarred
+ method = 0
+
+"""
+
+from ccpi.framework import ImageData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Identity, \
+ Gradient, SymmetrizedGradient, ZeroOperator
+from ccpi.optimisation.functions import ZeroFunction, L1Norm, \
+ MixedL21Norm, BlockFunction, KullbackLeibler, L2NormSquared
+
+from ccpi.framework import TestData
+import os, sys
+if int(numpy.version.version.split('.')[1]) > 12:
+ from skimage.util import random_noise
+else:
+ from demoutil import random_noise
+
+# user supplied input
+if len(sys.argv) > 1:
+ which_noise = int(sys.argv[1])
+else:
+ which_noise = 0
+print ("Applying {} noise")
+
+if len(sys.argv) > 2:
+ method = sys.argv[2]
+else:
+ method = '0'
+print ("method ", method)
+
+
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+data = loader.load(TestData.SHAPES)
+ig = data.geometry
+ag = ig
+
+# Create noisy data.
+noises = ['gaussian', 'poisson', 's&p']
+noise = noises[which_noise]
+if noise == 's&p':
+ n1 = random_noise(data.as_array(), mode = noise, salt_vs_pepper = 0.9, amount=0.2, seed=10)
+elif noise == 'poisson':
+ scale = 5
+ n1 = random_noise( data.as_array()/scale, mode = noise, seed = 10)*scale
+elif noise == 'gaussian':
+ n1 = random_noise(data.as_array(), mode = noise, seed = 10)
+else:
+ raise ValueError('Unsupported Noise ', noise)
+noisy_data = ImageData(n1)
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,5))
+plt.subplot(1,2,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(1,2,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+# Regularisation Parameter depending on the noise distribution
+if noise == 's&p':
+ alpha = 0.8
+elif noise == 'poisson':
+ alpha = .3
+elif noise == 'gaussian':
+ alpha = .2
+
+# TODO add ref why this choice
+beta = 2 * alpha
+
+# Fidelity
+if noise == 's&p':
+ f3 = L1Norm(b=noisy_data)
+elif noise == 'poisson':
+ f3 = KullbackLeibler(noisy_data)
+elif noise == 'gaussian':
+ f3 = 0.5 * L2NormSquared(b=noisy_data)
+
+if method == '0':
+
+ # Create operators
+ op11 = Gradient(ig)
+ op12 = Identity(op11.range_geometry())
+
+ op22 = SymmetrizedGradient(op11.domain_geometry())
+ op21 = ZeroOperator(ig, op22.range_geometry())
+
+ op31 = Identity(ig, ag)
+ op32 = ZeroOperator(op22.domain_geometry(), ag)
+
+ operator = BlockOperator(op11, -1*op12, op21, op22, op31, op32, shape=(3,2) )
+
+ f1 = alpha * MixedL21Norm()
+ f2 = beta * MixedL21Norm()
+
+ f = BlockFunction(f1, f2, f3)
+ g = ZeroFunction()
+
+else:
+
+ # Create operators
+ op11 = Gradient(ig)
+ op12 = Identity(op11.range_geometry())
+ op22 = SymmetrizedGradient(op11.domain_geometry())
+ op21 = ZeroOperator(ig, op22.range_geometry())
+
+ operator = BlockOperator(op11, -1*op12, op21, op22, shape=(2,2) )
+
+ f1 = alpha * MixedL21Norm()
+ f2 = beta * MixedL21Norm()
+
+ f = BlockFunction(f1, f2)
+ g = BlockFunction(f3, ZeroFunction())
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 100
+pdhg.run(2000)
+
+# Show results
+plt.figure(figsize=(20,5))
+plt.subplot(1,4,1)
+plt.imshow(data.subset(channel=0).as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(1,4,2)
+plt.imshow(noisy_data.subset(channel=0).as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(1,4,3)
+plt.imshow(pdhg.get_output()[0].as_array())
+plt.title('TGV Reconstruction')
+plt.colorbar()
+plt.subplot(1,4,4)
+plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), data.as_array()[int(ig.shape[0]/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output()[0].as_array()[int(ig.shape[0]/2),:], label = 'TGV reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+#%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+if cvx_not_installable:
+
+ u = Variable(ig.shape)
+ w1 = Variable(ig.shape)
+ w2 = Variable(ig.shape)
+
+ # create TGV regulariser
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ regulariser = alpha * sum(norm(vstack([DX.matrix() * vec(u) - vec(w1), \
+ DY.matrix() * vec(u) - vec(w2)]), 2, axis = 0)) + \
+ beta * sum(norm(vstack([ DX.matrix().transpose() * vec(w1), DY.matrix().transpose() * vec(w2), \
+ 0.5 * ( DX.matrix().transpose() * vec(w2) + DY.matrix().transpose() * vec(w1) ), \
+ 0.5 * ( DX.matrix().transpose() * vec(w2) + DY.matrix().transpose() * vec(w1) ) ]), 2, axis = 0 ) )
+
+ constraints = []
+
+ # choose solver
+ if 'MOSEK' in installed_solvers():
+ solver = MOSEK
+ else:
+ solver = SCS
+
+ # fidelity
+ if noise == 's&p':
+ fidelity = pnorm( u - noisy_data.as_array(),1)
+ elif noise == 'poisson':
+ fidelity = sum(kl_div(noisy_data.as_array(), u))
+ solver = SCS
+ elif noise == 'gaussian':
+ fidelity = 0.5 * sum_squares(noisy_data.as_array() - u)
+
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj)
+ result = prob.solve(verbose = True, solver = solver)
+
+ diff_cvx = numpy.abs( pdhg.get_output()[0].as_array() - u.value )
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(pdhg.get_output()[0].as_array())
+ plt.title('PDHG solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(u.value)
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output()[0].as_array()[int(ig.shape[0]/2),:], label = 'PDHG')
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), u.value[int(ig.shape[0]/2),:], label = 'CVX')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (PDHG) {} '.format(pdhg.objective[-1][0])) \ No newline at end of file
diff --git a/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising.py b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising.py
new file mode 100755
index 0000000..6937fa0
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising.py
@@ -0,0 +1,280 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+
+Total Variation Denoising using PDHG algorithm:
+
+
+Problem: min_{u}, \alpha * ||\nabla u||_{2,1} + Fidelity(u, g)
+
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: Noisy Data
+
+ Fidelity = 1) L2NormSquarred ( \frac{1}{2} * || u - g ||_{2}^{2} ) if Noise is Gaussian
+ 2) L1Norm ( ||u - g||_{1} )if Noise is Salt & Pepper
+ 3) Kullback Leibler (\int u - g * log(u) + Id_{u>0}) if Noise is Poisson
+
+ Method = 0 ( PDHG - split ) : K = [ \nabla,
+ Identity]
+
+
+ Method = 1 (PDHG - explicit ): K = \nabla
+
+
+ Default: ROF denoising
+ noise = Gaussian
+ Fidelity = L2NormSquarred
+ method = 0
+
+
+"""
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L1Norm, \
+ MixedL21Norm, BlockFunction, L2NormSquared,\
+ KullbackLeibler
+from ccpi.framework import TestData
+import os, sys
+#import scipy.io
+if int(numpy.version.version.split('.')[1]) > 12:
+ from skimage.util import random_noise
+else:
+ from demoutil import random_noise
+
+# user supplied input
+if len(sys.argv) > 1:
+ which_noise = int(sys.argv[1])
+else:
+ which_noise = 0
+print ("Applying {} noise")
+
+if len(sys.argv) > 2:
+ method = sys.argv[2]
+else:
+ method = '0'
+print ("method ", method)
+
+
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+data = loader.load(TestData.SHAPES, size=(50,50))
+ig = data.geometry
+ag = ig
+
+# Create noisy data.
+noises = ['gaussian', 'poisson', 's&p']
+noise = noises[which_noise]
+if noise == 's&p':
+ n1 = random_noise(data.as_array(), mode = noise, salt_vs_pepper = 0.9, amount=0.2)
+elif noise == 'poisson':
+ scale = 5
+ n1 = random_noise( data.as_array()/scale, mode = noise, seed = 10)*scale
+elif noise == 'gaussian':
+ n1 = random_noise(data.as_array(), mode = noise, seed = 10)
+else:
+ raise ValueError('Unsupported Noise ', noise)
+noisy_data = ig.allocate()
+noisy_data.fill(n1)
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,5))
+plt.subplot(1,2,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(1,2,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+
+# Regularisation Parameter depending on the noise distribution
+if noise == 's&p':
+ alpha = 0.8
+elif noise == 'poisson':
+ alpha = 1
+elif noise == 'gaussian':
+ alpha = .3
+
+# fidelity
+if noise == 's&p':
+ f2 = L1Norm(b=noisy_data)
+elif noise == 'poisson':
+ f2 = KullbackLeibler(noisy_data)
+elif noise == 'gaussian':
+ f2 = 0.5 * L2NormSquared(b=noisy_data)
+
+if method == '0':
+
+ # Create operators
+ op1 = Gradient(ig, correlation=Gradient.CORRELATION_SPACE)
+ op2 = Identity(ig, ag)
+
+ # Create BlockOperator
+ operator = BlockOperator(op1, op2, shape=(2,1) )
+
+ # Create functions
+ f = BlockFunction(alpha * MixedL21Norm(), f2)
+ g = ZeroFunction()
+
+else:
+
+ operator = Gradient(ig)
+ f = alpha * MixedL21Norm()
+ g = f2
+
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 100
+pdhg.run(2000)
+
+
+if data.geometry.channels > 1:
+ plt.figure(figsize=(20,15))
+ for row in range(data.geometry.channels):
+
+ plt.subplot(3,4,1+row*4)
+ plt.imshow(data.subset(channel=row).as_array())
+ plt.title('Ground Truth')
+ plt.colorbar()
+ plt.subplot(3,4,2+row*4)
+ plt.imshow(noisy_data.subset(channel=row).as_array())
+ plt.title('Noisy Data')
+ plt.colorbar()
+ plt.subplot(3,4,3+row*4)
+ plt.imshow(pdhg.get_output().subset(channel=row).as_array())
+ plt.title('TV Reconstruction')
+ plt.colorbar()
+ plt.subplot(3,4,4+row*4)
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), data.subset(channel=row).as_array()[int(N/2),:], label = 'GTruth')
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output().subset(channel=row).as_array()[int(N/2),:], label = 'TV reconstruction')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+else:
+ plt.figure(figsize=(20,5))
+ plt.subplot(1,4,1)
+ plt.imshow(data.subset(channel=0).as_array())
+ plt.title('Ground Truth')
+ plt.colorbar()
+ plt.subplot(1,4,2)
+ plt.imshow(noisy_data.subset(channel=0).as_array())
+ plt.title('Noisy Data')
+ plt.colorbar()
+ plt.subplot(1,4,3)
+ plt.imshow(pdhg.get_output().subset(channel=0).as_array())
+ plt.title('TV Reconstruction')
+ plt.colorbar()
+ plt.subplot(1,4,4)
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), data.as_array()[int(ig.shape[0]/2),:], label = 'GTruth')
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output().as_array()[int(ig.shape[0]/2),:], label = 'TV reconstruction')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+
+#%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+
+if cvx_not_installable:
+
+ ##Construct problem
+ u = Variable(ig.shape)
+
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ # Define Total Variation as a regulariser
+ regulariser = alpha * sum(norm(vstack([Constant(DX.matrix()) * vec(u), Constant(DY.matrix()) * vec(u)]), 2, axis = 0))
+
+ # choose solver
+ if 'MOSEK' in installed_solvers():
+ solver = MOSEK
+ else:
+ solver = SCS
+
+ # fidelity
+ if noise == 's&p':
+ fidelity = pnorm( u - noisy_data.as_array(),1)
+ elif noise == 'poisson':
+ fidelity = sum(kl_div(noisy_data.as_array(), u))
+ solver = SCS
+ elif noise == 'gaussian':
+ fidelity = 0.5 * sum_squares(noisy_data.as_array() - u)
+
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj)
+ result = prob.solve(verbose = True, solver = solver)
+
+ diff_cvx = numpy.abs( pdhg.get_output().as_array() - u.value )
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(pdhg.get_output().as_array())
+ plt.title('PDHG solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(u.value)
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output().as_array()[int(ig.shape[0]/2),:], label = 'PDHG')
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), u.value[int(ig.shape[0]/2),:], label = 'CVX')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (PDHG) {} '.format(pdhg.objective[-1][0]))
diff --git a/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_2D_time.py b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_2D_time.py
new file mode 100644
index 0000000..febe76d
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_2D_time.py
@@ -0,0 +1,243 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+"""
+
+Total Variation (Dynamic) Denoising using PDHG algorithm and Tomophantom:
+
+
+Problem: min_{x} \alpha * ||\nabla x||_{2,1} + \frac{1}{2} * || x - g ||_{2}^{2}
+
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: 2D Dynamic noisy data with Gaussian Noise
+
+ K = \nabla
+
+"""
+
+from ccpi.framework import ImageData, ImageGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient, Identity
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorMC
+
+import os
+import tomophantom
+from tomophantom import TomoP2D
+
+# Create phantom for TV 2D dynamic tomography
+
+model = 102 # note that the selected model is temporal (2D + time)
+N = 128 # set dimension of the phantom
+
+path = os.path.dirname(tomophantom.__file__)
+path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
+phantom_2Dt = TomoP2D.ModelTemporal(model, N, path_library2D)
+
+plt.close('all')
+plt.figure(1)
+plt.rcParams.update({'font.size': 21})
+plt.title('{}''{}'.format('2D+t phantom using model no.',model))
+for sl in range(0,np.shape(phantom_2Dt)[0]):
+ im = phantom_2Dt[sl,:,:]
+ plt.imshow(im, vmin=0, vmax=1)
+# plt.pause(.1)
+# plt.draw
+
+# Setup geometries
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N, channels = np.shape(phantom_2Dt)[0])
+data = ImageData(phantom_2Dt, geometry=ig)
+ag = ig
+
+# Create noisy data. Apply Gaussian noise
+np.random.seed(10)
+noisy_data = ImageData( data.as_array() + np.random.normal(0, 0.25, size=ig.shape) )
+
+# time-frames index
+tindex = [8, 16, 24]
+
+fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 10))
+plt.subplot(1,3,1)
+plt.imshow(noisy_data.as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+plt.subplot(1,3,2)
+plt.imshow(noisy_data.as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+plt.subplot(1,3,3)
+plt.imshow(noisy_data.as_array()[tindex[2],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+plt.show()
+
+# Regularisation Parameter
+alpha = 0.3
+
+# Create operators
+op1 = Gradient(ig, correlation='Space')
+op2 = Gradient(ig, correlation='SpaceChannels')
+op3 = Identity(ig, ag)
+
+# Create BlockOperator
+operator1 = BlockOperator(op1, op3, shape=(2,1) )
+operator2 = BlockOperator(op2, op3, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = 0.5 * L2NormSquared(b = noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+# Compute operator Norm
+normK1 = operator1.norm()
+normK2 = operator2.norm()
+
+#%%
+# Primal & dual stepsizes
+sigma1 = 1
+tau1 = 1/(sigma1*normK1**2)
+
+sigma2 = 1
+tau2 = 1/(sigma2*normK2**2)
+
+# Setup and run the PDHG algorithm
+pdhg1 = PDHG(f=f,g=g,operator=operator1, tau=tau1, sigma=sigma1)
+pdhg1.max_iteration = 2000
+pdhg1.update_objective_interval = 200
+pdhg1.run(2000)
+
+# Setup and run the PDHG algorithm
+pdhg2 = PDHG(f=f,g=g,operator=operator2, tau=tau2, sigma=sigma2)
+pdhg2.max_iteration = 2000
+pdhg2.update_objective_interval = 200
+pdhg2.run(2000)
+
+
+#%%
+
+tindex = [8, 16, 24]
+fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8))
+
+plt.subplot(3,3,1)
+plt.imshow(phantom_2Dt[tindex[0],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+
+plt.subplot(3,3,2)
+plt.imshow(phantom_2Dt[tindex[1],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+
+plt.subplot(3,3,3)
+plt.imshow(phantom_2Dt[tindex[2],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+plt.subplot(3,3,4)
+plt.imshow(pdhg1.get_output().as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.subplot(3,3,5)
+plt.imshow(pdhg1.get_output().as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.subplot(3,3,6)
+plt.imshow(pdhg1.get_output().as_array()[tindex[2],:,:])
+plt.axis('off')
+
+
+plt.subplot(3,3,7)
+plt.imshow(pdhg2.get_output().as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.subplot(3,3,8)
+plt.imshow(pdhg2.get_output().as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.subplot(3,3,9)
+plt.imshow(pdhg2.get_output().as_array()[tindex[2],:,:])
+plt.axis('off')
+
+
+im = plt.imshow(pdhg1.get_output().as_array()[tindex[0],:,:])
+
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
+cbar = fig.colorbar(im, cax=cb_ax)
+plt.show()
+
+#%%
+import matplotlib.animation as animation
+fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 30))
+ims1 = []
+ims2 = []
+ims3 = []
+for sl in range(0,np.shape(phantom_2Dt)[0]):
+
+ plt.subplot(1,3,1)
+ im1 = plt.imshow(phantom_2Dt[sl,:,:], animated=True)
+
+ plt.subplot(1,3,2)
+ im2 = plt.imshow(pdhg1.get_output().as_array()[sl,:,:])
+
+ plt.subplot(1,3,3)
+ im3 = plt.imshow(pdhg2.get_output().as_array()[sl,:,:])
+
+ ims1.append([im1])
+ ims2.append([im2])
+ ims3.append([im3])
+
+
+ani1 = animation.ArtistAnimation(fig, ims1, interval=500,
+ repeat_delay=10)
+
+ani2 = animation.ArtistAnimation(fig, ims2, interval=500,
+ repeat_delay=10)
+
+ani3 = animation.ArtistAnimation(fig, ims3, interval=500,
+ repeat_delay=10)
+plt.show()
+# plt.pause(0.25)
+# plt.show()
+
+
+
+
+
+
+
+
diff --git a/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_Gaussian_3D.py b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_Gaussian_3D.py
new file mode 100644
index 0000000..15709cd
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Denoising_Gaussian_3D.py
@@ -0,0 +1,147 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+"""
+
+Total Variation (3D) Denoising using PDHG algorithm and Tomophantom:
+
+
+Problem: min_{x} \alpha * ||\nabla x||_{2,1} + \frac{1}{2} * || x - g ||_{2}^{2}
+
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: 3D Noisy Data with Gaussian Noise
+
+ Method = 0 ( PDHG - split ) : K = [ \nabla,
+ Identity]
+
+
+ Method = 1 (PDHG - explicit ): K = \nabla
+
+"""
+
+from ccpi.framework import ImageData, ImageGeometry
+import matplotlib.pyplot as plt
+from ccpi.optimisation.algorithms import PDHG
+from ccpi.optimisation.operators import Gradient
+from ccpi.optimisation.functions import L2NormSquared, MixedL21Norm
+
+from skimage.util import random_noise
+
+import timeit
+import os
+from tomophantom import TomoP3D
+import tomophantom
+
+# Create a phantom from Tomophantom
+print ("Building 3D phantom using TomoPhantom software")
+tic=timeit.default_timer()
+model = 13 # select a model number from the library
+N = 64 # Define phantom dimensions using a scalar value (cubic phantom)
+path = os.path.dirname(tomophantom.__file__)
+path_library3D = os.path.join(path, "Phantom3DLibrary.dat")
+
+#This will generate a N x N x N phantom (3D)
+phantom_tm = TomoP3D.Model(model, N, path_library3D)
+
+# Create noisy data. Apply Gaussian noise
+ig = ImageGeometry(voxel_num_x=N, voxel_num_y=N, voxel_num_z=N)
+ag = ig
+n1 = random_noise(phantom_tm, mode = 'gaussian', mean=0, var = 0.001, seed=10)
+noisy_data = ImageData(n1)
+
+# Show results
+sliceSel = int(0.5*N)
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(noisy_data.as_array()[sliceSel,:,:],vmin=0, vmax=1)
+plt.title('Axial View')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array()[:,sliceSel,:],vmin=0, vmax=1)
+plt.title('Coronal View')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(noisy_data.as_array()[:,:,sliceSel],vmin=0, vmax=1)
+plt.title('Sagittal View')
+plt.colorbar()
+plt.show()
+
+# Regularisation Parameter
+alpha = 0.05
+
+# Setup and run the PDHG algorithm
+operator = Gradient(ig)
+f = alpha * MixedL21Norm()
+g = 0.5 * L2NormSquared(b = noisy_data)
+
+normK = operator.norm()
+
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 1000
+pdhg.update_objective_interval = 200
+pdhg.run(1000, verbose = True)
+
+# Show results
+fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8))
+fig.suptitle('TV Reconstruction',fontsize=20)
+
+plt.subplot(2,3,1)
+plt.imshow(noisy_data.as_array()[sliceSel,:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Axial View')
+
+plt.subplot(2,3,2)
+plt.imshow(noisy_data.as_array()[:,sliceSel,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Coronal View')
+
+plt.subplot(2,3,3)
+plt.imshow(noisy_data.as_array()[:,:,sliceSel],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Sagittal View')
+
+
+plt.subplot(2,3,4)
+plt.imshow(pdhg.get_output().as_array()[sliceSel,:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.subplot(2,3,5)
+plt.imshow(pdhg.get_output().as_array()[:,sliceSel,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.subplot(2,3,6)
+plt.imshow(pdhg.get_output().as_array()[:,:,sliceSel],vmin=0, vmax=1)
+plt.axis('off')
+im = plt.imshow(pdhg.get_output().as_array()[:,:,sliceSel],vmin=0, vmax=1)
+
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
+cbar = fig.colorbar(im, cax=cb_ax)
+
+
+plt.show()
+
diff --git a/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Tomo2D.py b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Tomo2D.py
new file mode 100644
index 0000000..4f7639e
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_TV_Tomo2D.py
@@ -0,0 +1,173 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+
+Total Variation 2D Tomography Reconstruction using PDHG algorithm:
+
+
+Problem: min_u \alpha * ||\nabla u||_{2,1} + \frac{1}{2}||Au - g||^{2}
+ min_u, u>0 \alpha * ||\nabla u||_{2,1} + \int A u - g log (Au + \eta)
+
+ \nabla: Gradient operator
+ A: System Matrix
+ g: Noisy sinogram
+ \eta: Background noise
+
+ \alpha: Regularization parameter
+
+"""
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction, KullbackLeibler, IndicatorBox
+
+from ccpi.astra.ops import AstraProjectorSimple
+from ccpi.framework import TestData
+from PIL import Image
+import os, sys
+#if int(numpy.version.version.split('.')[1]) > 12:
+from skimage.util import random_noise
+#else:
+# from demoutil import random_noise
+
+#import scipy.io
+
+# user supplied input
+if len(sys.argv) > 1:
+ which_noise = int(sys.argv[1])
+else:
+ which_noise = 1
+
+# Load 256 shepp-logan
+data256 = scipy.io.loadmat('phantom.mat')['phantom256']
+data = ImageData(numpy.array(Image.fromarray(data256).resize((256,256))))
+N, M = data.shape
+ig = ImageGeometry(voxel_num_x=N, voxel_num_y=M)
+
+# Add it to testdata or use tomophantom
+#loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+#data = loader.load(TestData.SIMPLE_PHANTOM_2D, size=(50, 50))
+#ig = data.geometry
+
+# Create acquisition data and geometry
+detectors = N
+angles = np.linspace(0, np.pi, 180)
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+
+# Select device
+device = '0'
+#device = input('Available device: GPU==1 / CPU==0 ')
+if device=='1':
+ dev = 'gpu'
+else:
+ dev = 'cpu'
+
+Aop = AstraProjectorSimple(ig, ag, dev)
+sin = Aop.direct(data)
+
+# Create noisy data. Apply Gaussian noise
+noises = ['gaussian', 'poisson']
+noise = noises[which_noise]
+
+if noise == 'poisson':
+ scale = 5
+ eta = 0
+ noisy_data = AcquisitionData(np.random.poisson( scale * (eta + sin.as_array()))/scale, ag)
+elif noise == 'gaussian':
+ n1 = np.random.normal(0, 1, size = ag.shape)
+ noisy_data = AcquisitionData(n1 + sin.as_array(), ag)
+else:
+ raise ValueError('Unsupported Noise ', noise)
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,10))
+plt.subplot(1,2,2)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(1,2,1)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+# Create operators
+op1 = Gradient(ig)
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Create functions
+if noise == 'poisson':
+ alpha = 3
+ f2 = KullbackLeibler(noisy_data)
+ g = IndicatorBox(lower=0)
+ sigma = 1
+ tau = 1/(sigma*normK**2)
+
+elif noise == 'gaussian':
+ alpha = 20
+ f2 = 0.5 * L2NormSquared(b=noisy_data)
+ g = ZeroFunction()
+ sigma = 10
+ tau = 1/(sigma*normK**2)
+
+f1 = alpha * MixedL21Norm()
+f = BlockFunction(f1, f2)
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 200
+pdhg.run(2000)
+
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('TV Reconstruction')
+plt.colorbar()
+plt.show()
+plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), data.as_array()[int(N/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output().as_array()[int(N/2),:], label = 'TV reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
diff --git a/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_Tikhonov_Denoising.py b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_Tikhonov_Denoising.py
new file mode 100644
index 0000000..78d4980
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/GatherAll/PDHG_Tikhonov_Denoising.py
@@ -0,0 +1,258 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+
+``Tikhonov`` Regularization Denoising using PDHG algorithm:
+
+
+Problem: min_{u}, \alpha * ||\nabla u||_{2,2} + Fidelity(u, g)
+
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: Noisy Data
+
+ Fidelity = 1) L2NormSquarred ( \frac{1}{2} * || u - g ||_{2}^{2} ) if Noise is Gaussian
+ 2) L1Norm ( ||u - g||_{1} )if Noise is Salt & Pepper
+ 3) Kullback Leibler (\int u - g * log(u) + Id_{u>0}) if Noise is Poisson
+
+ Method = 0 ( PDHG - split ) : K = [ \nabla,
+ Identity]
+
+
+ Method = 1 (PDHG - explicit ): K = \nabla
+
+
+ Default: Tikhonov denoising
+ noise = Gaussian
+ Fidelity = L2NormSquarred
+ method = 0
+
+"""
+
+from ccpi.framework import ImageData, TestData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared,\
+ BlockFunction, KullbackLeibler, L1Norm
+
+import sys, os
+if int(numpy.version.version.split('.')[1]) > 12:
+ from skimage.util import random_noise
+else:
+ from demoutil import random_noise
+
+
+# user supplied input
+if len(sys.argv) > 1:
+ which_noise = int(sys.argv[1])
+else:
+ which_noise = 2
+print ("Applying {} noise")
+
+if len(sys.argv) > 2:
+ method = sys.argv[2]
+else:
+ method = '0'
+print ("method ", method)
+
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+data = loader.load(TestData.SHAPES)
+ig = data.geometry
+ag = ig
+
+# Create noisy data.
+noises = ['gaussian', 'poisson', 's&p']
+noise = noises[which_noise]
+if noise == 's&p':
+ n1 = random_noise(data.as_array(), mode = noise, salt_vs_pepper = 0.9, amount=0.2)
+elif noise == 'poisson':
+ scale = 5
+ n1 = random_noise( data.as_array()/scale, mode = noise, seed = 10)*scale
+elif noise == 'gaussian':
+ n1 = random_noise(data.as_array(), mode = noise, seed = 10)
+else:
+ raise ValueError('Unsupported Noise ', noise)
+noisy_data = ImageData(n1)
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,5))
+plt.subplot(1,2,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(1,2,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+# Regularisation Parameter depending on the noise distribution
+if noise == 's&p':
+ alpha = 20
+elif noise == 'poisson':
+ alpha = 10
+elif noise == 'gaussian':
+ alpha = 5
+
+# fidelity
+if noise == 's&p':
+ f2 = L1Norm(b=noisy_data)
+elif noise == 'poisson':
+ f2 = KullbackLeibler(noisy_data)
+elif noise == 'gaussian':
+ f2 = 0.5 * L2NormSquared(b=noisy_data)
+
+if method == '0':
+
+ # Create operators
+ op1 = Gradient(ig)
+ op2 = Identity(ig, ag)
+
+ # Create BlockOperator
+ operator = BlockOperator(op1, op2, shape=(2,1) )
+
+ # Create functions
+ f1 = alpha * L2NormSquared()
+ f = BlockFunction(f1, f2)
+ g = ZeroFunction()
+
+else:
+
+ operator = Gradient(ig)
+ g = f2
+
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 100
+pdhg.run(2000)
+
+
+plt.figure(figsize=(20,5))
+plt.subplot(1,4,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(1,4,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(1,4,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('TV Reconstruction')
+plt.colorbar()
+plt.subplot(1,4,4)
+plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), data.as_array()[int(ig.shape[0]/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output().as_array()[int(ig.shape[0]/2),:], label = 'Tikhonov reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
+
+##%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+if cvx_not_installable:
+
+ ##Construct problem
+ u = Variable(ig.shape)
+
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ # Define Total Variation as a regulariser
+
+ regulariser = alpha * sum_squares(norm(vstack([DX.matrix() * vec(u), DY.matrix() * vec(u)]), 2, axis = 0))
+
+ # choose solver
+ if 'MOSEK' in installed_solvers():
+ solver = MOSEK
+ else:
+ solver = SCS
+
+ # fidelity
+ if noise == 's&p':
+ fidelity = pnorm( u - noisy_data.as_array(),1)
+ elif noise == 'poisson':
+ fidelity = sum(kl_div(noisy_data.as_array(), u))
+ solver = SCS
+ elif noise == 'gaussian':
+ fidelity = 0.5 * sum_squares(noisy_data.as_array() - u)
+
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj)
+ result = prob.solve(verbose = True, solver = solver)
+
+ diff_cvx = numpy.abs( pdhg.get_output().as_array() - u.value )
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(pdhg.get_output().as_array())
+ plt.title('PDHG solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(u.value)
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), pdhg.get_output().as_array()[int(ig.shape[0]/2),:], label = 'PDHG')
+ plt.plot(np.linspace(0,ig.shape[1],ig.shape[1]), u.value[int(ig.shape[0]/2),:], label = 'CVX')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (PDHG) {} '.format(pdhg.objective[-1][0]))
+#
+#
+#
+#
+#
diff --git a/Wrappers/Python/demos/PDHG_examples/GatherAll/phantom.mat b/Wrappers/Python/demos/PDHG_examples/GatherAll/phantom.mat
new file mode 100755
index 0000000..c465bbe
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/GatherAll/phantom.mat
Binary files differ
diff --git a/Wrappers/Python/demos/PDHG_examples/IMATDemo.py b/Wrappers/Python/demos/PDHG_examples/IMATDemo.py
new file mode 100644
index 0000000..2051860
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/IMATDemo.py
@@ -0,0 +1,339 @@
+
+
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Mar 25 12:50:27 2019
+
+@author: vaggelis
+"""
+
+from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer, AcquisitionGeometry, AcquisitionData
+from astropy.io import fits
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import CGLS, PDHG
+from ccpi.optimisation.functions import MixedL21Norm, L2NormSquared, BlockFunction, ZeroFunction, KullbackLeibler, IndicatorBox
+from ccpi.optimisation.operators import Gradient, BlockOperator
+
+from ccpi.astra.operators import AstraProjectorMC, AstraProjectorSimple
+
+import pickle
+
+
+# load file
+
+#filename_sino = '/media/newhd/shared/DataProcessed/IMAT_beamtime_Feb_2019/preprocessed_test_flat/sino/rebin_slice_350/sino_log_rebin_282.fits'
+#filename_sino = '/media/newhd/shared/DataProcessed/IMAT_beamtime_Feb_2019/preprocessed_test_flat/sino/rebin_slice_350/sino_log_rebin_564.fits'
+#filename_sino = '/media/newhd/shared/DataProcessed/IMAT_beamtime_Feb_2019/preprocessed_test_flat/sino/rebin_slice_350/sino_log_rebin_141.fits'
+filename_sino = '/media/newhd/shared/DataProcessed/IMAT_beamtime_Feb_2019/preprocessed_test_flat/sino/rebin_slice_350/sino_log_rebin_80_channels.fits'
+
+sino_handler = fits.open(filename_sino)
+sino = numpy.array(sino_handler[0].data, dtype=float)
+
+# change axis order: channels, angles, detectors
+sino_new = numpy.rollaxis(sino, 2)
+sino_handler.close()
+
+
+sino_shape = sino_new.shape
+
+num_channels = sino_shape[0] # channelss
+num_pixels_h = sino_shape[2] # detectors
+num_pixels_v = sino_shape[2] # detectors
+num_angles = sino_shape[1] # angles
+
+
+ig = ImageGeometry(voxel_num_x = num_pixels_h, voxel_num_y = num_pixels_v, channels = num_channels)
+
+with open("/media/newhd/vaggelis/CCPi/IMAT_reconstruction/CCPi-Framework/Wrappers/Python/ccpi/optimisation/IMAT_data/golden_angles_new.txt") as f:
+ angles_string = [line.rstrip() for line in f]
+ angles = numpy.array(angles_string).astype(float)
+
+
+ag = AcquisitionGeometry('parallel', '2D', angles * numpy.pi / 180, pixel_num_h = num_pixels_h, channels = num_channels)
+op_MC = AstraProjectorMC(ig, ag, 'gpu')
+
+sino_aqdata = AcquisitionData(sino_new, ag)
+result_bp = op_MC.adjoint(sino_aqdata)
+
+#%%
+
+channel = [40, 60]
+for j in range(2):
+ z4 = sino_aqdata.as_array()[channel[j]]
+ plt.figure(figsize=(10,6))
+ plt.imshow(z4, cmap='viridis')
+ plt.axis('off')
+ plt.savefig('Sino_141/Sinogram_ch_{}_.png'.format(channel[j]), bbox_inches='tight', transparent=True)
+ plt.show()
+
+#%%
+
+def callback(iteration, objective, x):
+ plt.imshow(x.as_array()[40])
+ plt.colorbar()
+ plt.show()
+
+#%%
+# CGLS
+
+x_init = ig.allocate()
+cgls1 = CGLS(x_init=x_init, operator=op_MC, data=sino_aqdata)
+cgls1.max_iteration = 100
+cgls1.update_objective_interval = 2
+cgls1.run(20,verbose=True, callback=callback)
+
+plt.imshow(cgls1.get_output().subset(channel=20).array)
+plt.title('CGLS')
+plt.colorbar()
+plt.show()
+
+#%%
+with open('Sino_141/CGLS/CGLS_{}_iter.pkl'.format(20), 'wb') as f:
+ z = cgls1.get_output()
+ pickle.dump(z, f)
+
+#%%
+#% Tikhonov Space
+
+x_init = ig.allocate()
+alpha = [1,3,5,10,20,50]
+
+for a in alpha:
+
+ Grad = Gradient(ig, correlation = Gradient.CORRELATION_SPACE)
+ operator = BlockOperator(op_MC, a * Grad, shape=(2,1))
+ blockData = BlockDataContainer(sino_aqdata, \
+ Grad.range_geometry().allocate())
+ cgls2 = CGLS()
+ cgls2.max_iteration = 500
+ cgls2.set_up(x_init, operator, blockData)
+ cgls2.update_objective_interval = 50
+ cgls2.run(100,verbose=True)
+
+ with open('Sino_141/CGLS_Space/CGLS_a_{}.pkl'.format(a), 'wb') as f:
+ z = cgls2.get_output()
+ pickle.dump(z, f)
+
+#% Tikhonov SpaceChannels
+
+for a1 in alpha:
+
+ Grad1 = Gradient(ig, correlation = Gradient.CORRELATION_SPACECHANNEL)
+ operator1 = BlockOperator(op_MC, a1 * Grad1, shape=(2,1))
+ blockData1 = BlockDataContainer(sino_aqdata, \
+ Grad1.range_geometry().allocate())
+ cgls3 = CGLS()
+ cgls3.max_iteration = 500
+ cgls3.set_up(x_init, operator1, blockData1)
+ cgls3.update_objective_interval = 10
+ cgls3.run(100, verbose=True)
+
+ with open('Sino_141/CGLS_SpaceChannels/CGLS_a_{}.pkl'.format(a1), 'wb') as f1:
+ z1 = cgls3.get_output()
+ pickle.dump(z1, f1)
+
+
+
+#%%
+#
+ig_tmp = ImageGeometry(voxel_num_x = num_pixels_h, voxel_num_y = num_pixels_v)
+ag_tmp = AcquisitionGeometry('parallel', '2D', angles * numpy.pi / 180, pixel_num_h = num_pixels_h)
+op_tmp = AstraProjectorSimple(ig_tmp, ag_tmp, 'gpu')
+normK1 = op_tmp.norm()
+
+alpha_TV = [2, 5, 10] # for powder
+
+# Create operators
+op1 = Gradient(ig, correlation=Gradient.CORRELATION_SPACECHANNEL)
+op2 = op_MC
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+
+for alpha in alpha_TV:
+# Create functions
+ f1 = alpha * MixedL21Norm()
+
+ f2 = KullbackLeibler(sino_aqdata)
+ f = BlockFunction(f1, f2)
+ g = IndicatorBox(lower=0)
+
+ # Compute operator Norm
+ normK = numpy.sqrt(8 + normK1**2)
+
+ # Primal & dual stepsizes
+ sigma = 1
+ tau = 1/(sigma*normK**2)
+
+ # Setup and run the PDHG algorithm
+ pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+ pdhg.max_iteration = 5000
+ pdhg.update_objective_interval = 500
+# pdhg.run(2000, verbose=True, callback=callback)
+ pdhg.run(5000, verbose=True, callback=callback)
+#
+ with open('Sino_141/TV_SpaceChannels/TV_a = {}.pkl'.format(alpha), 'wb') as f3:
+ z3 = pdhg.get_output()
+ pickle.dump(z3, f3)
+#
+#
+#
+#
+##%%
+#
+#ig_tmp = ImageGeometry(voxel_num_x = num_pixels_h, voxel_num_y = num_pixels_v)
+#ag_tmp = AcquisitionGeometry('parallel', '2D', angles * numpy.pi / 180, pixel_num_h = num_pixels_h)
+#op_tmp = AstraProjectorSimple(ig_tmp, ag_tmp, 'gpu')
+#normK1 = op_tmp.norm()
+#
+#alpha_TV = 10 # for powder
+#
+## Create operators
+#op1 = Gradient(ig, correlation=Gradient.CORRELATION_SPACECHANNEL)
+#op2 = op_MC
+#
+## Create BlockOperator
+#operator = BlockOperator(op1, op2, shape=(2,1) )
+#
+#
+## Create functions
+#f1 = alpha_TV * MixedL21Norm()
+#f2 = 0.5 * L2NormSquared(b=sino_aqdata)
+#f = BlockFunction(f1, f2)
+#g = ZeroFunction()
+#
+## Compute operator Norm
+##normK = 8.70320267279591 # For powder Run one time no need to compute again takes time
+#normK = numpy.sqrt(8 + normK1**2) # for carbon
+#
+## Primal & dual stepsizes
+#sigma = 0.1
+#tau = 1/(sigma*normK**2)
+#
+#def callback(iteration, objective, x):
+# plt.imshow(x.as_array()[100])
+# plt.colorbar()
+# plt.show()
+#
+## Setup and run the PDHG algorithm
+#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+#pdhg.max_iteration = 2000
+#pdhg.update_objective_interval = 100
+#pdhg.run(2000, verbose=True)
+#
+#
+#
+#
+#
+#
+
+
+
+
+
+
+
+
+
+
+#%%
+
+#with open('/media/newhd/vaggelis/CCPi/IMAT_reconstruction/CCPi-Framework/Wrappers/Python/ccpi/optimisation/CGLS_Tikhonov/CGLS_Space/CGLS_Space_a = 50.pkl', 'wb') as f:
+# z = cgls2.get_output()
+# pickle.dump(z, f)
+#
+
+ #%%
+with open('Sino_141/CGLS_Space/CGLS_Space_a_20.pkl', 'rb') as f1:
+ x = pickle.load(f1)
+
+with open('Sino_141/CGLS_SpaceChannels/CGLS_SpaceChannels_a_20.pkl', 'rb') as f1:
+ x1 = pickle.load(f1)
+
+
+
+#
+plt.imshow(x.as_array()[40]*mask)
+plt.colorbar()
+plt.show()
+
+plt.imshow(x1.as_array()[40]*mask)
+plt.colorbar()
+plt.show()
+
+plt.plot(x.as_array()[40,100,:])
+plt.plot(x1.as_array()[40,100,:])
+plt.show()
+
+#%%
+
+# Show results
+
+def circ_mask(h, w, center=None, radius=None):
+
+ if center is None: # use the middle of the image
+ center = [int(w/2), int(h/2)]
+ if radius is None: # use the smallest distance between the center and image walls
+ radius = min(center[0], center[1], w-center[0], h-center[1])
+
+ Y, X = numpy.ogrid[:h, :w]
+ dist_from_center = numpy.sqrt((X - center[0])**2 + (Y-center[1])**2)
+
+ mask = dist_from_center <= radius
+ return mask
+
+mask = circ_mask(141, 141, center=None, radius = 55)
+plt.imshow(numpy.multiply(x.as_array()[40],mask))
+plt.show()
+#%%
+#channel = [100, 200, 300]
+#
+#for i in range(3):
+# tmp = cgls1.get_output().as_array()[channel[i]]
+#
+# z = tmp * mask
+# plt.figure(figsize=(10,6))
+# plt.imshow(z, vmin=0, cmap='viridis')
+# plt.axis('off')
+## plt.clim(0, 0.02)
+## plt.colorbar()
+## del z
+# plt.savefig('CGLS_282/CGLS_Chan_{}.png'.format(channel[i]), bbox_inches='tight', transparent=True)
+# plt.show()
+#
+#
+##%% Line Profiles
+#
+#n1, n2, n3 = cgs.get_output().as_array().shape
+#mask = circ_mask(564, 564, center=None, radius = 220)
+#material = ['Cu', 'Fe', 'Ni']
+#ycoords = [200, 300, 380]
+#
+#for i in range(3):
+# z = cgs.get_output().as_array()[channel[i]] * mask
+#
+# for k1 in range(len(ycoords)):
+# plt.plot(numpy.arange(0,n2), z[ycoords[k1],:])
+# plt.title('Channel {}: {}'.format(channel[i], material[k1]))
+# plt.savefig('CGLS/line_profile_chan_{}_material_{}.png'.\
+# format(channel[i], material[k1]), bbox_inches='tight')
+# plt.show()
+#
+#
+#
+#
+#
+##%%
+#
+#%%
+
+
+
+#%%
+
+#plt.imshow(pdhg.get_output().subset(channel=100).as_array())
+#plt.show()
diff --git a/Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_2D_time_denoising.py b/Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_2D_time_denoising.py
new file mode 100644
index 0000000..045458a
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_2D_time_denoising.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, KullbackLeibler, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorMC
+
+import os
+import tomophantom
+from tomophantom import TomoP2D
+
+# Create phantom for TV 2D dynamic tomography
+
+model = 102 # note that the selected model is temporal (2D + time)
+N = 50 # set dimension of the phantom
+# one can specify an exact path to the parameters file
+# path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
+path = os.path.dirname(tomophantom.__file__)
+path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
+#This will generate a N_size x N_size x Time frames phantom (2D + time)
+phantom_2Dt = TomoP2D.ModelTemporal(model, N, path_library2D)
+
+plt.close('all')
+plt.figure(1)
+plt.rcParams.update({'font.size': 21})
+plt.title('{}''{}'.format('2D+t phantom using model no.',model))
+for sl in range(0,np.shape(phantom_2Dt)[0]):
+ im = phantom_2Dt[sl,:,:]
+ plt.imshow(im, vmin=0, vmax=1)
+ plt.pause(.1)
+ plt.draw
+
+
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N, channels = np.shape(phantom_2Dt)[0])
+data = ImageData(phantom_2Dt, geometry=ig)
+
+detectors = N
+angles = np.linspace(0,np.pi,N)
+
+ag = AcquisitionGeometry('parallel','2D', angles, detectors, channels = np.shape(phantom_2Dt)[0])
+Aop = AstraProjectorMC(ig, ag, 'gpu')
+sin = Aop.direct(data)
+
+scale = 2
+n1 = scale * np.random.poisson(sin.as_array()/scale)
+noisy_data = AcquisitionData(n1, ag)
+
+tindex = [3, 6, 10]
+
+fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 10))
+plt.subplot(1,3,1)
+plt.imshow(noisy_data.as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+plt.subplot(1,3,2)
+plt.imshow(noisy_data.as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+plt.subplot(1,3,3)
+plt.imshow(noisy_data.as_array()[tindex[2],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+plt.show()
+
+#%%
+# Regularisation Parameter
+alpha = 5
+
+# Create operators
+#op1 = Gradient(ig)
+op1 = Gradient(ig, correlation='SpaceChannels')
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = KullbackLeibler(noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 200
+pdhg.run(2000)
+
+
+#%%
+fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8))
+
+plt.subplot(2,3,1)
+plt.imshow(phantom_2Dt[tindex[0],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+
+plt.subplot(2,3,2)
+plt.imshow(phantom_2Dt[tindex[1],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+
+plt.subplot(2,3,3)
+plt.imshow(phantom_2Dt[tindex[2],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+
+plt.subplot(2,3,4)
+plt.imshow(pdhg.get_output().as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.subplot(2,3,5)
+plt.imshow(pdhg.get_output().as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.subplot(2,3,6)
+plt.imshow(pdhg.get_output().as_array()[tindex[2],:,:])
+plt.axis('off')
+im = plt.imshow(pdhg.get_output().as_array()[tindex[0],:,:])
+
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
+cbar = fig.colorbar(im, cax=cb_ax)
+
+
+plt.show()
+
diff --git a/Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_TV_Tomo2D_time.py b/Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_TV_Tomo2D_time.py
new file mode 100644
index 0000000..045458a
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/MultiChannel/PDHG_TV_Tomo2D_time.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, KullbackLeibler, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorMC
+
+import os
+import tomophantom
+from tomophantom import TomoP2D
+
+# Create phantom for TV 2D dynamic tomography
+
+model = 102 # note that the selected model is temporal (2D + time)
+N = 50 # set dimension of the phantom
+# one can specify an exact path to the parameters file
+# path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
+path = os.path.dirname(tomophantom.__file__)
+path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
+#This will generate a N_size x N_size x Time frames phantom (2D + time)
+phantom_2Dt = TomoP2D.ModelTemporal(model, N, path_library2D)
+
+plt.close('all')
+plt.figure(1)
+plt.rcParams.update({'font.size': 21})
+plt.title('{}''{}'.format('2D+t phantom using model no.',model))
+for sl in range(0,np.shape(phantom_2Dt)[0]):
+ im = phantom_2Dt[sl,:,:]
+ plt.imshow(im, vmin=0, vmax=1)
+ plt.pause(.1)
+ plt.draw
+
+
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N, channels = np.shape(phantom_2Dt)[0])
+data = ImageData(phantom_2Dt, geometry=ig)
+
+detectors = N
+angles = np.linspace(0,np.pi,N)
+
+ag = AcquisitionGeometry('parallel','2D', angles, detectors, channels = np.shape(phantom_2Dt)[0])
+Aop = AstraProjectorMC(ig, ag, 'gpu')
+sin = Aop.direct(data)
+
+scale = 2
+n1 = scale * np.random.poisson(sin.as_array()/scale)
+noisy_data = AcquisitionData(n1, ag)
+
+tindex = [3, 6, 10]
+
+fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 10))
+plt.subplot(1,3,1)
+plt.imshow(noisy_data.as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+plt.subplot(1,3,2)
+plt.imshow(noisy_data.as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+plt.subplot(1,3,3)
+plt.imshow(noisy_data.as_array()[tindex[2],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+plt.show()
+
+#%%
+# Regularisation Parameter
+alpha = 5
+
+# Create operators
+#op1 = Gradient(ig)
+op1 = Gradient(ig, correlation='SpaceChannels')
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = KullbackLeibler(noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 200
+pdhg.run(2000)
+
+
+#%%
+fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8))
+
+plt.subplot(2,3,1)
+plt.imshow(phantom_2Dt[tindex[0],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+
+plt.subplot(2,3,2)
+plt.imshow(phantom_2Dt[tindex[1],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+
+plt.subplot(2,3,3)
+plt.imshow(phantom_2Dt[tindex[2],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+
+plt.subplot(2,3,4)
+plt.imshow(pdhg.get_output().as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.subplot(2,3,5)
+plt.imshow(pdhg.get_output().as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.subplot(2,3,6)
+plt.imshow(pdhg.get_output().as_array()[tindex[2],:,:])
+plt.axis('off')
+im = plt.imshow(pdhg.get_output().as_array()[tindex[0],:,:])
+
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
+cbar = fig.colorbar(im, cax=cb_ax)
+
+
+plt.show()
+
diff --git a/Wrappers/Python/demos/PDHG_examples/PDHG_TV_Color_Denoising.py b/Wrappers/Python/demos/PDHG_examples/PDHG_TV_Color_Denoising.py
new file mode 100644
index 0000000..ddf5ace
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/PDHG_TV_Color_Denoising.py
@@ -0,0 +1,115 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+Total Variation Denoising using PDHG algorithm:
+Problem: min_x, x>0 \alpha * ||\nabla x||_{2,1} + ||x-g||_{1}
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: Noisy Data with Salt & Pepper Noise
+
+
+ Method = 0 ( PDHG - split ) : K = [ \nabla,
+ Identity]
+
+
+ Method = 1 (PDHG - explicit ): K = \nabla
+
+
+"""
+
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import Gradient, BlockOperator, FiniteDiff
+from ccpi.optimisation.functions import MixedL21Norm, MixedL11Norm, L2NormSquared, BlockFunction, L1Norm
+from ccpi.framework import TestData, ImageGeometry
+import os, sys
+if int(numpy.version.version.split('.')[1]) > 12:
+ from skimage.util import random_noise
+else:
+ from demoutil import random_noise
+
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+data = loader.load(TestData.PEPPERS, size=(256,256))
+ig = data.geometry
+ag = ig
+
+# Create noisy data.
+n1 = random_noise(data.as_array(), mode = 'gaussian', var = 0.15, seed = 50)
+noisy_data = ig.allocate()
+noisy_data.fill(n1)
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,5))
+plt.subplot(1,2,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(1,2,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+# Regularisation Parameter
+operator = Gradient(ig, correlation=Gradient.CORRELATION_SPACE)
+f1 = 5 * MixedL21Norm()
+g = 0.5 * L2NormSquared(b=noisy_data)
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+# Setup and run the PDHG algorithm
+pdhg1 = PDHG(f=f1,g=g,operator=operator, tau=tau, sigma=sigma)
+pdhg1.max_iteration = 2000
+pdhg1.update_objective_interval = 200
+pdhg1.run(1000)
+
+
+# Show results
+plt.figure(figsize=(10,10))
+plt.subplot(2,2,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(2,2,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(2,2,3)
+plt.imshow(pdhg1.get_output().as_array())
+plt.title('TV Reconstruction')
+plt.colorbar()
+plt.subplot(2,2,4)
+plt.imshow(pdhg2.get_output().as_array())
+plt.title('TV Reconstruction')
+plt.colorbar()
+plt.show()
+
diff --git a/Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_2D_time.py b/Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_2D_time.py
new file mode 100644
index 0000000..14608db
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_2D_time.py
@@ -0,0 +1,192 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient, Identity
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorMC
+
+import os
+import tomophantom
+from tomophantom import TomoP2D
+
+# Create phantom for TV 2D dynamic tomography
+
+model = 102 # note that the selected model is temporal (2D + time)
+N = 128 # set dimension of the phantom
+# one can specify an exact path to the parameters file
+# path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
+path = os.path.dirname(tomophantom.__file__)
+path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
+#This will generate a N_size x N_size x Time frames phantom (2D + time)
+phantom_2Dt = TomoP2D.ModelTemporal(model, N, path_library2D)
+
+plt.close('all')
+plt.figure(1)
+plt.rcParams.update({'font.size': 21})
+plt.title('{}''{}'.format('2D+t phantom using model no.',model))
+for sl in range(0,np.shape(phantom_2Dt)[0]):
+ im = phantom_2Dt[sl,:,:]
+ plt.imshow(im, vmin=0, vmax=1)
+# plt.pause(.1)
+# plt.draw
+
+
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N, channels = np.shape(phantom_2Dt)[0])
+data = ImageData(phantom_2Dt, geometry=ig)
+ag = ig
+
+# Create Noisy data. Add Gaussian noise
+np.random.seed(10)
+noisy_data = ImageData( data.as_array() + np.random.normal(0, 0.25, size=ig.shape) )
+
+tindex = [3, 6, 10]
+
+fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 10))
+plt.subplot(1,3,1)
+plt.imshow(noisy_data.as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+plt.subplot(1,3,2)
+plt.imshow(noisy_data.as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+plt.subplot(1,3,3)
+plt.imshow(noisy_data.as_array()[tindex[2],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+plt.show()
+
+#%%
+# Regularisation Parameter
+alpha = 0.3
+
+# Create operators
+#op1 = Gradient(ig)
+op1 = Gradient(ig, correlation='Space')
+op2 = Gradient(ig, correlation='SpaceChannels')
+
+op3 = Identity(ig, ag)
+
+# Create BlockOperator
+operator1 = BlockOperator(op1, op3, shape=(2,1) )
+operator2 = BlockOperator(op2, op3, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = 0.5 * L2NormSquared(b = noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+# Compute operator Norm
+normK1 = operator1.norm()
+normK2 = operator2.norm()
+
+#%%
+# Primal & dual stepsizes
+sigma1 = 1
+tau1 = 1/(sigma1*normK1**2)
+
+sigma2 = 1
+tau2 = 1/(sigma2*normK2**2)
+
+# Setup and run the PDHG algorithm
+pdhg1 = PDHG(f=f,g=g,operator=operator1, tau=tau1, sigma=sigma1)
+pdhg1.max_iteration = 2000
+pdhg1.update_objective_interval = 200
+pdhg1.run(2000)
+
+# Setup and run the PDHG algorithm
+pdhg2 = PDHG(f=f,g=g,operator=operator2, tau=tau2, sigma=sigma2)
+pdhg2.max_iteration = 2000
+pdhg2.update_objective_interval = 200
+pdhg2.run(2000)
+
+
+#%%
+
+tindex = [3, 6, 10]
+fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8))
+
+plt.subplot(3,3,1)
+plt.imshow(phantom_2Dt[tindex[0],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+
+plt.subplot(3,3,2)
+plt.imshow(phantom_2Dt[tindex[1],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+
+plt.subplot(3,3,3)
+plt.imshow(phantom_2Dt[tindex[2],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+plt.subplot(3,3,4)
+plt.imshow(pdhg1.get_output().as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.subplot(3,3,5)
+plt.imshow(pdhg1.get_output().as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.subplot(3,3,6)
+plt.imshow(pdhg1.get_output().as_array()[tindex[2],:,:])
+plt.axis('off')
+
+
+plt.subplot(3,3,7)
+plt.imshow(pdhg2.get_output().as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.subplot(3,3,8)
+plt.imshow(pdhg2.get_output().as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.subplot(3,3,9)
+plt.imshow(pdhg2.get_output().as_array()[tindex[2],:,:])
+plt.axis('off')
+
+#%%
+im = plt.imshow(pdhg1.get_output().as_array()[tindex[0],:,:])
+
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
+cbar = fig.colorbar(im, cax=cb_ax)
+
+
+plt.show()
+
diff --git a/Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_Gaussian_3D.py b/Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_Gaussian_3D.py
new file mode 100644
index 0000000..03dc2ef
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/TV_Denoising/PDHG_TV_Denoising_Gaussian_3D.py
@@ -0,0 +1,181 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+"""
+
+Total Variation (3D) Denoising using PDHG algorithm:
+
+
+Problem: min_{x} \alpha * ||\nabla x||_{2,1} + \frac{1}{2} * || x - g ||_{2}^{2}
+
+ \alpha: Regularization parameter
+
+ \nabla: Gradient operator
+
+ g: Noisy Data with Gaussian Noise
+
+ Method = 0 ( PDHG - split ) : K = [ \nabla,
+ Identity]
+
+
+ Method = 1 (PDHG - explicit ): K = \nabla
+
+"""
+
+from ccpi.framework import ImageData, ImageGeometry
+
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction
+
+from skimage.util import random_noise
+
+# Create phantom for TV Gaussian denoising
+import timeit
+import os
+from tomophantom import TomoP3D
+import tomophantom
+
+print ("Building 3D phantom using TomoPhantom software")
+tic=timeit.default_timer()
+model = 13 # select a model number from the library
+N = 64 # Define phantom dimensions using a scalar value (cubic phantom)
+path = os.path.dirname(tomophantom.__file__)
+path_library3D = os.path.join(path, "Phantom3DLibrary.dat")
+
+#This will generate a N x N x N phantom (3D)
+phantom_tm = TomoP3D.Model(model, N, path_library3D)
+
+#%%
+
+# Create noisy data. Add Gaussian noise
+ig = ImageGeometry(voxel_num_x=N, voxel_num_y=N, voxel_num_z=N)
+ag = ig
+n1 = random_noise(phantom_tm, mode = 'gaussian', mean=0, var = 0.001, seed=10)
+noisy_data = ImageData(n1)
+
+sliceSel = int(0.5*N)
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(noisy_data.as_array()[sliceSel,:,:],vmin=0, vmax=1)
+plt.title('Axial View')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array()[:,sliceSel,:],vmin=0, vmax=1)
+plt.title('Coronal View')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(noisy_data.as_array()[:,:,sliceSel],vmin=0, vmax=1)
+plt.title('Sagittal View')
+plt.colorbar()
+plt.show()
+
+#%%
+
+# Regularisation Parameter
+alpha = 0.05
+
+method = '0'
+
+if method == '0':
+
+ # Create operators
+ op1 = Gradient(ig)
+ op2 = Identity(ig, ag)
+
+ # Create BlockOperator
+ operator = BlockOperator(op1, op2, shape=(2,1) )
+
+ # Create functions
+
+ f1 = alpha * MixedL21Norm()
+ f2 = 0.5 * L2NormSquared(b = noisy_data)
+ f = BlockFunction(f1, f2)
+
+ g = ZeroFunction()
+
+else:
+
+ # Without the "Block Framework"
+ operator = Gradient(ig)
+ f = alpha * MixedL21Norm()
+ g = 0.5 * L2NormSquared(b = noisy_data)
+
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 200
+pdhg.run(2000, verbose = True)
+
+
+#%%
+fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8))
+fig.suptitle('TV Reconstruction',fontsize=20)
+
+
+plt.subplot(2,3,1)
+plt.imshow(noisy_data.as_array()[sliceSel,:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Axial View')
+
+plt.subplot(2,3,2)
+plt.imshow(noisy_data.as_array()[:,sliceSel,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Coronal View')
+
+plt.subplot(2,3,3)
+plt.imshow(noisy_data.as_array()[:,:,sliceSel],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Sagittal View')
+
+
+plt.subplot(2,3,4)
+plt.imshow(pdhg.get_output().as_array()[sliceSel,:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.subplot(2,3,5)
+plt.imshow(pdhg.get_output().as_array()[:,sliceSel,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.subplot(2,3,6)
+plt.imshow(pdhg.get_output().as_array()[:,:,sliceSel],vmin=0, vmax=1)
+plt.axis('off')
+im = plt.imshow(pdhg.get_output().as_array()[:,:,sliceSel],vmin=0, vmax=1)
+
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
+cbar = fig.colorbar(im, cax=cb_ax)
+
+
+plt.show()
+
diff --git a/Wrappers/Python/demos/PDHG_examples/Tomo/PDHG_Tikhonov_Tomo2D.py b/Wrappers/Python/demos/PDHG_examples/Tomo/PDHG_Tikhonov_Tomo2D.py
new file mode 100644
index 0000000..02cd053
--- /dev/null
+++ b/Wrappers/Python/demos/PDHG_examples/Tomo/PDHG_Tikhonov_Tomo2D.py
@@ -0,0 +1,156 @@
+#========================================================================
+# Copyright 2019 Science Technology Facilities Council
+# Copyright 2019 University of Manchester
+#
+# This work is part of the Core Imaging Library developed by Science Technology
+# Facilities Council and University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#=========================================================================
+
+"""
+
+Total Variation Denoising using PDHG algorithm:
+
+Problem: min_x, x>0 \alpha * ||\nabla x||_{2}^{2} + int A x -g log(Ax + \eta)
+
+ \nabla: Gradient operator
+
+ A: Projection Matrix
+ g: Noisy sinogram corrupted with Poisson Noise
+
+ \eta: Background Noise
+ \alpha: Regularization parameter
+
+
+
+"""
+
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorSimple
+from ccpi.framework import TestData
+import os, sys
+
+loader = TestData(data_dir=os.path.join(sys.prefix, 'share','ccpi'))
+
+# Load Data
+N = 100
+M = 100
+data = loader.load(TestData.SIMPLE_PHANTOM_2D, size=(N,M), scale=(0,1))
+
+ig = data.geometry
+ag = ig
+
+#Create Acquisition Data and apply poisson noise
+
+detectors = N
+angles = np.linspace(0, np.pi, N)
+
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+
+device = input('Available device: GPU==1 / CPU==0 ')
+
+if device=='1':
+ dev = 'gpu'
+else:
+ dev = 'cpu'
+
+Aop = AstraProjectorSimple(ig, ag, 'cpu')
+sin = Aop.direct(data)
+
+# Create noisy data. Apply Poisson noise
+scale = 0.5
+eta = 0
+n1 = scale * np.random.poisson(eta + sin.as_array()/scale)
+
+noisy_data = AcquisitionData(n1, ag)
+
+# Show Ground Truth and Noisy Data
+plt.figure(figsize=(10,10))
+plt.subplot(2,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(2,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.show()
+
+
+# Regularisation Parameter
+alpha = 1000
+
+# Create operators
+op1 = Gradient(ig)
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * L2NormSquared()
+f2 = 0.5 * L2NormSquared(b=noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 500
+pdhg.run(2000)
+
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('Tikhonov Reconstruction')
+plt.colorbar()
+plt.show()
+##
+plt.plot(np.linspace(0,N,M), data.as_array()[int(N/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,N,M), pdhg.get_output().as_array()[int(N/2),:], label = 'Tikhonov reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
diff --git a/Wrappers/Python/demos/pdhg_TV_tomography2Dccpi.py b/Wrappers/Python/demos/pdhg_TV_tomography2Dccpi.py
new file mode 100644
index 0000000..854f645
--- /dev/null
+++ b/Wrappers/Python/demos/pdhg_TV_tomography2Dccpi.py
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Feb 22 14:53:03 2019
+
+@author: evangelos
+"""
+
+from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer, \
+ AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG, PDHG_old
+
+from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction, ScaledFunction
+
+from ccpi.plugins.operators import CCPiProjectorSimple
+from timeit import default_timer as timer
+from ccpi.reconstruction.parallelbeam import alg as pbalg
+import os
+
+try:
+ import tomophantom
+ from tomophantom import TomoP3D
+ no_tomophantom = False
+except ImportError as ie:
+ no_tomophantom = True
+
+#%%
+
+#%%###############################################################################
+# Create phantom for TV tomography
+
+#import os
+#import tomophantom
+#from tomophantom import TomoP2D
+#from tomophantom.supp.qualitymetrics import QualityTools
+
+#model = 1 # select a model number from the library
+#N = 150 # set dimension of the phantom
+## one can specify an exact path to the parameters file
+## path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
+#path = os.path.dirname(tomophantom.__file__)
+#path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
+##This will generate a N_size x N_size phantom (2D)
+#phantom_2D = TomoP2D.Model(model, N, path_library2D)
+#ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+#data = ImageData(phantom_2D, geometry=ig)
+
+N = 75
+#x = np.zeros((N,N))
+
+vert = 4
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N, voxel_num_z=vert)
+
+angles_num = 100
+det_w = 1.0
+det_num = N
+
+angles = np.linspace(-90.,90.,N, dtype=np.float32)
+# Inputs: Geometry, 2D or 3D, angles, horz detector pixel count,
+# horz detector pixel size, vert detector pixel count,
+# vert detector pixel size.
+ag = AcquisitionGeometry('parallel',
+ '3D',
+ angles,
+ N,
+ det_w,
+ vert,
+ det_w)
+
+#no_tomophantom = True
+if no_tomophantom:
+ data = ig.allocate()
+ Phantom = data
+ # Populate image data by looping over and filling slices
+ i = 0
+ while i < vert:
+ if vert > 1:
+ x = Phantom.subset(vertical=i).array
+ else:
+ x = Phantom.array
+ x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+ x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 0.98
+ if vert > 1 :
+ Phantom.fill(x, vertical=i)
+ i += 1
+
+ Aop = CCPiProjectorSimple(ig, ag, 'cpu')
+ sin = Aop.direct(data)
+else:
+
+ model = 13 # select a model number from the library
+ N_size = N # Define phantom dimensions using a scalar value (cubic phantom)
+ path = os.path.dirname(tomophantom.__file__)
+ path_library3D = os.path.join(path, "Phantom3DLibrary.dat")
+ #This will generate a N_size x N_size x N_size phantom (3D)
+ phantom_tm = TomoP3D.Model(model, N_size, path_library3D)
+
+ #%%
+ Horiz_det = int(np.sqrt(2)*N_size) # detector column count (horizontal)
+ Vert_det = N_size # detector row count (vertical) (no reason for it to be > N)
+ #angles_num = int(0.5*np.pi*N_size); # angles number
+ #angles = np.linspace(0.0,179.9,angles_num,dtype='float32') # in degrees
+
+ print ("Building 3D analytical projection data with TomoPhantom")
+ projData3D_analyt = TomoP3D.ModelSino(model,
+ N_size,
+ Horiz_det,
+ Vert_det,
+ angles,
+ path_library3D)
+
+ # tomophantom outputs in [vert,angles,horiz]
+ # we want [angle,vert,horiz]
+ data = np.transpose(projData3D_analyt, [1,0,2])
+ ag.pixel_num_h = Horiz_det
+ ag.pixel_num_v = Vert_det
+ sin = ag.allocate()
+ sin.fill(data)
+ ig.voxel_num_y = Vert_det
+
+ Aop = CCPiProjectorSimple(ig, ag, 'cpu')
+
+
+plt.imshow(sin.subset(vertical=0).as_array())
+plt.title('Sinogram')
+plt.colorbar()
+plt.show()
+
+
+#%%
+# Add Gaussian noise to the sinogram data
+np.random.seed(10)
+n1 = np.random.random(sin.shape)
+
+noisy_data = sin + ImageData(5*n1)
+
+plt.imshow(noisy_data.subset(vertical=0).as_array())
+plt.title('Noisy Sinogram')
+plt.colorbar()
+plt.show()
+
+
+#%% Works only with Composite Operator Structure of PDHG
+
+#ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+
+# Create operators
+op1 = Gradient(ig)
+op2 = Aop
+
+# Form Composite Operator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+alpha = 50
+f = BlockFunction( alpha * MixedL21Norm(), \
+ 0.5 * L2NormSquared(b = noisy_data) )
+g = ZeroFunction()
+
+normK = Aop.norm()
+
+# Compute operator Norm
+normK = operator.norm()
+
+## Primal & dual stepsizes
+diag_precon = False
+
+if diag_precon:
+
+ def tau_sigma_precond(operator):
+
+ tau = 1/operator.sum_abs_row()
+ sigma = 1/ operator.sum_abs_col()
+
+ return tau, sigma
+
+ tau, sigma = tau_sigma_precond(operator)
+
+else:
+ sigma = 1
+ tau = 1/(sigma*normK**2)
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+niter = 50
+opt = {'niter':niter}
+opt1 = {'niter':niter, 'memopt': True}
+
+
+
+pdhg1 = PDHG(f=f,g=g, operator=operator, tau=tau, sigma=sigma, max_iteration=niter)
+#pdhg1.max_iteration = 2000
+pdhg1.update_objective_interval = 100
+
+t1_old = timer()
+resold, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
+t2_old = timer()
+
+pdhg1.run(niter)
+print (sum(pdhg1.timing))
+res = pdhg1.get_output().subset(vertical=0)
+
+#%%
+plt.figure()
+plt.subplot(1,4,1)
+plt.imshow(res.as_array())
+plt.title('Algorithm')
+plt.colorbar()
+plt.subplot(1,4,2)
+plt.imshow(resold.subset(vertical=0).as_array())
+plt.title('function')
+plt.colorbar()
+plt.subplot(1,4,3)
+plt.imshow((res - resold.subset(vertical=0)).abs().as_array())
+plt.title('diff')
+plt.colorbar()
+plt.subplot(1,4,4)
+plt.plot(np.linspace(0,N,N), res.as_array()[int(N/2),:], label = 'Algorithm')
+plt.plot(np.linspace(0,N,N), resold.subset(vertical=0).as_array()[int(N/2),:], label = 'function')
+plt.legend()
+plt.show()
+#
+print ("Time: No memopt in {}s, \n Time: Memopt in {}s ".format(sum(pdhg1.timing), t2_old -t1_old))
+diff = (res - resold.subset(vertical=0)).abs().as_array().max()
+#
+print(" Max of abs difference is {}".format(diff))
+
diff --git a/Wrappers/Python/environment.yml b/Wrappers/Python/environment.yml
new file mode 100644
index 0000000..5cdd6fe
--- /dev/null
+++ b/Wrappers/Python/environment.yml
@@ -0,0 +1,11 @@
+name: test_new
+dependencies:
+ - python=3.6.7=h8dc6b48_1004
+ - numpy=1.11.3=py36hdf140aa_1207
+ - spyder=3.3.4=py36_0
+ - scikit-image=0.15.0=py36h6de7cb9_0
+ - scipy=1.2.1=py36hbd7caa9_1
+ - astra-toolbox=1.8.3=py36h804c3c0_0
+
+
+
diff --git a/Wrappers/Python/setup.py b/Wrappers/Python/setup.py
index a3fde59..8bd33a6 100644
--- a/Wrappers/Python/setup.py
+++ b/Wrappers/Python/setup.py
@@ -31,13 +31,17 @@ if cil_version == '':
setup(
name="ccpi-framework",
version=cil_version,
- packages=['ccpi' , 'ccpi.io',
+ packages=['ccpi' , 'ccpi.io',
'ccpi.framework', 'ccpi.optimisation',
'ccpi.optimisation.operators',
'ccpi.optimisation.algorithms',
'ccpi.optimisation.functions',
+ 'ccpi.processors',
'ccpi.contrib','ccpi.contrib.optimisation',
'ccpi.contrib.optimisation.algorithms'],
+ data_files = [('share/ccpi', ['data/boat.tiff', 'data/peppers.tiff',
+ 'data/camera.png',
+ 'data/resolution_chart.tiff'])],
# Project uses reStructuredText, so ensure that the docutils get
# installed or upgraded on the target machine
@@ -52,8 +56,9 @@ setup(
# zip_safe = False,
# metadata for upload to PyPI
- author="Edoardo Pasca",
- author_email="edoardo.pasca@stfc.ac.uk",
+ author="CCPi developers",
+ maintainer="Edoardo Pasca",
+ maintainer_email="edoardo.pasca@stfc.ac.uk",
description='CCPi Core Imaging Library - Python Framework Module',
license="Apache v2.0",
keywords="Python Framework",
diff --git a/Wrappers/Python/test/test_DataContainer.py b/Wrappers/Python/test/test_DataContainer.py
index e92d4c6..16f7b86 100755
--- a/Wrappers/Python/test/test_DataContainer.py
+++ b/Wrappers/Python/test/test_DataContainer.py
@@ -201,21 +201,25 @@ class TestDataContainer(unittest.TestCase):
self.assertNumpyArrayEqual(out.as_array(), ds2.as_array())
ds0 = ds
- steps.append(timer())
- ds0.add(2, out=out)
- steps.append(timer())
- print("ds0.add(2,out=out)", dt(steps), 3, ds0.as_array()[0][0][0])
- self.assertEqual(3., out.as_array()[0][0][0])
-
- dt1 = dt(steps)
- steps.append(timer())
- ds3 = ds0.add(2)
- steps.append(timer())
- print("ds3 = ds0.add(2)", dt(steps), 5, ds3.as_array()[0][0][0])
- dt2 = dt(steps)
- self.assertLess(dt1, dt2)
+ dt1 = 0
+ dt2 = 0
+ for i in range(10):
+ steps.append(timer())
+ ds0.add(2, out=out)
+ steps.append(timer())
+ print("ds0.add(2,out=out)", dt(steps), 3, ds0.as_array()[0][0][0])
+ self.assertEqual(3., out.as_array()[0][0][0])
+
+ dt1 += dt(steps)/10
+ steps.append(timer())
+ ds3 = ds0.add(2)
+ steps.append(timer())
+ print("ds3 = ds0.add(2)", dt(steps), 5, ds3.as_array()[0][0][0])
+ dt2 += dt(steps)/10
self.assertNumpyArrayEqual(out.as_array(), ds3.as_array())
+ self.assertLess(dt1, dt2)
+
def binary_subtract(self):
print("Test binary subtract")
@@ -324,16 +328,19 @@ class TestDataContainer(unittest.TestCase):
ds = DataContainer(a, False, ['X', 'Y', 'Z'])
ds1 = ds.copy()
- steps.append(timer())
- ds.divide(ds1, out=ds)
- steps.append(timer())
- t1 = dt(steps)
- print("ds.divide(ds1, out=ds)", dt(steps))
- steps.append(timer())
- ds2 = ds.divide(ds1)
- steps.append(timer())
- t2 = dt(steps)
- print("ds2 = ds.divide(ds1)", dt(steps))
+ t1 = 0
+ t2 = 0
+ for i in range(10):
+ steps.append(timer())
+ ds.divide(ds1, out=ds)
+ steps.append(timer())
+ t1 += dt(steps)/10.
+ print("ds.divide(ds1, out=ds)", dt(steps))
+ steps.append(timer())
+ ds2 = ds.divide(ds1)
+ steps.append(timer())
+ t2 += dt(steps)/10.
+ print("ds2 = ds.divide(ds1)", dt(steps))
self.assertLess(t1, t2)
self.assertEqual(ds.as_array()[0][0][0], 1.)
@@ -487,6 +494,13 @@ class TestDataContainer(unittest.TestCase):
self.assertNumpyArrayEqual(vol1.as_array(), numpy.ones(vol.shape) * 4)
self.assertEqual(vol.number_of_dimensions, 3)
+
+ ig2 = ImageGeometry (voxel_num_x=2,voxel_num_y=3,voxel_num_z=4,
+ dimension_labels=[ImageGeometry.HORIZONTAL_X, ImageGeometry.HORIZONTAL_Y,
+ ImageGeometry.VERTICAL])
+ data = ig2.allocate()
+ self.assertNumpyArrayEqual(numpy.asarray(data.shape), numpy.asarray(ig2.shape))
+ self.assertNumpyArrayEqual(numpy.asarray(data.shape), data.as_array().shape)
def test_AcquisitionData(self):
sgeometry = AcquisitionGeometry(dimension=2, angles=numpy.linspace(0, 180, num=10),
@@ -494,6 +508,29 @@ class TestDataContainer(unittest.TestCase):
pixel_num_h=5, channels=2)
sino = AcquisitionData(geometry=sgeometry)
self.assertEqual(sino.shape, (2, 10, 3, 5))
+
+ ag = AcquisitionGeometry (pixel_num_h=2,pixel_num_v=3,channels=4, dimension=2, angles=numpy.linspace(0, 180, num=10),
+ geom_type='parallel', )
+ print (ag.shape)
+ print (ag.dimension_labels)
+
+ data = ag.allocate()
+ self.assertNumpyArrayEqual(numpy.asarray(data.shape), numpy.asarray(ag.shape))
+ self.assertNumpyArrayEqual(numpy.asarray(data.shape), data.as_array().shape)
+
+ print (data.shape, ag.shape, data.as_array().shape)
+
+ ag2 = AcquisitionGeometry (pixel_num_h=2,pixel_num_v=3,channels=4, dimension=2, angles=numpy.linspace(0, 180, num=10),
+ geom_type='parallel',
+ dimension_labels=[AcquisitionGeometry.VERTICAL ,
+ AcquisitionGeometry.ANGLE, AcquisitionGeometry.HORIZONTAL, AcquisitionGeometry.CHANNEL])
+
+ data = ag2.allocate()
+ print (data.shape, ag2.shape, data.as_array().shape)
+ self.assertNumpyArrayEqual(numpy.asarray(data.shape), numpy.asarray(ag2.shape))
+ self.assertNumpyArrayEqual(numpy.asarray(data.shape), data.as_array().shape)
+
+
def test_ImageGeometry_allocate(self):
vgeometry = ImageGeometry(voxel_num_x=4, voxel_num_y=3, channels=2)
image = vgeometry.allocate()
diff --git a/Wrappers/Python/test/test_DataProcessor.py b/Wrappers/Python/test/test_DataProcessor.py
index 1c1de3a..3e6a83e 100755
--- a/Wrappers/Python/test/test_DataProcessor.py
+++ b/Wrappers/Python/test/test_DataProcessor.py
@@ -11,8 +11,32 @@ from timeit import default_timer as timer
from ccpi.framework import AX, CastDataContainer, PixelByPixelDataProcessor
+from ccpi.io.reader import NexusReader
+from ccpi.processors import CenterOfRotationFinder
+import wget
+import os
+
class TestDataProcessor(unittest.TestCase):
+ def setUp(self):
+ wget.download('https://github.com/DiamondLightSource/Savu/raw/master/test_data/data/24737_fd.nxs')
+ self.filename = '24737_fd.nxs'
+
+ def tearDown(self):
+ os.remove(self.filename)
+ def test_CenterOfRotation(self):
+ reader = NexusReader(self.filename)
+ ad = reader.get_acquisition_data_whole()
+ print (ad.geometry)
+ cf = CenterOfRotationFinder()
+ cf.set_input(ad)
+ print ("Center of rotation", cf.get_output())
+ self.assertAlmostEqual(86.25, cf.get_output())
+ def test_Normalizer(self):
+ pass
+
+
+
def test_DataProcessorChaining(self):
shape = (2,3,4,5)
size = shape[0]
diff --git a/Wrappers/Python/test/test_Gradient.py b/Wrappers/Python/test/test_Gradient.py
index c6b2d2e..4b7a034 100755
--- a/Wrappers/Python/test/test_Gradient.py
+++ b/Wrappers/Python/test/test_Gradient.py
@@ -84,3 +84,18 @@ class TestGradient(unittest.TestCase):
res = G2D.direct(u4)
print(res[0].as_array())
print(res[1].as_array())
+
+ M, N = 20, 30
+ ig = ImageGeometry(M, N)
+ arr = ig.allocate('random_int' )
+
+ # check direct of Gradient and sparse matrix
+ G = Gradient(ig)
+ norm1 = G.norm(iterations=300)
+ print ("should be sqrt(8) {} {}".format(numpy.sqrt(8), norm1))
+ numpy.testing.assert_almost_equal(norm1, numpy.sqrt(8), decimal=1)
+ ig4 = ImageGeometry(M,N, channels=3)
+ G4 = Gradient(ig4, correlation=Gradient.CORRELATION_SPACECHANNEL)
+ norm4 = G4.norm(iterations=300)
+ print ("should be sqrt(12) {} {}".format(numpy.sqrt(12), norm4))
+ self.assertTrue((norm4 - numpy.sqrt(12))/norm4 < 0.2)
diff --git a/Wrappers/Python/test/test_NexusReader.py b/Wrappers/Python/test/test_NexusReader.py
index 55543ba..a498d71 100755
--- a/Wrappers/Python/test/test_NexusReader.py
+++ b/Wrappers/Python/test/test_NexusReader.py
@@ -21,67 +21,67 @@ class TestNexusReader(unittest.TestCase):
def tearDown(self):
os.remove(self.filename)
-
- def testGetDimensions(self):
+ def testAll(self):
+ # def testGetDimensions(self):
nr = NexusReader(self.filename)
self.assertEqual(nr.get_sinogram_dimensions(), (135, 91, 160), "Sinogram dimensions are not correct")
- def testGetProjectionDimensions(self):
+ # def testGetProjectionDimensions(self):
nr = NexusReader(self.filename)
self.assertEqual(nr.get_projection_dimensions(), (91,135,160), "Projection dimensions are not correct")
- def testLoadProjectionWithoutDimensions(self):
+ # def testLoadProjectionWithoutDimensions(self):
nr = NexusReader(self.filename)
projections = nr.load_projection()
self.assertEqual(projections.shape, (91,135,160), "Loaded projection data dimensions are not correct")
- def testLoadProjectionWithDimensions(self):
+ # def testLoadProjectionWithDimensions(self):
nr = NexusReader(self.filename)
projections = nr.load_projection((slice(0,1), slice(0,135), slice(0,160)))
self.assertEqual(projections.shape, (1,135,160), "Loaded projection data dimensions are not correct")
- def testLoadProjectionCompareSingle(self):
+ # def testLoadProjectionCompareSingle(self):
nr = NexusReader(self.filename)
projections_full = nr.load_projection()
projections_part = nr.load_projection((slice(0,1), slice(0,135), slice(0,160)))
numpy.testing.assert_array_equal(projections_part, projections_full[0:1,:,:])
- def testLoadProjectionCompareMulti(self):
+ # def testLoadProjectionCompareMulti(self):
nr = NexusReader(self.filename)
projections_full = nr.load_projection()
projections_part = nr.load_projection((slice(0,3), slice(0,135), slice(0,160)))
numpy.testing.assert_array_equal(projections_part, projections_full[0:3,:,:])
- def testLoadProjectionCompareRandom(self):
+ # def testLoadProjectionCompareRandom(self):
nr = NexusReader(self.filename)
projections_full = nr.load_projection()
projections_part = nr.load_projection((slice(1,8), slice(5,10), slice(8,20)))
numpy.testing.assert_array_equal(projections_part, projections_full[1:8,5:10,8:20])
- def testLoadProjectionCompareFull(self):
+ # def testLoadProjectionCompareFull(self):
nr = NexusReader(self.filename)
projections_full = nr.load_projection()
projections_part = nr.load_projection((slice(None,None), slice(None,None), slice(None,None)))
numpy.testing.assert_array_equal(projections_part, projections_full[:,:,:])
- def testLoadFlatCompareFull(self):
+ # def testLoadFlatCompareFull(self):
nr = NexusReader(self.filename)
flats_full = nr.load_flat()
flats_part = nr.load_flat((slice(None,None), slice(None,None), slice(None,None)))
numpy.testing.assert_array_equal(flats_part, flats_full[:,:,:])
- def testLoadDarkCompareFull(self):
+ # def testLoadDarkCompareFull(self):
nr = NexusReader(self.filename)
darks_full = nr.load_dark()
darks_part = nr.load_dark((slice(None,None), slice(None,None), slice(None,None)))
numpy.testing.assert_array_equal(darks_part, darks_full[:,:,:])
- def testProjectionAngles(self):
+ # def testProjectionAngles(self):
nr = NexusReader(self.filename)
angles = nr.get_projection_angles()
self.assertEqual(angles.shape, (91,), "Loaded projection number of angles are not correct")
- def test_get_acquisition_data_subset(self):
+ # def test_get_acquisition_data_subset(self):
nr = NexusReader(self.filename)
key = nr.get_image_keys()
sl = nr.get_acquisition_data_subset(0,10)
diff --git a/Wrappers/Python/test/test_algorithms.py b/Wrappers/Python/test/test_algorithms.py
index 669804e..3bb3d57 100755
--- a/Wrappers/Python/test/test_algorithms.py
+++ b/Wrappers/Python/test/test_algorithms.py
@@ -13,11 +13,11 @@ from ccpi.framework import AcquisitionData
from ccpi.framework import ImageGeometry
from ccpi.framework import AcquisitionGeometry
from ccpi.optimisation.operators import Identity
-from ccpi.optimisation.funcs import Norm2sq
+from ccpi.optimisation.functions import Norm2Sq, ZeroFunction, \
+ L2NormSquared, FunctionOperatorComposition
from ccpi.optimisation.algorithms import GradientDescent
from ccpi.optimisation.algorithms import CGLS
from ccpi.optimisation.algorithms import FISTA
-from ccpi.optimisation.algorithms import FBPD
@@ -50,11 +50,13 @@ class TestAlgorithms(unittest.TestCase):
identity = Identity(ig)
- norm2sq = Norm2sq(identity, b)
+ norm2sq = Norm2Sq(identity, b)
+ rate = 0.3
+ rate = norm2sq.L / 3.
alg = GradientDescent(x_init=x_init,
objective_function=norm2sq,
- rate=0.3)
+ rate=rate)
alg.max_iteration = 20
alg.run(20, verbose=True)
self.assertNumpyArrayAlmostEqual(alg.x.as_array(), b.as_array())
@@ -62,10 +64,11 @@ class TestAlgorithms(unittest.TestCase):
print ("Test CGLS")
ig = ImageGeometry(124,153,154)
x_init = ImageData(geometry=ig)
+ x_init = ig.allocate()
b = x_init.copy()
# fill with random numbers
b.fill(numpy.random.random(x_init.shape))
-
+ b = ig.allocate('random')
identity = Identity(ig)
alg = CGLS(x_init=x_init, operator=identity, data=b)
@@ -80,20 +83,19 @@ class TestAlgorithms(unittest.TestCase):
b = x_init.copy()
# fill with random numbers
b.fill(numpy.random.random(x_init.shape))
- x_init = ImageData(geometry=ig)
- x_init.fill(numpy.random.random(x_init.shape))
-
+ x_init = ig.allocate(ImageGeometry.RANDOM)
identity = Identity(ig)
- norm2sq = Norm2sq(identity, b)
- norm2sq.L = 2 * norm2sq.c * identity.norm()**2
+ #### it seems FISTA does not work with Nowm2Sq
+ # norm2sq = Norm2Sq(identity, b)
+ # norm2sq.L = 2 * norm2sq.c * identity.norm()**2
+ norm2sq = FunctionOperatorComposition(L2NormSquared(b=b), identity)
opt = {'tol': 1e-4, 'memopt':False}
- alg = FISTA(x_init=x_init, f=norm2sq, g=None, opt=opt)
+ print ("initial objective", norm2sq(x_init))
+ alg = FISTA(x_init=x_init, f=norm2sq, g=ZeroFunction())
alg.max_iteration = 2
alg.run(20, verbose=True)
self.assertNumpyArrayAlmostEqual(alg.x.as_array(), b.as_array())
- alg.run(20, verbose=True)
- self.assertNumpyArrayAlmostEqual(alg.x.as_array(), b.as_array())
diff --git a/Wrappers/Python/test/test_functions.py b/Wrappers/Python/test/test_functions.py
index 523008d..021ad99 100644
--- a/Wrappers/Python/test/test_functions.py
+++ b/Wrappers/Python/test/test_functions.py
@@ -299,7 +299,7 @@ class TestFunction(unittest.TestCase):
A = 0.5 * Identity(ig)
old_chisq = Norm2Sq(A, b, 1.0)
- new_chisq = FunctionOperatorComposition(A, L2NormSquared(b=b))
+ new_chisq = FunctionOperatorComposition(L2NormSquared(b=b),A)
yold = old_chisq(u)
ynew = new_chisq(u)
diff --git a/Wrappers/Python/test/test_run_test.py b/Wrappers/Python/test/test_run_test.py
index a6c13f4..81ee738 100755
--- a/Wrappers/Python/test/test_run_test.py
+++ b/Wrappers/Python/test/test_run_test.py
@@ -10,8 +10,8 @@ from ccpi.optimisation.algorithms import FISTA
#from ccpi.optimisation.algs import FBPD
from ccpi.optimisation.functions import Norm2Sq
from ccpi.optimisation.functions import ZeroFunction
-from ccpi.optimisation.funcs import Norm1
-from ccpi.optimisation.funcs import Norm2
+# from ccpi.optimisation.funcs import Norm1
+from ccpi.optimisation.functions import L1Norm
from ccpi.optimisation.operators import LinearOperatorMatrix
from ccpi.optimisation.operators import Identity
@@ -137,8 +137,11 @@ class TestAlgorithms(unittest.TestCase):
# A = Identity()
# Change n to equal to m.
-
- b = DataContainer(bmat)
+ vgb = VectorGeometry(m)
+ vgx = VectorGeometry(n)
+ b = vgb.allocate()
+ b.fill(bmat)
+ #b = DataContainer(bmat)
# Regularization parameter
lam = 10
@@ -148,7 +151,8 @@ class TestAlgorithms(unittest.TestCase):
g0 = ZeroFunction()
# Initial guess
- x_init = DataContainer(np.zeros((n, 1)))
+ #x_init = DataContainer(np.zeros((n, 1)))
+ x_init = vgx.allocate()
# Create 1-norm object instance
g1 = Norm1(lam)
diff --git a/Wrappers/Python/wip/Demos/FISTA_vs_CGLS.py b/Wrappers/Python/wip/Demos/FISTA_vs_CGLS.py
new file mode 100644
index 0000000..2dcaa89
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/FISTA_vs_CGLS.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import FISTA, CGLS
+
+from ccpi.optimisation.operators import Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, FunctionOperatorComposition
+from skimage.util import random_noise
+from ccpi.astra.ops import AstraProjectorSimple
+
+#%%
+
+N = 75
+x = np.zeros((N,N))
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+data = ImageData(x)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+Aop = AstraProjectorSimple(ig, ag, 'cpu')
+sin = Aop.direct(data)
+
+noisy_data = sin
+
+fidelity = FunctionOperatorComposition(L2NormSquared(b=noisy_data), Aop)
+regularizer = ZeroFunction()
+
+x_init = ig.allocate()
+
+## Setup and run the FISTA algorithm
+opt = {'tol': 1e-4, 'memopt':True}
+fista = FISTA(x_init=x_init , f=fidelity, g=regularizer, opt=opt)
+fista.max_iteration = 500
+fista.update_objective_interval = 50
+fista.run(500, verbose=True)
+
+## Setup and run the CGLS algorithm
+cgls = CGLS(x_init=x_init, operator=Aop, data=noisy_data)
+cgls.max_iteration = 500
+cgls.update_objective_interval = 50
+cgls.run(500, verbose=True)
+
+diff = fista.get_output() - cgls.get_output()
+
+
+#%%
+print( diff.norm())
+
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(fista.get_output().as_array())
+plt.title('FISTA reconstruction')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(cgls.get_output().as_array())
+plt.title('CGLS reconstruction')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(diff.abs().as_array())
+plt.title('Difference reconstruction')
+plt.colorbar()
+plt.show()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wrappers/Python/wip/Demos/FISTA_vs_PDHG.py b/Wrappers/Python/wip/Demos/FISTA_vs_PDHG.py
new file mode 100644
index 0000000..b7777ef
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/FISTA_vs_PDHG.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import FISTA, PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient, Identity
+from ccpi.optimisation.functions import L2NormSquared, L1Norm, \
+ MixedL21Norm, FunctionOperatorComposition, BlockFunction, ZeroFunction
+
+from skimage.util import random_noise
+
+# Create phantom for TV Gaussian denoising
+N = 100
+
+data = np.zeros((N,N))
+data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+data = ImageData(data)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+ag = ig
+
+# Create noisy data. Add Gaussian noise
+n1 = random_noise(data.as_array(), mode = 's&p', salt_vs_pepper = 0.9, amount=0.2)
+noisy_data = ImageData(n1)
+
+# Regularisation Parameter
+alpha = 5
+
+operator = Gradient(ig)
+
+fidelity = L1Norm(b=noisy_data)
+regulariser = FunctionOperatorComposition(alpha * L2NormSquared(), operator)
+
+x_init = ig.allocate()
+
+## Setup and run the PDHG algorithm
+opt = {'tol': 1e-4, 'memopt':True}
+fista = FISTA(x_init=x_init , f=regulariser, g=fidelity, opt=opt)
+fista.max_iteration = 2000
+fista.update_objective_interval = 50
+fista.run(2000, verbose=True)
+
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(fista.get_output().as_array())
+plt.title('TV Reconstruction')
+plt.colorbar()
+plt.show()
+
+# Compare with PDHG
+# Create operators
+op1 = Gradient(ig)
+op2 = Identity(ig, ag)
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+f = BlockFunction(alpha * L2NormSquared(), fidelity)
+g = ZeroFunction()
+
+## Compute operator Norm
+normK = operator.norm()
+#
+## Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+#
+#
+## Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 50
+pdhg.run(2000)
+#
+#%%
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(fista.get_output().as_array())
+plt.title('FISTA')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('PDHG')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(np.abs(pdhg.get_output().as_array()-fista.get_output().as_array()))
+plt.title('Diff FISTA-PDHG')
+plt.colorbar()
+plt.show()
+
+
diff --git a/Wrappers/Python/wip/Demos/IMAT_Reconstruction/TV_WhiteBeam_reconstruction.py b/Wrappers/Python/wip/Demos/IMAT_Reconstruction/TV_WhiteBeam_reconstruction.py
new file mode 100644
index 0000000..e67bdb1
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/IMAT_Reconstruction/TV_WhiteBeam_reconstruction.py
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageGeometry, AcquisitionGeometry, AcquisitionData
+from astropy.io import fits
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, KullbackLeibler, L2NormSquared,\
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorSimple
+
+
+# load IMAT file
+#filename_sino = '/media/newhd/shared/DataProcessed/IMAT_beamtime_Feb_2019/preprocessed_test_flat/sino/rebin_slice_350/sino_log_rebin_141.fits'
+filename_sino = '/media/newhd/shared/DataProcessed/IMAT_beamtime_Feb_2019/preprocessed_test_flat/sino/rebin_slice_350/sino_log_rebin_564.fits'
+
+sino_handler = fits.open(filename_sino)
+sino_tmp = numpy.array(sino_handler[0].data, dtype=float)
+# reorder sino coordinate: channels, angles, detectors
+sinogram = numpy.rollaxis(sino_tmp, 2)
+sino_handler.close()
+#%%
+# white beam data
+sinogram_wb = sinogram.sum(axis=0)
+
+pixh = sinogram_wb.shape[1] # detectors
+pixv = sinogram_wb.shape[1] # detectors
+
+# WhiteBeam Geometry
+igWB = ImageGeometry(voxel_num_x = pixh, voxel_num_y = pixv)
+
+# Load Golden angles
+with open("golden_angles.txt") as f:
+ angles_string = [line.rstrip() for line in f]
+ angles = numpy.array(angles_string).astype(float)
+agWB = AcquisitionGeometry('parallel', '2D', angles * numpy.pi / 180, pixh)
+op_WB = AstraProjectorSimple(igWB, agWB, 'gpu')
+sinogram_aqdata = AcquisitionData(sinogram_wb, agWB)
+
+# BackProjection
+result_bp = op_WB.adjoint(sinogram_aqdata)
+
+plt.imshow(result_bp.subset(channel=50).array)
+plt.title('BackProjection')
+plt.show()
+
+
+
+#%%
+
+# Regularisation Parameter
+alpha = 2000
+
+# Create operators
+op1 = Gradient(igWB)
+op2 = op_WB
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = KullbackLeibler(sinogram_aqdata)
+#f2 = L2NormSquared(b = sinogram_aqdata)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+diag_precon = False
+
+if diag_precon:
+
+ def tau_sigma_precond(operator):
+
+ tau = 1/operator.sum_abs_row()
+ sigma = 1/ operator.sum_abs_col()
+
+ return tau, sigma
+
+ tau, sigma = tau_sigma_precond(operator)
+
+else:
+ # Compute operator Norm
+ normK = operator.norm()
+ print ("normK", normK)
+ # Primal & dual stepsizes
+ sigma = 0.1
+ tau = 1/(sigma*normK**2)
+
+#%%
+
+
+## Primal & dual stepsizes
+#sigma = 0.1
+#tau = 1/(sigma*normK**2)
+#
+#
+## Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 10000
+pdhg.update_objective_interval = 500
+
+def circ_mask(h, w, center=None, radius=None):
+
+ if center is None: # use the middle of the image
+ center = [int(w/2), int(h/2)]
+ if radius is None: # use the smallest distance between the center and image walls
+ radius = min(center[0], center[1], w-center[0], h-center[1])
+
+ Y, X = numpy.ogrid[:h, :w]
+ dist_from_center = numpy.sqrt((X - center[0])**2 + (Y-center[1])**2)
+
+ mask = dist_from_center <= radius
+ return mask
+
+def show_result(niter, objective, solution):
+
+ mask = circ_mask(pixh, pixv, center=None, radius = 220) # 55 with 141,
+ plt.imshow(solution.as_array() * mask)
+ plt.colorbar()
+ plt.title("Iter: {}".format(niter))
+ plt.show()
+
+
+ print( "{:04}/{:04} {:<5} {:.4f} {:<5} {:.4f} {:<5} {:.4f}".\
+ format(niter, pdhg.max_iteration,'', \
+ objective[0],'',\
+ objective[1],'',\
+ objective[2]))
+
+pdhg.run(10000, callback = show_result)
+
+#%%
+
+mask = circ_mask(pixh, pixv, center=None, radius = 210) # 55 with 141,
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(pdhg.get_output().as_array() * mask)
+plt.title('Ground Truth')
+plt.colorbar()
+plt.show()
diff --git a/Wrappers/Python/wip/Demos/IMAT_Reconstruction/golden_angles.txt b/Wrappers/Python/wip/Demos/IMAT_Reconstruction/golden_angles.txt
new file mode 100644
index 0000000..95ce73a
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/IMAT_Reconstruction/golden_angles.txt
@@ -0,0 +1,186 @@
+0
+0.9045
+1.809
+2.368
+3.2725
+4.736
+5.6405
+6.1995
+7.104
+8.5675
+9.472
+10.9356
+11.8401
+12.3991
+13.3036
+14.7671
+15.6716
+16.2306
+17.1351
+18.0396
+18.5986
+19.5031
+20.9666
+21.8711
+22.4301
+23.3346
+24.7981
+25.7026
+27.1661
+28.0706
+28.6297
+29.5342
+30.9977
+31.9022
+32.4612
+33.3657
+34.8292
+35.7337
+37.1972
+38.1017
+38.6607
+39.5652
+41.0287
+41.9332
+42.4922
+43.3967
+44.3012
+44.8602
+45.7647
+47.2283
+48.1328
+48.6918
+49.5963
+51.0598
+51.9643
+53.4278
+54.3323
+54.8913
+55.7958
+57.2593
+58.1638
+58.7228
+59.6273
+60.5318
+61.0908
+61.9953
+63.4588
+64.3633
+64.9224
+65.8269
+67.2904
+68.1949
+69.6584
+70.5629
+71.1219
+72.0264
+73.4899
+74.3944
+74.9534
+75.8579
+77.3214
+78.2259
+79.6894
+80.5939
+81.1529
+82.0574
+83.521
+84.4255
+84.9845
+85.889
+86.7935
+87.3525
+88.257
+89.7205
+90.625
+91.184
+92.0885
+93.552
+94.4565
+95.92
+96.8245
+97.3835
+98.288
+99.7516
+100.656
+101.215
+102.12
+103.583
+104.488
+105.951
+106.856
+107.415
+108.319
+109.783
+110.687
+111.246
+112.151
+113.055
+113.614
+114.519
+115.982
+116.887
+117.446
+118.35
+119.814
+120.718
+122.182
+123.086
+123.645
+124.55
+126.013
+126.918
+127.477
+128.381
+129.286
+129.845
+130.749
+132.213
+133.117
+133.676
+134.581
+136.044
+136.949
+138.412
+139.317
+139.876
+140.78
+142.244
+143.148
+143.707
+144.612
+146.075
+146.98
+148.443
+149.348
+149.907
+150.811
+152.275
+153.179
+153.738
+154.643
+155.547
+156.106
+157.011
+158.474
+159.379
+159.938
+160.842
+162.306
+163.21
+164.674
+165.578
+166.137
+167.042
+168.505
+169.41
+169.969
+170.873
+172.337
+173.242
+174.705
+175.609
+176.168
+177.073
+178.536
+179.441
diff --git a/Wrappers/Python/wip/Demos/LeastSq_CGLS_FISTA_PDHG.py b/Wrappers/Python/wip/Demos/LeastSq_CGLS_FISTA_PDHG.py
new file mode 100644
index 0000000..97c71ba
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/LeastSq_CGLS_FISTA_PDHG.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG, CGLS, FISTA
+
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, FunctionOperatorComposition
+from ccpi.astra.ops import AstraProjectorSimple
+
+#%%
+
+N = 68
+x = np.zeros((N,N))
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+data = ImageData(x)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+Aop = AstraProjectorSimple(ig, ag, 'cpu')
+sin = Aop.direct(data)
+
+noisy_data = sin
+
+
+#%%
+###############################################################################
+## Setup and run the CGLS algorithm
+
+x_init = ig.allocate()
+cgls = CGLS(x_init=x_init, operator=Aop, data=noisy_data)
+cgls.max_iteration = 500
+cgls.update_objective_interval = 50
+cgls.run(500, verbose=True)
+
+#%%
+plt.imshow(cgls.get_output().as_array())
+#%%
+###############################################################################
+## Setup and run the PDHG algorithm
+
+operator = Aop
+f = L2NormSquared(b = noisy_data)
+g = ZeroFunction()
+
+## Compute operator Norm
+normK = operator.norm()
+
+## Primal & dual stepsizes
+sigma = 0.1
+tau = 1/(sigma*normK**2)
+
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 50
+pdhg.run(2000)
+
+
+#%%
+###############################################################################
+## Setup and run the FISTA algorithm
+
+fidelity = FunctionOperatorComposition(L2NormSquared(b=noisy_data), Aop)
+regularizer = ZeroFunction()
+
+## Setup and run the FISTA algorithm
+opt = {'memopt':True}
+fista = FISTA(x_init=x_init , f=fidelity, g=regularizer, opt=opt)
+fista.max_iteration = 2000
+fista.update_objective_interval = 200
+fista.run(2000, verbose=True)
+
+#%% Show results
+
+diff1 = pdhg.get_output() - cgls.get_output()
+diff2 = fista.get_output() - cgls.get_output()
+
+print( diff1.norm())
+print( diff2.norm())
+
+plt.figure(figsize=(10,10))
+plt.subplot(2,3,1)
+plt.imshow(cgls.get_output().as_array())
+plt.title('CGLS reconstruction')
+plt.subplot(2,3,2)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('PDHG reconstruction')
+plt.subplot(2,3,3)
+plt.imshow(fista.get_output().as_array())
+plt.title('FISTA reconstruction')
+plt.subplot(2,3,4)
+plt.imshow(diff1.abs().as_array())
+plt.title('Diff PDHG vs CGLS')
+plt.colorbar()
+plt.subplot(2,3,5)
+plt.imshow(diff2.abs().as_array())
+plt.title('Diff FISTA vs CGLS')
+plt.colorbar()
+plt.show()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+#
+#
+#
+#
+#
+#
+#
diff --git a/Wrappers/Python/wip/Demos/PDHG_TGV_Denoising_SaltPepper.py b/Wrappers/Python/wip/Demos/PDHG_TGV_Denoising_SaltPepper.py
new file mode 100644
index 0000000..7b65c31
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/PDHG_TGV_Denoising_SaltPepper.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Feb 22 14:53:03 2019
+
+@author: evangelos
+"""
+
+from ccpi.framework import ImageData, ImageGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Identity, \
+ Gradient, SymmetrizedGradient, ZeroOperator
+from ccpi.optimisation.functions import ZeroFunction, L1Norm, \
+ MixedL21Norm, BlockFunction
+
+from skimage.util import random_noise
+
+# Create phantom for TGV SaltPepper denoising
+
+N = 100
+
+data = np.zeros((N,N))
+
+x1 = np.linspace(0, int(N/2), N)
+x2 = np.linspace(int(N/2), 0., N)
+xv, yv = np.meshgrid(x1, x2)
+
+xv[int(N/4):int(3*N/4)-1, int(N/4):int(3*N/4)-1] = yv[int(N/4):int(3*N/4)-1, int(N/4):int(3*N/4)-1].T
+
+data = xv
+data = ImageData(data/data.max())
+
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+ag = ig
+
+# Create noisy data. Add Gaussian noise
+n1 = random_noise(data.as_array(), mode = 's&p', salt_vs_pepper = 0.9, amount=0.2)
+noisy_data = ImageData(n1)
+
+# Regularisation Parameters
+alpha = 0.8
+beta = numpy.sqrt(2)* alpha
+
+method = '1'
+
+if method == '0':
+
+ # Create operators
+ op11 = Gradient(ig)
+ op12 = Identity(op11.range_geometry())
+
+ op22 = SymmetrizedGradient(op11.domain_geometry())
+ op21 = ZeroOperator(ig, op22.range_geometry())
+
+ op31 = Identity(ig, ag)
+ op32 = ZeroOperator(op22.domain_geometry(), ag)
+
+ operator = BlockOperator(op11, -1*op12, op21, op22, op31, op32, shape=(3,2) )
+
+ f1 = alpha * MixedL21Norm()
+ f2 = beta * MixedL21Norm()
+ f3 = L1Norm(b=noisy_data)
+ f = BlockFunction(f1, f2, f3)
+ g = ZeroFunction()
+
+else:
+
+ # Create operators
+ op11 = Gradient(ig)
+ op12 = Identity(op11.range_geometry())
+ op22 = SymmetrizedGradient(op11.domain_geometry())
+ op21 = ZeroOperator(ig, op22.range_geometry())
+
+ operator = BlockOperator(op11, -1*op12, op21, op22, shape=(2,2) )
+
+ f1 = alpha * MixedL21Norm()
+ f2 = beta * MixedL21Norm()
+
+ f = BlockFunction(f1, f2)
+ g = BlockFunction(L1Norm(b=noisy_data), ZeroFunction())
+
+## Compute operator Norm
+normK = operator.norm()
+#
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 50
+pdhg.run(2000)
+
+#%%
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(pdhg.get_output()[0].as_array())
+plt.title('TGV Reconstruction')
+plt.colorbar()
+plt.show()
+##
+plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,N,N), pdhg.get_output()[0].as_array()[int(N/2),:], label = 'TV reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
+#%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+if cvx_not_installable:
+
+ u = Variable(ig.shape)
+ w1 = Variable((N, N))
+ w2 = Variable((N, N))
+
+ # create TGV regulariser
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ regulariser = alpha * sum(norm(vstack([DX.matrix() * vec(u) - vec(w1), \
+ DY.matrix() * vec(u) - vec(w2)]), 2, axis = 0)) + \
+ beta * sum(norm(vstack([ DX.matrix().transpose() * vec(w1), DY.matrix().transpose() * vec(w2), \
+ 0.5 * ( DX.matrix().transpose() * vec(w2) + DY.matrix().transpose() * vec(w1) ), \
+ 0.5 * ( DX.matrix().transpose() * vec(w2) + DY.matrix().transpose() * vec(w1) ) ]), 2, axis = 0 ) )
+
+ constraints = []
+ fidelity = pnorm(u - noisy_data.as_array(),1)
+ solver = MOSEK
+
+ # choose solver
+ if 'MOSEK' in installed_solvers():
+ solver = MOSEK
+ else:
+ solver = SCS
+
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj)
+ result = prob.solve(verbose = True, solver = solver)
+
+ diff_cvx = numpy.abs( pdhg.get_output()[0].as_array() - u.value )
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(pdhg.get_output()[0].as_array())
+ plt.title('PDHG solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(u.value)
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,N,N), pdhg.get_output()[0].as_array()[int(N/2),:], label = 'PDHG')
+ plt.plot(np.linspace(0,N,N), u.value[int(N/2),:], label = 'CVX')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (PDHG) {} '.format(pdhg.objective[-1][0]))
+
+
+
+
+
diff --git a/Wrappers/Python/wip/Demos/PDHG_TV_Denoising_Gaussian_DiagPrecond.py b/Wrappers/Python/wip/Demos/PDHG_TV_Denoising_Gaussian_DiagPrecond.py
new file mode 100644
index 0000000..d65478c
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/PDHG_TV_Denoising_Gaussian_DiagPrecond.py
@@ -0,0 +1,208 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""
+Total Variation Denoising using PDHG algorithm
+
+Problem: min_x \alpha * ||\nabla x||_{1} + || x - g ||_{2}^{2}
+
+ \nabla: Gradient operator
+ g: Noisy Data with Gaussian Noise
+ \alpha: Regularization parameter
+
+"""
+
+from ccpi.framework import ImageData, ImageGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction
+
+
+# Create phantom for TV Gaussian denoising
+N = 400
+
+data = np.zeros((N,N))
+data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+data = ImageData(data)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+ag = ig
+
+# Create noisy data. Add Gaussian noise
+np.random.seed(10)
+noisy_data = ImageData( data.as_array() + np.random.normal(0, 0.05, size=ig.shape) )
+
+# Regularisation Parameter
+alpha = 2
+
+method = '1'
+
+if method == '0':
+
+ # Create operators
+ op1 = Gradient(ig)
+ op2 = Identity(ig, ag)
+
+ # Create BlockOperator
+ operator = BlockOperator(op1, op2, shape=(2,1) )
+
+ # Create functions
+
+ f1 = alpha * MixedL21Norm()
+ f2 = 0.5 * L2NormSquared(b = noisy_data)
+ f = BlockFunction(f1, f2)
+
+ g = ZeroFunction()
+
+else:
+
+ # Without the "Block Framework"
+ operator = Gradient(ig)
+ f = alpha * MixedL21Norm()
+ g = 0.5 * L2NormSquared(b = noisy_data)
+
+
+diag_precon = False
+
+
+if diag_precon:
+
+ def tau_sigma_precond(operator):
+
+ tau = 1/operator.sum_abs_col()
+ sigma = 1/operator.sum_abs_row()
+
+ sigma[0].as_array()[sigma[0].as_array()==np.inf]=0
+ sigma[1].as_array()[sigma[1].as_array()==np.inf]=0
+
+ return tau, sigma
+
+ tau, sigma = tau_sigma_precond(operator)
+
+else:
+ # Compute operator Norm
+ normK = operator.norm()
+
+ # Primal & dual stepsizes
+ sigma = 1
+ tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 3000
+pdhg.update_objective_interval = 200
+pdhg.run(3000, verbose=False)
+
+#%%
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('TV Reconstruction')
+plt.colorbar()
+plt.show()
+##
+plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,N,N), pdhg.get_output().as_array()[int(N/2),:], label = 'TV reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
+#%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+
+if cvx_not_installable:
+
+ ##Construct problem
+ u = Variable(ig.shape)
+
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ # Define Total Variation as a regulariser
+ regulariser = alpha * sum(norm(vstack([DX.matrix() * vec(u), DY.matrix() * vec(u)]), 2, axis = 0))
+ fidelity = 0.5 * sum_squares(u - noisy_data.as_array())
+
+ # choose solver
+ if 'MOSEK' in installed_solvers():
+ solver = MOSEK
+ else:
+ solver = SCS
+
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj)
+ result = prob.solve(verbose = True, solver = MOSEK)
+
+ diff_cvx = numpy.abs( pdhg.get_output().as_array() - u.value )
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(pdhg.get_output().as_array())
+ plt.title('PDHG solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(u.value)
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,N,N), pdhg.get_output().as_array()[int(N/2),:], label = 'PDHG')
+ plt.plot(np.linspace(0,N,N), u.value[int(N/2),:], label = 'CVX')
+ plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'Truth')
+
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (PDHG) {} '.format(pdhg.objective[-1][0]))
+
+
+
+
+
diff --git a/Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D.py b/Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D.py
new file mode 100644
index 0000000..87d5328
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D.py
@@ -0,0 +1,245 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
+from ccpi.optimisation.functions import ZeroFunction, KullbackLeibler, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorSimple
+
+"""
+
+Total Variation Denoising using PDHG algorithm:
+
+ min_{x} max_{y} < K x, y > + g(x) - f^{*}(y)
+
+
+Problem: min_x, x>0 \alpha * ||\nabla x||_{1} + int A x -g log(Ax + \eta)
+
+ \nabla: Gradient operator
+
+ A: Projection Matrix
+ g: Noisy sinogram corrupted with Poisson Noise
+
+ \eta: Background Noise
+ \alpha: Regularization parameter
+
+"""
+
+# Create phantom for TV 2D tomography
+N = 75
+x = np.zeros((N,N))
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+data = ImageData(x)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+Aop = AstraProjectorSimple(ig, ag, 'cpu')
+sin = Aop.direct(data)
+
+# Create noisy data. Apply Poisson noise
+scale = 2
+n1 = scale * np.random.poisson(sin.as_array()/scale)
+noisy_data = AcquisitionData(n1, ag)
+
+# Regularisation Parameter
+alpha = 5
+
+# Create operators
+op1 = Gradient(ig)
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = KullbackLeibler(noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+diag_precon = True
+
+if diag_precon:
+
+ def tau_sigma_precond(operator):
+
+ tau = 1/operator.sum_abs_row()
+ sigma = 1/ operator.sum_abs_col()
+
+ return tau, sigma
+
+ tau, sigma = tau_sigma_precond(operator)
+
+else:
+ # Compute operator Norm
+ normK = operator.norm()
+ # Primal & dual stepsizes
+ sigma = 10
+ tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 50
+pdhg.run(2000)
+
+
+#%%
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('TV Reconstruction')
+plt.colorbar()
+plt.show()
+##
+plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,N,N), pdhg.get_output().as_array()[int(N/2),:], label = 'TV reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
+#%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+import astra
+import numpy
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+
+if cvx_not_installable:
+
+
+ ##Construct problem
+ u = Variable(N*N)
+ #q = Variable()
+
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ regulariser = alpha * sum(norm(vstack([DX.matrix() * vec(u), DY.matrix() * vec(u)]), 2, axis = 0))
+
+ # create matrix representation for Astra operator
+
+ vol_geom = astra.create_vol_geom(N, N)
+ proj_geom = astra.create_proj_geom('parallel', 1.0, detectors, angles)
+
+ proj_id = astra.create_projector('strip', proj_geom, vol_geom)
+
+ matrix_id = astra.projector.matrix(proj_id)
+
+ ProjMat = astra.matrix.get(matrix_id)
+
+ fidelity = sum( ProjMat * u - noisy_data.as_array().ravel() * log(ProjMat * u))
+ #constraints = [q>= fidelity, u>=0]
+ constraints = [u>=0]
+
+ solver = SCS
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj, constraints)
+ result = prob.solve(verbose = True, solver = solver)
+
+
+##%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+
+if cvx_not_installable:
+
+ ##Construct problem
+ u = Variable(ig.shape)
+
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ # Define Total Variation as a regulariser
+ regulariser = alpha * sum(norm(vstack([DX.matrix() * vec(u), DY.matrix() * vec(u)]), 2, axis = 0))
+ fidelity = pnorm( u - noisy_data.as_array(),1)
+
+ # choose solver
+ if 'MOSEK' in installed_solvers():
+ solver = MOSEK
+ else:
+ solver = SCS
+
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj)
+ result = prob.solve(verbose = True, solver = solver)
+
+
+ plt.figure(figsize=(15,15))
+ plt.subplot(3,1,1)
+ plt.imshow(pdhg.get_output().as_array())
+ plt.title('PDHG solution')
+ plt.colorbar()
+ plt.subplot(3,1,2)
+ plt.imshow(np.reshape(u.value, (N, N)))
+ plt.title('CVX solution')
+ plt.colorbar()
+ plt.subplot(3,1,3)
+ plt.imshow(diff_cvx)
+ plt.title('Difference')
+ plt.colorbar()
+ plt.show()
+
+ plt.plot(np.linspace(0,N,N), pdhg.get_output().as_array()[int(N/2),:], label = 'PDHG')
+ plt.plot(np.linspace(0,N,N), u.value[int(N/2),:], label = 'CVX')
+ plt.legend()
+ plt.title('Middle Line Profiles')
+ plt.show()
+
+ print('Primal Objective (CVX) {} '.format(obj.value))
+ print('Primal Objective (PDHG) {} '.format(pdhg.objective[-1][0])) \ No newline at end of file
diff --git a/Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D_time.py b/Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D_time.py
new file mode 100644
index 0000000..045458a
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/PDHG_TV_Tomo2D_time.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, KullbackLeibler, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorMC
+
+import os
+import tomophantom
+from tomophantom import TomoP2D
+
+# Create phantom for TV 2D dynamic tomography
+
+model = 102 # note that the selected model is temporal (2D + time)
+N = 50 # set dimension of the phantom
+# one can specify an exact path to the parameters file
+# path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
+path = os.path.dirname(tomophantom.__file__)
+path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
+#This will generate a N_size x N_size x Time frames phantom (2D + time)
+phantom_2Dt = TomoP2D.ModelTemporal(model, N, path_library2D)
+
+plt.close('all')
+plt.figure(1)
+plt.rcParams.update({'font.size': 21})
+plt.title('{}''{}'.format('2D+t phantom using model no.',model))
+for sl in range(0,np.shape(phantom_2Dt)[0]):
+ im = phantom_2Dt[sl,:,:]
+ plt.imshow(im, vmin=0, vmax=1)
+ plt.pause(.1)
+ plt.draw
+
+
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N, channels = np.shape(phantom_2Dt)[0])
+data = ImageData(phantom_2Dt, geometry=ig)
+
+detectors = N
+angles = np.linspace(0,np.pi,N)
+
+ag = AcquisitionGeometry('parallel','2D', angles, detectors, channels = np.shape(phantom_2Dt)[0])
+Aop = AstraProjectorMC(ig, ag, 'gpu')
+sin = Aop.direct(data)
+
+scale = 2
+n1 = scale * np.random.poisson(sin.as_array()/scale)
+noisy_data = AcquisitionData(n1, ag)
+
+tindex = [3, 6, 10]
+
+fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 10))
+plt.subplot(1,3,1)
+plt.imshow(noisy_data.as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+plt.subplot(1,3,2)
+plt.imshow(noisy_data.as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+plt.subplot(1,3,3)
+plt.imshow(noisy_data.as_array()[tindex[2],:,:])
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+plt.show()
+
+#%%
+# Regularisation Parameter
+alpha = 5
+
+# Create operators
+#op1 = Gradient(ig)
+op1 = Gradient(ig, correlation='SpaceChannels')
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = KullbackLeibler(noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 200
+pdhg.run(2000)
+
+
+#%%
+fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8))
+
+plt.subplot(2,3,1)
+plt.imshow(phantom_2Dt[tindex[0],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[0]))
+
+plt.subplot(2,3,2)
+plt.imshow(phantom_2Dt[tindex[1],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[1]))
+
+plt.subplot(2,3,3)
+plt.imshow(phantom_2Dt[tindex[2],:,:],vmin=0, vmax=1)
+plt.axis('off')
+plt.title('Time {}'.format(tindex[2]))
+
+
+plt.subplot(2,3,4)
+plt.imshow(pdhg.get_output().as_array()[tindex[0],:,:])
+plt.axis('off')
+plt.subplot(2,3,5)
+plt.imshow(pdhg.get_output().as_array()[tindex[1],:,:])
+plt.axis('off')
+plt.subplot(2,3,6)
+plt.imshow(pdhg.get_output().as_array()[tindex[2],:,:])
+plt.axis('off')
+im = plt.imshow(pdhg.get_output().as_array()[tindex[0],:,:])
+
+
+fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
+ wspace=0.02, hspace=0.02)
+
+cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
+cbar = fig.colorbar(im, cax=cb_ax)
+
+
+plt.show()
+
diff --git a/Wrappers/Python/wip/Demos/PDHG_Tikhonov_Tomo2D.py b/Wrappers/Python/wip/Demos/PDHG_Tikhonov_Tomo2D.py
new file mode 100644
index 0000000..f17c4fe
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/PDHG_Tikhonov_Tomo2D.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, BlockFunction
+from skimage.util import random_noise
+from ccpi.astra.ops import AstraProjectorSimple
+
+# Create phantom for TV 2D tomography
+N = 75
+x = np.zeros((N,N))
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+data = ImageData(x)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+Aop = AstraProjectorSimple(ig, ag, 'gpu')
+sin = Aop.direct(data)
+
+# Create noisy data. Apply Gaussian noise
+
+np.random.seed(10)
+noisy_data = sin + AcquisitionData(np.random.normal(0, 3, sin.shape))
+
+# Regularisation Parameter
+alpha = 500
+
+# Create operators
+op1 = Gradient(ig)
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+# Create functions
+
+f1 = alpha * L2NormSquared()
+f2 = 0.5 * L2NormSquared(b=noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+# Compute operator Norm
+normK = operator.norm()
+
+# Primal & dual stepsizes
+sigma = 1
+tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 5000
+pdhg.update_objective_interval = 50
+pdhg.run(2000)
+
+#%%
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('Tikhonov Reconstruction')
+plt.colorbar()
+plt.show()
+##
+plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'GTruth')
+plt.plot(np.linspace(0,N,N), pdhg.get_output().as_array()[int(N/2),:], label = 'Tikhonov reconstruction')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
diff --git a/Wrappers/Python/wip/Demos/PDHG_vs_CGLS.py b/Wrappers/Python/wip/Demos/PDHG_vs_CGLS.py
new file mode 100644
index 0000000..3155654
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/PDHG_vs_CGLS.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, AcquisitionData
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG, CGLS
+
+from ccpi.optimisation.operators import Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, FunctionOperatorComposition
+from skimage.util import random_noise
+from ccpi.astra.ops import AstraProjectorSimple
+
+#%%
+
+N = 128
+x = np.zeros((N,N))
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+data = ImageData(x)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+Aop = AstraProjectorSimple(ig, ag, 'cpu')
+sin = Aop.direct(data)
+
+noisy_data = sin
+
+x_init = ig.allocate()
+
+## Setup and run the CGLS algorithm
+cgls = CGLS(x_init=x_init, operator=Aop, data=noisy_data)
+cgls.max_iteration = 500
+cgls.update_objective_interval = 50
+cgls.run(500, verbose=True)
+
+# Create BlockOperator
+operator = Aop
+f = 0.5 * L2NormSquared(b = noisy_data)
+g = ZeroFunction()
+
+## Compute operator Norm
+normK = operator.norm()
+
+## Primal & dual stepsizes
+sigma = 0.1
+tau = 1/(sigma*normK**2)
+#
+#
+## Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 2000
+pdhg.update_objective_interval = 50
+pdhg.run(2000)
+
+#%%
+
+diff = pdhg.get_output() - cgls.get_output()
+print( diff.norm())
+#
+plt.figure(figsize=(15,15))
+plt.subplot(3,1,1)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('PDHG reconstruction')
+plt.colorbar()
+plt.subplot(3,1,2)
+plt.imshow(cgls.get_output().as_array())
+plt.title('CGLS reconstruction')
+plt.colorbar()
+plt.subplot(3,1,3)
+plt.imshow(diff.abs().as_array())
+plt.title('Difference reconstruction')
+plt.colorbar()
+plt.show()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+#
+#
+#
+#
+#
+#
+#
diff --git a/Wrappers/Python/wip/Demos/check_blockOperator_sum_row_cols.py b/Wrappers/Python/wip/Demos/check_blockOperator_sum_row_cols.py
new file mode 100644
index 0000000..bdb2c38
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/check_blockOperator_sum_row_cols.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Fri May 3 13:10:09 2019
+
+@author: evangelos
+"""
+
+from ccpi.optimisation.operators import FiniteDiff, SparseFiniteDiff, BlockOperator, Gradient
+from ccpi.framework import ImageGeometry, AcquisitionGeometry, BlockDataContainer, ImageData
+from ccpi.astra.ops import AstraProjectorSimple
+
+from scipy import sparse
+import numpy as np
+
+N = 3
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+u = ig.allocate('random_int')
+
+# Compare FiniteDiff with SparseFiniteDiff
+
+DY = FiniteDiff(ig, direction = 0, bnd_cond = 'Neumann')
+DX = FiniteDiff(ig, direction = 1, bnd_cond = 'Neumann')
+
+DXu = DX.direct(u)
+DYu = DY.direct(u)
+
+DX_sparse = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+DY_sparse = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+
+DXu_sparse = DX_sparse.direct(u)
+DYu_sparse = DY_sparse.direct(u)
+
+#np.testing.assert_array_almost_equal(DYu.as_array(), DYu_sparse.as_array(), decimal=4)
+#np.testing.assert_array_almost_equal(DXu.as_array(), DXu_sparse.as_array(), decimal=4)
+
+#%% Tau/ Sigma
+
+A1 = DY_sparse.matrix()
+A2 = DX_sparse.matrix()
+A3 = sparse.eye(np.prod(ig.shape))
+
+sum_rows1 = np.array(np.sum(abs(A1), axis=1))
+sum_rows2 = np.array(np.sum(abs(A2), axis=1))
+sum_rows3 = np.array(np.sum(abs(A3), axis=1))
+
+sum_cols1 = np.array(np.sum(abs(A1), axis=0))
+sum_cols2 = np.array(np.sum(abs(A2), axis=0))
+sum_cols3 = np.array(np.sum(abs(A2), axis=0))
+
+# Check if Grad sum row/cols is OK
+Grad = Gradient(ig)
+
+Sum_Block_row = Grad.sum_abs_row()
+Sum_Block_col = Grad.sum_abs_col()
+
+tmp1 = BlockDataContainer( ImageData(np.reshape(sum_rows1, ig.shape, order='F')),\
+ ImageData(np.reshape(sum_rows2, ig.shape, order='F')))
+
+
+#np.testing.assert_array_almost_equal(tmp1[0].as_array(), Sum_Block_row[0].as_array(), decimal=4)
+#np.testing.assert_array_almost_equal(tmp1[1].as_array(), Sum_Block_row[1].as_array(), decimal=4)
+
+tmp2 = ImageData(np.reshape(sum_cols1 + sum_cols2, ig.shape, order='F'))
+
+#np.testing.assert_array_almost_equal(tmp2.as_array(), Sum_Block_col.as_array(), decimal=4)
+
+
+#%% BlockOperator with Gradient, Identity
+
+Id = Identity(ig)
+Block_GrId = BlockOperator(Grad, Id, shape=(2,1))
+
+
+Sum_Block_GrId_row = Block_GrId.sum_abs_row()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wrappers/Python/wip/Demos/check_precond.py b/Wrappers/Python/wip/Demos/check_precond.py
new file mode 100644
index 0000000..8cf95fa
--- /dev/null
+++ b/Wrappers/Python/wip/Demos/check_precond.py
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+# This work is part of the Core Imaging Library developed by
+# Visual Analytics and Imaging System Group of the Science Technology
+# Facilities Council, STFC
+
+# Copyright 2018-2019 Evangelos Papoutsellis and Edoardo Pasca
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry
+
+import numpy as np
+import numpy
+import matplotlib.pyplot as plt
+
+from ccpi.optimisation.algorithms import PDHG
+
+from ccpi.optimisation.operators import BlockOperator, Gradient
+from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
+ MixedL21Norm, BlockFunction
+
+from ccpi.astra.ops import AstraProjectorSimple
+
+# Create phantom for TV 2D tomography
+N = 75
+x = np.zeros((N,N))
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+data = ImageData(x)
+ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
+
+detectors = N
+angles = np.linspace(0, np.pi, N, dtype=np.float32)
+
+ag = AcquisitionGeometry('parallel','2D',angles, detectors)
+Aop = AstraProjectorSimple(ig, ag, 'cpu')
+sin = Aop.direct(data)
+
+# Create noisy data
+np.random.seed(10)
+n1 = np.random.random(sin.shape)
+noisy_data = sin + ImageData(5*n1)
+
+#%%
+
+# Regularisation Parameter
+alpha = 50
+
+# Create operators
+op1 = Gradient(ig)
+op2 = Aop
+
+# Create BlockOperator
+operator = BlockOperator(op1, op2, shape=(2,1) )
+
+
+
+# Create functions
+
+f1 = alpha * MixedL21Norm()
+f2 = L2NormSquared(b=noisy_data)
+f = BlockFunction(f1, f2)
+
+g = ZeroFunction()
+
+diag_precon = True
+
+if diag_precon:
+
+ def tau_sigma_precond(operator):
+
+ tau = 1/operator.sum_abs_row()
+ sigma = 1/ operator.sum_abs_col()
+
+ return tau, sigma
+
+ tau, sigma = tau_sigma_precond(operator)
+
+else:
+ # Compute operator Norm
+ normK = operator.norm()
+ # Primal & dual stepsizes
+ sigma = 10
+ tau = 1/(sigma*normK**2)
+
+
+# Setup and run the PDHG algorithm
+pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
+pdhg.max_iteration = 1000
+pdhg.update_objective_interval = 200
+pdhg.run(1000)
+
+#%% Check with CVX solution
+
+from ccpi.optimisation.operators import SparseFiniteDiff
+import astra
+import numpy
+
+try:
+ from cvxpy import *
+ cvx_not_installable = True
+except ImportError:
+ cvx_not_installable = False
+
+
+if cvx_not_installable:
+
+ ##Construct problem
+ u = Variable(N*N)
+
+ DY = SparseFiniteDiff(ig, direction=0, bnd_cond='Neumann')
+ DX = SparseFiniteDiff(ig, direction=1, bnd_cond='Neumann')
+
+ regulariser = alpha * sum(norm(vstack([DX.matrix() * vec(u), DY.matrix() * vec(u)]), 2, axis = 0))
+
+ # create matrix representation for Astra operator
+
+ vol_geom = astra.create_vol_geom(N, N)
+ proj_geom = astra.create_proj_geom('parallel', 1.0, detectors, angles)
+
+ proj_id = astra.create_projector('line', proj_geom, vol_geom)
+
+ matrix_id = astra.projector.matrix(proj_id)
+
+ ProjMat = astra.matrix.get(matrix_id)
+
+ fidelity = sum_squares( ProjMat * u - noisy_data.as_array().ravel())
+ #constraints = [q>=fidelity]
+# constraints = [u>=0]
+
+ solver = MOSEK
+ obj = Minimize( regulariser + fidelity)
+ prob = Problem(obj)
+ result = prob.solve(verbose = True, solver = solver)
+
+
+#%%
+
+plt.figure(figsize=(15,15))
+plt.subplot(2,2,1)
+plt.imshow(data.as_array())
+plt.title('Ground Truth')
+
+plt.subplot(2,2,2)
+plt.imshow(noisy_data.as_array())
+plt.title('Noisy Data')
+
+plt.subplot(2,2,3)
+plt.imshow(pdhg.get_output().as_array())
+plt.title('PDHG Reconstruction')
+
+plt.subplot(2,2,4)
+plt.imshow(np.reshape(u.value, ig.shape))
+plt.title('CVX Reconstruction')
+
+plt.show()
+
+#%%
+plt.plot(np.linspace(0,N,N), pdhg.get_output().as_array()[int(N/2),:], label = 'PDHG')
+plt.plot(np.linspace(0,N,N), np.reshape(u.value, ig.shape)[int(N/2),:], label = 'CVX')
+plt.legend()
+plt.title('Middle Line Profiles')
+plt.show()
+
+
+
+
+
+
+
+
diff --git a/Wrappers/Python/wip/compare_CGLS_algos.py b/Wrappers/Python/wip/compare_CGLS_algos.py
index 119752c..52f3f31 100644
--- a/Wrappers/Python/wip/compare_CGLS_algos.py
+++ b/Wrappers/Python/wip/compare_CGLS_algos.py
@@ -12,6 +12,8 @@ from ccpi.optimisation.algorithms import CGLS as CGLSalg
import numpy as np
import matplotlib.pyplot as plt
+from ccpi.optimisation.functions import Norm2Sq
+
# Choose either a parallel-beam (1=parallel2D) or fan-beam (2=cone2D) test case
test_case = 1
@@ -101,25 +103,29 @@ x_CGLS, it_CGLS, timing_CGLS, criter_CGLS = CGLS(x_init, Aop, b, opt)
#plt.title('CGLS criterion')
#plt.show()
+f = Norm2Sq(Aop, b, c=1.)
+def callback(it, objective, solution):
+ print (objective, f(solution))
# Now CLGS using the algorithm class
CGLS_alg = CGLSalg()
CGLS_alg.set_up(x_init, Aop, b )
-CGLS_alg.max_iteration = 2000
-CGLS_alg.run(opt['iter'])
+CGLS_alg.max_iteration = 500
+CGLS_alg.update_objective_interval = 10
+CGLS_alg.run(300, callback=callback)
x_CGLS_alg = CGLS_alg.get_output()
-#plt.figure()
-#plt.imshow(x_CGLS_alg.as_array())
-#plt.title('CGLS ALG')
-#plt.colorbar()
-#plt.show()
+plt.figure()
+plt.imshow(x_CGLS_alg.as_array())
+plt.title('CGLS ALG')
+plt.colorbar()
+plt.show()
-#plt.figure()
-#plt.semilogy(CGLS_alg.objective)
-#plt.title('CGLS criterion')
-#plt.show()
+plt.figure()
+plt.semilogy(CGLS_alg.objective)
+plt.title('CGLS criterion')
+plt.show()
print(criter_CGLS)
print(CGLS_alg.objective)
diff --git a/Wrappers/Python/wip/demo_box_constraints_FISTA.py b/Wrappers/Python/wip/demo_box_constraints_FISTA.py
index 2f9e0c6..b15dd45 100644
--- a/Wrappers/Python/wip/demo_box_constraints_FISTA.py
+++ b/Wrappers/Python/wip/demo_box_constraints_FISTA.py
@@ -72,7 +72,7 @@ else:
# Set up Operator object combining the ImageGeometry and AcquisitionGeometry
# wrapping calls to ASTRA as well as specifying whether to use CPU or GPU.
-Aop = AstraProjectorSimple(ig, ag, 'gpu')
+Aop = AstraProjectorSimple(ig, ag, 'cpu')
Aop = Identity(ig,ig)
diff --git a/Wrappers/Python/wip/demo_colourbay.py b/Wrappers/Python/wip/demo_colourbay.py
index 5dbf2e1..0536b07 100644
--- a/Wrappers/Python/wip/demo_colourbay.py
+++ b/Wrappers/Python/wip/demo_colourbay.py
@@ -18,7 +18,7 @@ from ccpi.optimisation.funcs import Norm2sq, Norm1
# Permute (numpy.transpose) puts into our default ordering which is
# (channel, angle, vertical, horizontal).
-pathname = '/media/jakob/050d8d45-fab3-4285-935f-260e6c5f162c1/Data/ColourBay/spectral_data_sets/CarbonPd/'
+pathname = '/media/newhd/shared/Data/ColourBay/spectral_data_sets/CarbonPd/'
filename = 'carbonPd_full_sinogram_stripes_removed.mat'
X = loadmat(pathname + filename)
diff --git a/Wrappers/Python/wip/fista_TV_denoising.py b/Wrappers/Python/wip/fista_TV_denoising.py
deleted file mode 100644
index a9712fc..0000000
--- a/Wrappers/Python/wip/fista_TV_denoising.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old, FISTA
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
- MixedL21Norm, FunctionOperatorComposition, BlockFunction, ScaledFunction
-
-from ccpi.optimisation.algs import FISTA
-
-from skimage.util import random_noise
-
-from timeit import default_timer as timer
-def dt(steps):
- return steps[-1] - steps[-2]
-
-# Create phantom for TV denoising
-
-N = 100
-
-data = np.zeros((N,N))
-data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-ag = ig
-
-# Create noisy data. Add Gaussian noise
-n1 = random_noise(data, mode = 'gaussian', mean=0, var = 0.05, seed=10)
-noisy_data = ImageData(n1)
-
-
-plt.imshow(noisy_data.as_array())
-plt.title('Noisy data')
-plt.show()
-
-# Regularisation Parameter
-alpha = 2
-
-operator = Gradient(ig)
-g = alpha * MixedL21Norm()
-f = 0.5 * L2NormSquared(b = noisy_data)
-
-x_init = ig.allocate()
-opt = {'niter':2000}
-
-
-x = FISTA(x_init, f, g, opt)
-
-#fista = FISTA()
-#fista.set_up(x_init, f, g, opt )
-#fista.max_iteration = 10
-#
-#fista.run(2000)
-#plt.figure(figsize=(15,15))
-#plt.subplot(3,1,1)
-#plt.imshow(fista.get_output().as_array())
-#plt.title('no memopt class')
-
-
-
diff --git a/Wrappers/Python/wip/fix_test.py b/Wrappers/Python/wip/fix_test.py
new file mode 100755
index 0000000..b1006c0
--- /dev/null
+++ b/Wrappers/Python/wip/fix_test.py
@@ -0,0 +1,208 @@
+import numpy as np
+import numpy
+from ccpi.optimisation.operators import *
+from ccpi.optimisation.algorithms import *
+from ccpi.optimisation.functions import *
+from ccpi.framework import *
+
+def isSizeCorrect(data1 ,data2):
+ if issubclass(type(data1), DataContainer) and \
+ issubclass(type(data2), DataContainer):
+ # check dimensionality
+ if data1.check_dimensions(data2):
+ return True
+ elif issubclass(type(data1) , numpy.ndarray) and \
+ issubclass(type(data2) , numpy.ndarray):
+ return data1.shape == data2.shape
+ else:
+ raise ValueError("{0}: getting two incompatible types: {1} {2}"\
+ .format('Function', type(data1), type(data2)))
+ return False
+
+class Norm1(Function):
+
+ def __init__(self,gamma):
+ super(Norm1, self).__init__()
+ self.gamma = gamma
+ self.L = 1
+ self.sign_x = None
+
+ def __call__(self,x,out=None):
+ if out is None:
+ return self.gamma*(x.abs().sum())
+ else:
+ if not x.shape == out.shape:
+ raise ValueError('Norm1 Incompatible size:',
+ x.shape, out.shape)
+ x.abs(out=out)
+ return out.sum() * self.gamma
+
+ def prox(self,x,tau):
+ return (x.abs() - tau*self.gamma).maximum(0) * x.sign()
+
+ def proximal(self, x, tau, out=None):
+ if out is None:
+ return self.prox(x, tau)
+ else:
+ if isSizeCorrect(x,out):
+ # check dimensionality
+ if issubclass(type(out), DataContainer):
+ v = (x.abs() - tau*self.gamma).maximum(0)
+ x.sign(out=out)
+ out *= v
+ #out.fill(self.prox(x,tau))
+ elif issubclass(type(out) , numpy.ndarray):
+ v = (x.abs() - tau*self.gamma).maximum(0)
+ out[:] = x.sign()
+ out *= v
+ #out[:] = self.prox(x,tau)
+ else:
+ raise ValueError ('Wrong size: x{0} out{1}'.format(x.shape,out.shape) )
+
+opt = {'memopt': True}
+# Problem data.
+m = 500
+n = 200
+
+# if m < n then the problem is under-determined and algorithms will struggle to find a solution.
+# One approach is to add regularisation
+
+#np.random.seed(1)
+Amat = np.asarray( np.random.randn(m, n), dtype=numpy.float32)
+Amat = np.asarray( np.random.random_integers(1,10, (m, n)), dtype=numpy.float32)
+#Amat = np.asarray(np.eye(m), dtype=np.float32) * 2
+A = LinearOperatorMatrix(Amat)
+bmat = np.asarray( np.random.randn(m), dtype=numpy.float32)
+#bmat *= 0
+#bmat += 2
+print ("bmat", bmat.shape)
+print ("A", A.A)
+#bmat.shape = (bmat.shape[0], 1)
+
+# A = Identity()
+# Change n to equal to m.
+vgb = VectorGeometry(m)
+vgx = VectorGeometry(n)
+b = vgb.allocate(VectorGeometry.RANDOM_INT, dtype=numpy.float32)
+# b.fill(bmat)
+#b = DataContainer(bmat)
+
+# Regularization parameter
+lam = 10
+opt = {'memopt': True}
+# Create object instances with the test data A and b.
+f = Norm2Sq(A, b, c=1., memopt=True)
+#f = FunctionOperatorComposition(A, L2NormSquared(b=bmat))
+g0 = ZeroFunction()
+
+#f.L = 30.003
+x_init = vgx.allocate(VectorGeometry.RANDOM, dtype=numpy.float32)
+x_initcgls = x_init.copy()
+
+a = VectorData(x_init.as_array(), deep_copy=True)
+
+assert id(x_init.as_array()) != id(a.as_array())
+
+
+#f.L = LinearOperator.PowerMethod(A, 25, x_init)[0]
+#print ('f.L', f.L)
+rate = (1 / f.L) / 6
+#f.L *= 12
+
+# Initial guess
+#x_init = DataContainer(np.zeros((n, 1)))
+print ('x_init', x_init.as_array())
+print ('b', b.as_array())
+# Create 1-norm object instance
+g1_new = lam * L1Norm()
+g1 = Norm1(lam)
+
+g1 = ZeroFunction()
+#g1(x_init)
+x = g1.prox(x_init, 1/f.L )
+print ("g1.proximal ", x.as_array())
+
+x = g1.prox(x_init, 0.03 )
+print ("g1.proximal ", x.as_array())
+x = g1_new.proximal(x_init, 0.03 )
+print ("g1.proximal ", x.as_array())
+
+x1 = vgx.allocate(VectorGeometry.RANDOM, dtype=numpy.float32)
+pippo = vgx.allocate()
+
+print ("x_init", x_init.as_array())
+print ("x1", x1.as_array())
+a = x_init.subtract(x1, out=pippo)
+
+print ("pippo", pippo.as_array())
+print ("x_init", x_init.as_array())
+print ("x1", x1.as_array())
+
+y = A.direct(x_init)
+y *= 0
+A.direct(x_init, out=y)
+
+# Combine with least squares and solve using generic FISTA implementation
+#x_fista1, it1, timing1, criter1 = FISTA(x_init, f, g1, opt=opt)
+def callback(it, objective, solution):
+ print ("callback " , it , objective, f(solution))
+
+fa = FISTA(x_init=x_init, f=f, g=g1)
+fa.max_iteration = 1000
+fa.update_objective_interval = int( fa.max_iteration / 10 )
+fa.run(fa.max_iteration, callback = None, verbose=True)
+
+gd = GradientDescent(x_init=x_init, objective_function=f, rate = rate )
+gd.max_iteration = 5000
+gd.update_objective_interval = int( gd.max_iteration / 10 )
+gd.run(gd.max_iteration, callback = None, verbose=True)
+
+
+
+cgls = CGLS(x_init= x_initcgls, operator=A, data=b)
+cgls.max_iteration = 1000
+cgls.update_objective_interval = int( cgls.max_iteration / 10 )
+
+#cgls.should_stop = stop_criterion(cgls)
+cgls.run(cgls.max_iteration, callback = callback, verbose=True)
+
+
+
+# Print for comparison
+print("FISTA least squares plus 1-norm solution and objective value:")
+print(fa.get_output().as_array())
+print(fa.get_last_objective())
+
+print ("data ", b.as_array())
+print ('FISTA ', A.direct(fa.get_output()).as_array())
+print ('GradientDescent', A.direct(gd.get_output()).as_array())
+print ('CGLS ', A.direct(cgls.get_output()).as_array())
+
+cond = numpy.linalg.cond(A.A)
+
+print ("cond" , cond)
+
+#%%
+try:
+ import cvxpy as cp
+ # Construct the problem.
+ x = cp.Variable(n)
+ objective = cp.Minimize(cp.sum_squares(A.A*x - bmat))
+ prob = cp.Problem(objective)
+ # The optimal objective is returned by prob.solve().
+ result = prob.solve(solver = cp.MOSEK)
+
+ print ('CGLS ', cgls.get_output().as_array())
+ print ('CVX ', x.value)
+
+ print ('FISTA ', fa.get_output().as_array())
+ print ('GD ', gd.get_output().as_array())
+except ImportError as ir:
+ pass
+
+ #%%
+
+
+
+
+
diff --git a/Wrappers/Python/wip/old_demos/demo_colourbay.py b/Wrappers/Python/wip/old_demos/demo_colourbay.py
new file mode 100644
index 0000000..5dbf2e1
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/demo_colourbay.py
@@ -0,0 +1,137 @@
+# This script demonstrates how to load a mat-file with UoM colour-bay data
+# into the CIL optimisation framework and run (simple) multichannel
+# reconstruction methods.
+
+# All third-party imports.
+import numpy
+from scipy.io import loadmat
+import matplotlib.pyplot as plt
+
+# All own imports.
+from ccpi.framework import AcquisitionData, AcquisitionGeometry, ImageGeometry, ImageData
+from ccpi.astra.ops import AstraProjectorMC
+from ccpi.optimisation.algs import CGLS, FISTA
+from ccpi.optimisation.funcs import Norm2sq, Norm1
+
+# Load full data and permute to expected ordering. Change path as necessary.
+# The loaded X has dims 80x60x80x150, which is pix x angle x pix x channel.
+# Permute (numpy.transpose) puts into our default ordering which is
+# (channel, angle, vertical, horizontal).
+
+pathname = '/media/jakob/050d8d45-fab3-4285-935f-260e6c5f162c1/Data/ColourBay/spectral_data_sets/CarbonPd/'
+filename = 'carbonPd_full_sinogram_stripes_removed.mat'
+
+X = loadmat(pathname + filename)
+X = numpy.transpose(X['SS'],(3,1,2,0))
+
+# Store geometric variables for reuse
+num_channels = X.shape[0]
+num_pixels_h = X.shape[3]
+num_pixels_v = X.shape[2]
+num_angles = X.shape[1]
+
+# Display a single projection in a single channel
+plt.imshow(X[100,5,:,:])
+plt.title('Example of a projection image in one channel' )
+plt.show()
+
+# Set angles to use
+angles = numpy.linspace(-numpy.pi,numpy.pi,num_angles,endpoint=False)
+
+# Define full 3D acquisition geometry and data container.
+# Geometric info is taken from the txt-file in the same dir as the mat-file
+ag = AcquisitionGeometry('cone',
+ '3D',
+ angles,
+ pixel_num_h=num_pixels_h,
+ pixel_size_h=0.25,
+ pixel_num_v=num_pixels_v,
+ pixel_size_v=0.25,
+ dist_source_center=233.0,
+ dist_center_detector=245.0,
+ channels=num_channels)
+data = AcquisitionData(X, geometry=ag)
+
+# Reduce to central slice by extracting relevant parameters from data and its
+# geometry. Perhaps create function to extract central slice automatically?
+data2d = data.subset(vertical=40)
+ag2d = AcquisitionGeometry('cone',
+ '2D',
+ ag.angles,
+ pixel_num_h=ag.pixel_num_h,
+ pixel_size_h=ag.pixel_size_h,
+ pixel_num_v=1,
+ pixel_size_v=ag.pixel_size_h,
+ dist_source_center=ag.dist_source_center,
+ dist_center_detector=ag.dist_center_detector,
+ channels=ag.channels)
+data2d.geometry = ag2d
+
+# Set up 2D Image Geometry.
+# First need the geometric magnification to scale the voxel size relative
+# to the detector pixel size.
+mag = (ag.dist_source_center + ag.dist_center_detector)/ag.dist_source_center
+ig2d = ImageGeometry(voxel_num_x=ag2d.pixel_num_h,
+ voxel_num_y=ag2d.pixel_num_h,
+ voxel_size_x=ag2d.pixel_size_h/mag,
+ voxel_size_y=ag2d.pixel_size_h/mag,
+ channels=X.shape[0])
+
+# Create GPU multichannel projector/backprojector operator with ASTRA.
+Aall = AstraProjectorMC(ig2d,ag2d,'gpu')
+
+# Compute and simple backprojction and display one channel as image.
+Xbp = Aall.adjoint(data2d)
+plt.imshow(Xbp.subset(channel=100).array)
+plt.show()
+
+# Set initial guess ImageData with zeros for algorithms, and algorithm options.
+x_init = ImageData(numpy.zeros((num_channels,num_pixels_v,num_pixels_h)),
+ geometry=ig2d,
+ dimension_labels=['channel','horizontal_y','horizontal_x'])
+opt_CGLS = {'tol': 1e-4, 'iter': 5}
+
+# Run CGLS algorithm and display one channel.
+x_CGLS, it_CGLS, timing_CGLS, criter_CGLS = CGLS(x_init, Aall, data2d, opt_CGLS)
+
+plt.imshow(x_CGLS.subset(channel=100).array)
+plt.title('CGLS')
+plt.show()
+
+plt.semilogy(criter_CGLS)
+plt.title('CGLS Criterion vs iterations')
+plt.show()
+
+# Create least squares object instance with projector, test data and a constant
+# coefficient of 0.5. Note it is least squares over all channels.
+f = Norm2sq(Aall,data2d,c=0.5)
+
+# Options for FISTA algorithm.
+opt = {'tol': 1e-4, 'iter': 100}
+
+# Run FISTA for least squares without regularization and display one channel
+# reconstruction as image.
+x_fista0, it0, timing0, criter0 = FISTA(x_init, f, None, opt)
+
+plt.imshow(x_fista0.subset(channel=100).array)
+plt.title('FISTA LS')
+plt.show()
+
+plt.semilogy(criter0)
+plt.title('FISTA LS Criterion vs iterations')
+plt.show()
+
+# Set up 1-norm regularisation (over all channels), solve with FISTA, and
+# display one channel of reconstruction.
+lam = 0.1
+g0 = Norm1(lam)
+
+x_fista1, it1, timing1, criter1 = FISTA(x_init, f, g0, opt)
+
+plt.imshow(x_fista1.subset(channel=100).array)
+plt.title('FISTA LS+1')
+plt.show()
+
+plt.semilogy(criter1)
+plt.title('FISTA LS+1 Criterion vs iterations')
+plt.show() \ No newline at end of file
diff --git a/Wrappers/Python/wip/old_demos/demo_compare_cvx.py b/Wrappers/Python/wip/old_demos/demo_compare_cvx.py
new file mode 100644
index 0000000..27b1c97
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/demo_compare_cvx.py
@@ -0,0 +1,306 @@
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, DataContainer
+from ccpi.optimisation.algs import FISTA, FBPD, CGLS
+from ccpi.optimisation.funcs import Norm2sq, ZeroFun, Norm1, TV2D, Norm2
+
+from ccpi.optimisation.ops import LinearOperatorMatrix, TomoIdentity
+from ccpi.optimisation.ops import Identity
+from ccpi.optimisation.ops import FiniteDiff2D
+
+# Requires CVXPY, see http://www.cvxpy.org/
+# CVXPY can be installed in anaconda using
+# conda install -c cvxgrp cvxpy libgcc
+
+# Whether to use or omit CVXPY
+use_cvxpy = True
+if use_cvxpy:
+ from cvxpy import *
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Problem data.
+m = 30
+n = 20
+np.random.seed(1)
+Amat = np.random.randn(m, n)
+A = LinearOperatorMatrix(Amat)
+bmat = np.random.randn(m)
+bmat.shape = (bmat.shape[0],1)
+
+# A = Identity()
+# Change n to equal to m.
+
+b = DataContainer(bmat)
+
+# Regularization parameter
+lam = 10
+opt = {'memopt':True}
+# Create object instances with the test data A and b.
+f = Norm2sq(A,b,c=0.5, memopt=True)
+g0 = ZeroFun()
+
+# Initial guess
+x_init = DataContainer(np.zeros((n,1)))
+
+f.grad(x_init)
+
+# Run FISTA for least squares plus zero function.
+x_fista0, it0, timing0, criter0 = FISTA(x_init, f, g0 , opt=opt)
+
+# Print solution and final objective/criterion value for comparison
+print("FISTA least squares plus zero function solution and objective value:")
+print(x_fista0.array)
+print(criter0[-1])
+
+if use_cvxpy:
+ # Compare to CVXPY
+
+ # Construct the problem.
+ x0 = Variable(n)
+ objective0 = Minimize(0.5*sum_squares(Amat*x0 - bmat.T[0]) )
+ prob0 = Problem(objective0)
+
+ # The optimal objective is returned by prob.solve().
+ result0 = prob0.solve(verbose=False,solver=SCS,eps=1e-9)
+
+ # The optimal solution for x is stored in x.value and optimal objective value
+ # is in result as well as in objective.value
+ print("CVXPY least squares plus zero function solution and objective value:")
+ print(x0.value)
+ print(objective0.value)
+
+# Plot criterion curve to see FISTA converge to same value as CVX.
+iternum = np.arange(1,1001)
+plt.figure()
+plt.loglog(iternum[[0,-1]],[objective0.value, objective0.value], label='CVX LS')
+plt.loglog(iternum,criter0,label='FISTA LS')
+plt.legend()
+plt.show()
+
+# Create 1-norm object instance
+g1 = Norm1(lam)
+
+g1(x_init)
+x_rand = DataContainer(np.reshape(np.random.rand(n),(n,1)))
+x_rand2 = DataContainer(np.reshape(np.random.rand(n-1),(n-1,1)))
+v = g1.prox(x_rand,0.02)
+#vv = g1.prox(x_rand2,0.02)
+vv = v.copy()
+vv *= 0
+print (">>>>>>>>>>vv" , vv.as_array())
+vv.fill(v)
+print (">>>>>>>>>>fill" , vv.as_array())
+g1.proximal(x_rand, 0.02, out=vv)
+print (">>>>>>>>>>v" , v.as_array())
+print (">>>>>>>>>>gradient" , vv.as_array())
+
+print (">>>>>>>>>>" , (v-vv).as_array())
+import sys
+#sys.exit(0)
+# Combine with least squares and solve using generic FISTA implementation
+x_fista1, it1, timing1, criter1 = FISTA(x_init, f, g1,opt=opt)
+
+# Print for comparison
+print("FISTA least squares plus 1-norm solution and objective value:")
+print(x_fista1)
+print(criter1[-1])
+
+if use_cvxpy:
+ # Compare to CVXPY
+
+ # Construct the problem.
+ x1 = Variable(n)
+ objective1 = Minimize(0.5*sum_squares(Amat*x1 - bmat.T[0]) + lam*norm(x1,1) )
+ prob1 = Problem(objective1)
+
+ # The optimal objective is returned by prob.solve().
+ result1 = prob1.solve(verbose=False,solver=SCS,eps=1e-9)
+
+ # The optimal solution for x is stored in x.value and optimal objective value
+ # is in result as well as in objective.value
+ print("CVXPY least squares plus 1-norm solution and objective value:")
+ print(x1.value)
+ print(objective1.value)
+
+# Now try another algorithm FBPD for same problem:
+x_fbpd1, itfbpd1, timingfbpd1, criterfbpd1 = FBPD(x_init,Identity(), None, f, g1)
+print(x_fbpd1)
+print(criterfbpd1[-1])
+
+# Plot criterion curve to see both FISTA and FBPD converge to same value.
+# Note that FISTA is very efficient for 1-norm minimization so it beats
+# FBPD in this test by a lot. But FBPD can handle a larger class of problems
+# than FISTA can.
+plt.figure()
+plt.loglog(iternum[[0,-1]],[objective1.value, objective1.value], label='CVX LS+1')
+plt.loglog(iternum,criter1,label='FISTA LS+1')
+plt.legend()
+plt.show()
+
+plt.figure()
+plt.loglog(iternum[[0,-1]],[objective1.value, objective1.value], label='CVX LS+1')
+plt.loglog(iternum,criter1,label='FISTA LS+1')
+plt.loglog(iternum,criterfbpd1,label='FBPD LS+1')
+plt.legend()
+plt.show()
+
+# Now try 1-norm and TV denoising with FBPD, first 1-norm.
+
+# Set up phantom size NxN by creating ImageGeometry, initialising the
+# ImageData object with this geometry and empty array and finally put some
+# data into its array, and display as image.
+N = 64
+ig = ImageGeometry(voxel_num_x=N,voxel_num_y=N)
+Phantom = ImageData(geometry=ig)
+
+x = Phantom.as_array()
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+plt.imshow(x)
+plt.title('Phantom image')
+plt.show()
+
+# Identity operator for denoising
+I = TomoIdentity(ig)
+
+# Data and add noise
+y = I.direct(Phantom)
+y.array = y.array + 0.1*np.random.randn(N, N)
+
+plt.imshow(y.array)
+plt.title('Noisy image')
+plt.show()
+
+
+###################
+# Data fidelity term
+f_denoise = Norm2sq(I,y,c=0.5,memopt=True)
+
+# 1-norm regulariser
+lam1_denoise = 1.0
+g1_denoise = Norm1(lam1_denoise)
+
+# Initial guess
+x_init_denoise = ImageData(np.zeros((N,N)))
+
+# Combine with least squares and solve using generic FISTA implementation
+x_fista1_denoise, it1_denoise, timing1_denoise, criter1_denoise = FISTA(x_init_denoise, f_denoise, g1_denoise, opt=opt)
+
+print(x_fista1_denoise)
+print(criter1_denoise[-1])
+
+#plt.imshow(x_fista1_denoise.as_array())
+#plt.title('FISTA LS+1')
+#plt.show()
+
+# Now denoise LS + 1-norm with FBPD
+x_fbpd1_denoise, itfbpd1_denoise, timingfbpd1_denoise, \
+ criterfbpd1_denoise = FBPD(x_init_denoise, I, None, f_denoise, g1_denoise)
+print(x_fbpd1_denoise)
+print(criterfbpd1_denoise[-1])
+
+#plt.imshow(x_fbpd1_denoise.as_array())
+#plt.title('FBPD LS+1')
+#plt.show()
+
+if use_cvxpy:
+ # Compare to CVXPY
+
+ # Construct the problem.
+ x1_denoise = Variable(N**2,1)
+ objective1_denoise = Minimize(0.5*sum_squares(x1_denoise - y.array.flatten()) + lam1_denoise*norm(x1_denoise,1) )
+ prob1_denoise = Problem(objective1_denoise)
+
+ # The optimal objective is returned by prob.solve().
+ result1_denoise = prob1_denoise.solve(verbose=False,solver=SCS,eps=1e-12)
+
+ # The optimal solution for x is stored in x.value and optimal objective value
+ # is in result as well as in objective.value
+ print("CVXPY least squares plus 1-norm solution and objective value:")
+ print(x1_denoise.value)
+ print(objective1_denoise.value)
+
+x1_cvx = x1_denoise.value
+x1_cvx.shape = (N,N)
+
+
+
+#plt.imshow(x1_cvx)
+#plt.title('CVX LS+1')
+#plt.show()
+
+fig = plt.figure()
+plt.subplot(1,4,1)
+plt.imshow(y.array)
+plt.title("LS+1")
+plt.subplot(1,4,2)
+plt.imshow(x_fista1_denoise.as_array())
+plt.title("fista")
+plt.subplot(1,4,3)
+plt.imshow(x_fbpd1_denoise.as_array())
+plt.title("fbpd")
+plt.subplot(1,4,4)
+plt.imshow(x1_cvx)
+plt.title("cvx")
+plt.show()
+
+##############################################################
+# Now TV with FBPD and Norm2
+lam_tv = 0.1
+gtv = TV2D(lam_tv)
+norm2 = Norm2(lam_tv)
+op = FiniteDiff2D()
+#gtv(gtv.op.direct(x_init_denoise))
+
+opt_tv = {'tol': 1e-4, 'iter': 10000}
+
+x_fbpdtv_denoise, itfbpdtv_denoise, timingfbpdtv_denoise, \
+ criterfbpdtv_denoise = FBPD(x_init_denoise, op, None, \
+ f_denoise, norm2 ,opt=opt_tv)
+print(x_fbpdtv_denoise)
+print(criterfbpdtv_denoise[-1])
+
+plt.imshow(x_fbpdtv_denoise.as_array())
+plt.title('FBPD TV')
+#plt.show()
+
+if use_cvxpy:
+ # Compare to CVXPY
+
+ # Construct the problem.
+ xtv_denoise = Variable((N,N))
+ #print (xtv_denoise.value.shape)
+ objectivetv_denoise = Minimize(0.5*sum_squares(xtv_denoise - y.array) + lam_tv*tv(xtv_denoise) )
+ probtv_denoise = Problem(objectivetv_denoise)
+
+ # The optimal objective is returned by prob.solve().
+ resulttv_denoise = probtv_denoise.solve(verbose=False,solver=SCS,eps=1e-12)
+
+ # The optimal solution for x is stored in x.value and optimal objective value
+ # is in result as well as in objective.value
+ print("CVXPY least squares plus 1-norm solution and objective value:")
+ print(xtv_denoise.value)
+ print(objectivetv_denoise.value)
+
+plt.imshow(xtv_denoise.value)
+plt.title('CVX TV')
+#plt.show()
+
+fig = plt.figure()
+plt.subplot(1,3,1)
+plt.imshow(y.array)
+plt.title("TV2D")
+plt.subplot(1,3,2)
+plt.imshow(x_fbpdtv_denoise.as_array())
+plt.title("fbpd tv denoise")
+plt.subplot(1,3,3)
+plt.imshow(xtv_denoise.value)
+plt.title("CVX tv")
+plt.show()
+
+
+
+plt.loglog([0,opt_tv['iter']], [objectivetv_denoise.value,objectivetv_denoise.value], label='CVX TV')
+plt.loglog(criterfbpdtv_denoise, label='FBPD TV')
diff --git a/Wrappers/Python/wip/old_demos/demo_gradient_descent.py b/Wrappers/Python/wip/old_demos/demo_gradient_descent.py
new file mode 100755
index 0000000..4d6647e
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/demo_gradient_descent.py
@@ -0,0 +1,295 @@
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, DataContainer
+from ccpi.optimisation.algs import FISTA, FBPD, CGLS
+from ccpi.optimisation.funcs import Norm2sq, ZeroFun, Norm1, TV2D, Norm2
+
+from ccpi.optimisation.ops import LinearOperatorMatrix, TomoIdentity
+from ccpi.optimisation.ops import Identity
+from ccpi.optimisation.ops import FiniteDiff2D
+
+# Requires CVXPY, see http://www.cvxpy.org/
+# CVXPY can be installed in anaconda using
+# conda install -c cvxgrp cvxpy libgcc
+
+# Whether to use or omit CVXPY
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+class Algorithm(object):
+ def __init__(self, *args, **kwargs):
+ pass
+ def set_up(self, *args, **kwargs):
+ raise NotImplementedError()
+ def update(self):
+ raise NotImplementedError()
+
+ def should_stop(self):
+ raise NotImplementedError()
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if self.should_stop():
+ raise StopIteration()
+ else:
+ self.update()
+
+class GradientDescent(Algorithm):
+ x = None
+ rate = 0
+ objective_function = None
+ regulariser = None
+ iteration = 0
+ stop_cryterion = 'max_iter'
+ __max_iteration = 0
+ __loss = []
+ def __init__(self, **kwargs):
+ args = ['x_init', 'objective_function', 'rate']
+ present = True
+ for k,v in kwargs.items():
+ if k in args:
+ args.pop(args.index(k))
+ if len(args) == 0:
+ return self.set_up(x_init=kwargs['x_init'],
+ objective_function=kwargs['objective_function'],
+ rate=kwargs['rate'])
+
+ def should_stop(self):
+ return self.iteration >= self.max_iteration
+
+ def set_up(self, x_init, objective_function, rate):
+ self.x = x_init.copy()
+ self.x_update = x_init.copy()
+ self.objective_function = objective_function
+ self.rate = rate
+ self.__loss.append(objective_function(x_init))
+
+ def update(self):
+
+ self.objective_function.gradient(self.x, out=self.x_update)
+ self.x_update *= -self.rate
+ self.x += self.x_update
+ self.__loss.append(self.objective_function(self.x))
+ self.iteration += 1
+
+ def get_output(self):
+ return self.x
+ def get_current_loss(self):
+ return self.__loss[-1]
+ @property
+ def loss(self):
+ return self.__loss
+ @property
+ def max_iteration(self):
+ return self.__max_iteration
+ @max_iteration.setter
+ def max_iteration(self, value):
+ assert isinstance(value, int)
+ self.__max_iteration = value
+
+
+
+
+
+# Problem data.
+m = 30
+n = 20
+np.random.seed(1)
+Amat = np.random.randn(m, n)
+A = LinearOperatorMatrix(Amat)
+bmat = np.random.randn(m)
+bmat.shape = (bmat.shape[0],1)
+
+# A = Identity()
+# Change n to equal to m.
+
+b = DataContainer(bmat)
+
+# Regularization parameter
+lam = 10
+opt = {'memopt':True}
+# Create object instances with the test data A and b.
+f = Norm2sq(A,b,c=0.5, memopt=True)
+g0 = ZeroFun()
+
+# Initial guess
+x_init = DataContainer(np.zeros((n,1)))
+
+f.grad(x_init)
+
+# Run FISTA for least squares plus zero function.
+x_fista0, it0, timing0, criter0 = FISTA(x_init, f, g0 , opt=opt)
+
+# Print solution and final objective/criterion value for comparison
+print("FISTA least squares plus zero function solution and objective value:")
+print(x_fista0.array)
+print(criter0[-1])
+
+gd = GradientDescent(x_init=x_init, objective_function=f, rate=0.001)
+gd.max_iteration = 5000
+
+for i,el in enumerate(gd):
+ if i%100 == 0:
+ print ("\rIteration {} Loss: {}".format(gd.iteration,
+ gd.get_current_loss()))
+
+
+#%%
+
+
+#
+#if use_cvxpy:
+# # Compare to CVXPY
+#
+# # Construct the problem.
+# x0 = Variable(n)
+# objective0 = Minimize(0.5*sum_squares(Amat*x0 - bmat.T[0]) )
+# prob0 = Problem(objective0)
+#
+# # The optimal objective is returned by prob.solve().
+# result0 = prob0.solve(verbose=False,solver=SCS,eps=1e-9)
+#
+# # The optimal solution for x is stored in x.value and optimal objective value
+# # is in result as well as in objective.value
+# print("CVXPY least squares plus zero function solution and objective value:")
+# print(x0.value)
+# print(objective0.value)
+#
+## Plot criterion curve to see FISTA converge to same value as CVX.
+#iternum = np.arange(1,1001)
+#plt.figure()
+#plt.loglog(iternum[[0,-1]],[objective0.value, objective0.value], label='CVX LS')
+#plt.loglog(iternum,criter0,label='FISTA LS')
+#plt.legend()
+#plt.show()
+#
+## Create 1-norm object instance
+#g1 = Norm1(lam)
+#
+#g1(x_init)
+#x_rand = DataContainer(np.reshape(np.random.rand(n),(n,1)))
+#x_rand2 = DataContainer(np.reshape(np.random.rand(n-1),(n-1,1)))
+#v = g1.prox(x_rand,0.02)
+##vv = g1.prox(x_rand2,0.02)
+#vv = v.copy()
+#vv *= 0
+#print (">>>>>>>>>>vv" , vv.as_array())
+#vv.fill(v)
+#print (">>>>>>>>>>fill" , vv.as_array())
+#g1.proximal(x_rand, 0.02, out=vv)
+#print (">>>>>>>>>>v" , v.as_array())
+#print (">>>>>>>>>>gradient" , vv.as_array())
+#
+#print (">>>>>>>>>>" , (v-vv).as_array())
+#import sys
+##sys.exit(0)
+## Combine with least squares and solve using generic FISTA implementation
+#x_fista1, it1, timing1, criter1 = FISTA(x_init, f, g1,opt=opt)
+#
+## Print for comparison
+#print("FISTA least squares plus 1-norm solution and objective value:")
+#print(x_fista1)
+#print(criter1[-1])
+#
+#if use_cvxpy:
+# # Compare to CVXPY
+#
+# # Construct the problem.
+# x1 = Variable(n)
+# objective1 = Minimize(0.5*sum_squares(Amat*x1 - bmat.T[0]) + lam*norm(x1,1) )
+# prob1 = Problem(objective1)
+#
+# # The optimal objective is returned by prob.solve().
+# result1 = prob1.solve(verbose=False,solver=SCS,eps=1e-9)
+#
+# # The optimal solution for x is stored in x.value and optimal objective value
+# # is in result as well as in objective.value
+# print("CVXPY least squares plus 1-norm solution and objective value:")
+# print(x1.value)
+# print(objective1.value)
+#
+## Now try another algorithm FBPD for same problem:
+#x_fbpd1, itfbpd1, timingfbpd1, criterfbpd1 = FBPD(x_init,Identity(), None, f, g1)
+#print(x_fbpd1)
+#print(criterfbpd1[-1])
+#
+## Plot criterion curve to see both FISTA and FBPD converge to same value.
+## Note that FISTA is very efficient for 1-norm minimization so it beats
+## FBPD in this test by a lot. But FBPD can handle a larger class of problems
+## than FISTA can.
+#plt.figure()
+#plt.loglog(iternum[[0,-1]],[objective1.value, objective1.value], label='CVX LS+1')
+#plt.loglog(iternum,criter1,label='FISTA LS+1')
+#plt.legend()
+#plt.show()
+#
+#plt.figure()
+#plt.loglog(iternum[[0,-1]],[objective1.value, objective1.value], label='CVX LS+1')
+#plt.loglog(iternum,criter1,label='FISTA LS+1')
+#plt.loglog(iternum,criterfbpd1,label='FBPD LS+1')
+#plt.legend()
+#plt.show()
+
+# Now try 1-norm and TV denoising with FBPD, first 1-norm.
+
+# Set up phantom size NxN by creating ImageGeometry, initialising the
+# ImageData object with this geometry and empty array and finally put some
+# data into its array, and display as image.
+N = 64
+ig = ImageGeometry(voxel_num_x=N,voxel_num_y=N)
+Phantom = ImageData(geometry=ig)
+
+x = Phantom.as_array()
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+plt.imshow(x)
+plt.title('Phantom image')
+plt.show()
+
+# Identity operator for denoising
+I = TomoIdentity(ig)
+
+# Data and add noise
+y = I.direct(Phantom)
+y.array = y.array + 0.1*np.random.randn(N, N)
+
+plt.imshow(y.array)
+plt.title('Noisy image')
+plt.show()
+
+
+###################
+# Data fidelity term
+f_denoise = Norm2sq(I,y,c=0.5,memopt=True)
+
+# 1-norm regulariser
+lam1_denoise = 1.0
+g1_denoise = Norm1(lam1_denoise)
+
+# Initial guess
+x_init_denoise = ImageData(np.zeros((N,N)))
+
+# Combine with least squares and solve using generic FISTA implementation
+x_fista1_denoise, it1_denoise, timing1_denoise, criter1_denoise = \
+ FISTA(x_init_denoise, f_denoise, g1_denoise, opt=opt)
+
+print(x_fista1_denoise)
+print(criter1_denoise[-1])
+
+f_2 =
+gd = GradientDescent(x_init=x_init_denoise,
+ objective_function=f, rate=0.001)
+gd.max_iteration = 5000
+
+for i,el in enumerate(gd):
+ if i%100 == 0:
+ print ("\rIteration {} Loss: {}".format(gd.iteration,
+ gd.get_current_loss()))
+
+plt.imshow(gd.get_output().as_array())
+plt.title('GD image')
+plt.show()
+
diff --git a/Wrappers/Python/wip/old_demos/demo_imat_multichan_RGLTK.py b/Wrappers/Python/wip/old_demos/demo_imat_multichan_RGLTK.py
new file mode 100644
index 0000000..8370c78
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/demo_imat_multichan_RGLTK.py
@@ -0,0 +1,151 @@
+# This script demonstrates how to load IMAT fits data
+# into the CIL optimisation framework and run reconstruction methods.
+#
+# Demo to reconstruct energy-discretized channels of IMAT data
+
+# needs dxchange: conda install -c conda-forge dxchange
+# needs astropy: conda install astropy
+
+
+# All third-party imports.
+import numpy as np
+import matplotlib.pyplot as plt
+from dxchange.reader import read_fits
+from astropy.io import fits
+
+# All own imports.
+from ccpi.framework import AcquisitionData, AcquisitionGeometry, ImageGeometry, ImageData, DataContainer
+from ccpi.astra.ops import AstraProjectorSimple, AstraProjector3DSimple
+from ccpi.optimisation.algs import CGLS, FISTA
+from ccpi.optimisation.funcs import Norm2sq, Norm1
+from ccpi.plugins.regularisers import FGP_TV
+
+# set main parameters here
+n = 512
+totalAngles = 250 # total number of projection angles
+# spectral discretization parameter
+num_average = 145 # channel discretization frequency (total number of averaged channels)
+numChannels = 2970 # 2970
+totChannels = round(numChannels/num_average) # the resulting number of channels
+Projections_stack = np.zeros((num_average,n,n),dtype='uint16')
+ProjAngleChannels = np.zeros((totalAngles,totChannels,n,n),dtype='float32')
+
+#########################################################################
+print ("Loading the data...")
+MainPath = '/media/jakob/050d8d45-fab3-4285-935f-260e6c5f162c1/Data/neutrondata/' # path to data
+pathname0 = '{!s}{!s}'.format(MainPath,'PSI_phantom_IMAT/DATA/Sample/')
+counterFileName = 4675
+# A main loop over all available angles
+for ll in range(0,totalAngles,1):
+ pathnameData = '{!s}{!s}{!s}/'.format(pathname0,'angle',str(ll))
+ filenameCurr = '{!s}{!s}{!s}'.format('IMAT0000',str(counterFileName),'_Tomo_test_000_')
+ counterT = 0
+ # loop over reduced channels (discretized)
+ for i in range(0,totChannels,1):
+ sumCount = 0
+ # loop over actual channels to obtain averaged one
+ for j in range(0,num_average,1):
+ if counterT < 10:
+ outfile = '{!s}{!s}{!s}{!s}.fits'.format(pathnameData,filenameCurr,'0000',str(counterT))
+ if ((counterT >= 10) & (counterT < 100)):
+ outfile = '{!s}{!s}{!s}{!s}.fits'.format(pathnameData,filenameCurr,'000',str(counterT))
+ if ((counterT >= 100) & (counterT < 1000)):
+ outfile = '{!s}{!s}{!s}{!s}.fits'.format(pathnameData,filenameCurr,'00',str(counterT))
+ if ((counterT >= 1000) & (counterT < 10000)):
+ outfile = '{!s}{!s}{!s}{!s}.fits'.format(pathnameData,filenameCurr,'0',str(counterT))
+ try:
+ Projections_stack[j,:,:] = read_fits(outfile)
+ except:
+ print("Fits is corrupted, skipping no.", counterT)
+ sumCount -= 1
+ counterT += 1
+ sumCount += 1
+ AverageProj=np.sum(Projections_stack,axis=0)/sumCount # averaged projection over "num_average" channels
+ ProjAngleChannels[ll,i,:,:] = AverageProj
+ print("Angle is processed", ll)
+ counterFileName += 1
+#########################################################################
+
+flat1 = read_fits('{!s}{!s}{!s}'.format(MainPath,'PSI_phantom_IMAT/DATA/','OpenBeam_aft1/IMAT00004932_Tomo_test_000_SummedImg.fits'))
+nonzero = flat1 > 0
+# Apply flat field and take negative log
+for ll in range(0,totalAngles,1):
+ for i in range(0,totChannels,1):
+ ProjAngleChannels[ll,i,nonzero] = ProjAngleChannels[ll,i,nonzero]/flat1[nonzero]
+
+eqzero = ProjAngleChannels == 0
+ProjAngleChannels[eqzero] = 1
+ProjAngleChannels_NormLog = -np.log(ProjAngleChannels) # normalised and neg-log data
+
+# extact sinogram over energy channels
+selectedVertical_slice = 256
+sino_all_channels = ProjAngleChannels_NormLog[:,:,:,selectedVertical_slice]
+# Set angles to use
+angles = np.linspace(-np.pi,np.pi,totalAngles,endpoint=False)
+
+# set the geometry
+ig = ImageGeometry(n,n)
+ag = AcquisitionGeometry('parallel',
+ '2D',
+ angles,
+ n,1)
+Aop = AstraProjectorSimple(ig, ag, 'gpu')
+
+
+# loop to reconstruct energy channels
+REC_chan = np.zeros((totChannels, n, n), 'float32')
+for i in range(0,totChannels,1):
+ sino_channel = sino_all_channels[:,i,:] # extract a sinogram for i-th channel
+
+ print ("Initial guess")
+ x_init = ImageData(geometry=ig)
+
+ # Create least squares object instance with projector and data.
+ print ("Create least squares object instance with projector and data.")
+ f = Norm2sq(Aop,DataContainer(sino_channel),c=0.5)
+
+ print ("Run FISTA-TV for least squares")
+ lamtv = 5
+ opt = {'tol': 1e-4, 'iter': 200}
+ g_fgp = FGP_TV(lambdaReg = lamtv,
+ iterationsTV=50,
+ tolerance=1e-6,
+ methodTV=0,
+ nonnegativity=0,
+ printing=0,
+ device='gpu')
+
+ x_fista_fgp, it1, timing1, criter_fgp = FISTA(x_init, f, g_fgp, opt)
+ REC_chan[i,:,:] = x_fista_fgp.array
+ """
+ plt.figure()
+ plt.subplot(121)
+ plt.imshow(x_fista_fgp.array, vmin=0, vmax=0.05)
+ plt.title('FISTA FGP TV')
+ plt.subplot(122)
+ plt.semilogy(criter_fgp)
+ plt.show()
+ """
+ """
+ print ("Run CGLS for least squares")
+ opt = {'tol': 1e-4, 'iter': 20}
+ x_init = ImageData(geometry=ig)
+ x_CGLS, it_CGLS, timing_CGLS, criter_CGLS = CGLS(x_init, Aop, DataContainer(sino_channel), opt=opt)
+
+ plt.figure()
+ plt.imshow(x_CGLS.array,vmin=0, vmax=0.05)
+ plt.title('CGLS')
+ plt.show()
+ """
+# Saving images into fits using astrapy if required
+counter = 0
+filename = 'FISTA_TV_imat_slice'
+for i in range(totChannels):
+ im = REC_chan[i,:,:]
+ add_val = np.min(im[:])
+ im += abs(add_val)
+ im = im/np.max(im[:])*65535
+ outfile = '{!s}_{!s}_{!s}.fits'.format(filename,str(selectedVertical_slice),str(counter))
+ hdu = fits.PrimaryHDU(np.uint16(im))
+ hdu.writeto(outfile, overwrite=True)
+ counter += 1 \ No newline at end of file
diff --git a/Wrappers/Python/wip/old_demos/demo_imat_whitebeam.py b/Wrappers/Python/wip/old_demos/demo_imat_whitebeam.py
new file mode 100644
index 0000000..e0d213e
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/demo_imat_whitebeam.py
@@ -0,0 +1,138 @@
+# This script demonstrates how to load IMAT fits data
+# into the CIL optimisation framework and run reconstruction methods.
+#
+# This demo loads the summedImg files which are the non-spectral images
+# resulting from summing projections over all spectral channels.
+
+# needs dxchange: conda install -c conda-forge dxchange
+# needs astropy: conda install astropy
+
+
+# All third-party imports.
+import numpy
+from scipy.io import loadmat
+import matplotlib.pyplot as plt
+from dxchange.reader import read_fits
+
+# All own imports.
+from ccpi.framework import AcquisitionData, AcquisitionGeometry, ImageGeometry, ImageData
+from ccpi.astra.ops import AstraProjectorSimple, AstraProjector3DSimple
+from ccpi.optimisation.algs import CGLS, FISTA
+from ccpi.optimisation.funcs import Norm2sq, Norm1
+
+# Load and display a couple of summed projection as examples
+pathname0 = '/media/newhd/shared/Data/neutrondata/PSI_phantom_IMAT/DATA/Sample/angle0/'
+filename0 = 'IMAT00004675_Tomo_test_000_SummedImg.fits'
+
+data0 = read_fits(pathname0 + filename0)
+
+pathname10 = '/media/newhd/shared/Data/neutrondata/PSI_phantom_IMAT/DATA/Sample/angle10/'
+filename10 = 'IMAT00004685_Tomo_test_000_SummedImg.fits'
+
+data10 = read_fits(pathname10 + filename10)
+
+# Load a flat field (more are available, should we average over them?)
+flat1 = read_fits('/media/newhd/shared/Data/neutrondata/PSI_phantom_IMAT/DATA/OpenBeam_aft1/IMAT00004932_Tomo_test_000_SummedImg.fits')
+
+# Apply flat field and display after flat-field correction and negative log
+data0_rel = numpy.zeros(numpy.shape(flat1), dtype = float)
+nonzero = flat1 > 0
+data0_rel[nonzero] = data0[nonzero] / flat1[nonzero]
+data10_rel = numpy.zeros(numpy.shape(flat1), dtype = float)
+data10_rel[nonzero] = data10[nonzero] / flat1[nonzero]
+
+plt.imshow(data0_rel)
+plt.colorbar()
+plt.show()
+
+plt.imshow(-numpy.log(data0_rel))
+plt.colorbar()
+plt.show()
+
+plt.imshow(data10_rel)
+plt.colorbar()
+plt.show()
+
+plt.imshow(-numpy.log(data10_rel))
+plt.colorbar()
+plt.show()
+
+# Set up for loading all summed images at 250 angles.
+pathname = '/media/newhd/shared/Data/neutrondata/PSI_phantom_IMAT/DATA/Sample/angle{}/'
+filename = 'IMAT0000{}_Tomo_test_000_SummedImg.fits'
+
+# Dimensions
+num_angles = 250
+imsize = 512
+
+# Initialise array
+data = numpy.zeros((num_angles,imsize,imsize))
+
+# Load only 0-249, as 250 is at repetition of zero degrees just like image 0
+for i in range(0,250):
+ curimfile = (pathname + filename).format(i, i+4675)
+ data[i,:,:] = read_fits(curimfile)
+
+# Apply flat field and take negative log
+nonzero = flat1 > 0
+for i in range(0,250):
+ data[i,nonzero] = data[i,nonzero]/flat1[nonzero]
+
+eqzero = data == 0
+data[eqzero] = 1
+
+data_rel = -numpy.log(data)
+
+# Permute order to get: angles, vertical, horizontal, as default in framework.
+data_rel = numpy.transpose(data_rel,(0,2,1))
+
+# Set angles to use
+angles = numpy.linspace(-numpy.pi,numpy.pi,num_angles,endpoint=False)
+
+# Create 3D acquisition geometry and acquisition data
+ag = AcquisitionGeometry('parallel',
+ '3D',
+ angles,
+ pixel_num_h=imsize,
+ pixel_num_v=imsize)
+b = AcquisitionData(data_rel, geometry=ag)
+
+# Reduce to single (noncentral) slice by extracting relevant parameters from data and its
+# geometry. Perhaps create function to extract central slice automatically?
+b2d = b.subset(vertical=128)
+ag2d = AcquisitionGeometry('parallel',
+ '2D',
+ ag.angles,
+ pixel_num_h=ag.pixel_num_h)
+b2d.geometry = ag2d
+
+# Create 2D image geometry
+ig2d = ImageGeometry(voxel_num_x=ag2d.pixel_num_h,
+ voxel_num_y=ag2d.pixel_num_h)
+
+# Create GPU projector/backprojector operator with ASTRA.
+Aop = AstraProjectorSimple(ig2d,ag2d,'gpu')
+
+# Demonstrate operator is working by applying simple backprojection.
+z = Aop.adjoint(b2d)
+plt.imshow(z.array)
+plt.title('Simple backprojection')
+plt.colorbar()
+plt.show()
+
+# Set initial guess ImageData with zeros for algorithms, and algorithm options.
+x_init = ImageData(numpy.zeros((imsize,imsize)),
+ geometry=ig2d)
+opt_CGLS = {'tol': 1e-4, 'iter': 20}
+
+# Run CGLS algorithm and display reconstruction.
+x_CGLS, it_CGLS, timing_CGLS, criter_CGLS = CGLS(x_init, Aop, b2d, opt_CGLS)
+
+plt.imshow(x_CGLS.array)
+plt.title('CGLS')
+plt.colorbar()
+plt.show()
+
+plt.semilogy(criter_CGLS)
+plt.title('CGLS Criterion vs iterations')
+plt.show() \ No newline at end of file
diff --git a/Wrappers/Python/wip/old_demos/demo_memhandle.py b/Wrappers/Python/wip/old_demos/demo_memhandle.py
new file mode 100755
index 0000000..db48d73
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/demo_memhandle.py
@@ -0,0 +1,193 @@
+
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, DataContainer
+from ccpi.optimisation.algs import FISTA, FBPD, CGLS
+from ccpi.optimisation.funcs import Norm2sq, ZeroFun, Norm1, TV2D
+
+from ccpi.optimisation.ops import LinearOperatorMatrix, Identity
+from ccpi.optimisation.ops import TomoIdentity
+
+# Requires CVXPY, see http://www.cvxpy.org/
+# CVXPY can be installed in anaconda using
+# conda install -c cvxgrp cvxpy libgcc
+
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Problem data.
+m = 30
+n = 20
+np.random.seed(1)
+Amat = np.random.randn(m, n)
+A = LinearOperatorMatrix(Amat)
+bmat = np.random.randn(m)
+bmat.shape = (bmat.shape[0],1)
+
+
+
+# A = Identity()
+# Change n to equal to m.
+
+b = DataContainer(bmat)
+
+# Regularization parameter
+lam = 10
+
+# Create object instances with the test data A and b.
+f = Norm2sq(A,b,c=0.5, memopt=True)
+g0 = ZeroFun()
+
+# Initial guess
+x_init = DataContainer(np.zeros((n,1)))
+
+f.grad(x_init)
+opt = {'memopt': True}
+# Run FISTA for least squares plus zero function.
+x_fista0, it0, timing0, criter0 = FISTA(x_init, f, g0)
+x_fista0_m, it0_m, timing0_m, criter0_m = FISTA(x_init, f, g0, opt=opt)
+
+iternum = [i for i in range(len(criter0))]
+# Print solution and final objective/criterion value for comparison
+print("FISTA least squares plus zero function solution and objective value:")
+print(x_fista0.array)
+print(criter0[-1])
+
+
+# Plot criterion curve to see FISTA converge to same value as CVX.
+#iternum = np.arange(1,1001)
+plt.figure()
+plt.loglog(iternum,criter0,label='FISTA LS')
+plt.loglog(iternum,criter0_m,label='FISTA LS memopt')
+plt.legend()
+plt.show()
+#%%
+# Create 1-norm object instance
+g1 = Norm1(lam)
+
+g1(x_init)
+g1.prox(x_init,0.02)
+
+# Combine with least squares and solve using generic FISTA implementation
+x_fista1, it1, timing1, criter1 = FISTA(x_init, f, g1)
+x_fista1_m, it1_m, timing1_m, criter1_m = FISTA(x_init, f, g1, opt=opt)
+iternum = [i for i in range(len(criter1))]
+# Print for comparison
+print("FISTA least squares plus 1-norm solution and objective value:")
+print(x_fista1)
+print(criter1[-1])
+
+
+# Now try another algorithm FBPD for same problem:
+x_fbpd1, itfbpd1, timingfbpd1, criterfbpd1 = FBPD(x_init, None, f, g1)
+iternum = [i for i in range(len(criterfbpd1))]
+print(x_fbpd1)
+print(criterfbpd1[-1])
+
+# Plot criterion curve to see both FISTA and FBPD converge to same value.
+# Note that FISTA is very efficient for 1-norm minimization so it beats
+# FBPD in this test by a lot. But FBPD can handle a larger class of problems
+# than FISTA can.
+plt.figure()
+plt.loglog(iternum,criter1,label='FISTA LS+1')
+plt.loglog(iternum,criter1_m,label='FISTA LS+1 memopt')
+plt.legend()
+plt.show()
+
+plt.figure()
+plt.loglog(iternum,criter1,label='FISTA LS+1')
+plt.loglog(iternum,criterfbpd1,label='FBPD LS+1')
+plt.legend()
+plt.show()
+#%%
+# Now try 1-norm and TV denoising with FBPD, first 1-norm.
+
+# Set up phantom size NxN by creating ImageGeometry, initialising the
+# ImageData object with this geometry and empty array and finally put some
+# data into its array, and display as image.
+N = 1000
+ig = ImageGeometry(voxel_num_x=N,voxel_num_y=N)
+Phantom = ImageData(geometry=ig)
+
+x = Phantom.as_array()
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+plt.imshow(x)
+plt.title('Phantom image')
+plt.show()
+
+# Identity operator for denoising
+I = TomoIdentity(ig)
+
+# Data and add noise
+y = I.direct(Phantom)
+y.array += 0.1*np.random.randn(N, N)
+
+plt.figure()
+plt.imshow(y.array)
+plt.title('Noisy image')
+plt.show()
+
+# Data fidelity term
+f_denoise = Norm2sq(I,y,c=0.5, memopt=True)
+
+# 1-norm regulariser
+lam1_denoise = 1.0
+g1_denoise = Norm1(lam1_denoise)
+
+# Initial guess
+x_init_denoise = ImageData(np.zeros((N,N)))
+opt = {'memopt': False, 'iter' : 50}
+# Combine with least squares and solve using generic FISTA implementation
+print ("no memopt")
+x_fista1_denoise, it1_denoise, timing1_denoise, \
+ criter1_denoise = FISTA(x_init_denoise, f_denoise, g1_denoise, opt=opt)
+opt = {'memopt': True, 'iter' : 50}
+print ("yes memopt")
+x_fista1_denoise_m, it1_denoise_m, timing1_denoise_m, \
+ criter1_denoise_m = FISTA(x_init_denoise, f_denoise, g1_denoise, opt=opt)
+
+print(x_fista1_denoise)
+print(criter1_denoise[-1])
+
+plt.figure()
+plt.imshow(x_fista1_denoise.as_array())
+plt.title('FISTA LS+1')
+plt.show()
+
+plt.figure()
+plt.imshow(x_fista1_denoise_m.as_array())
+plt.title('FISTA LS+1 memopt')
+plt.show()
+
+plt.figure()
+plt.loglog(iternum,criter1_denoise,label='FISTA LS+1')
+plt.loglog(iternum,criter1_denoise_m,label='FISTA LS+1 memopt')
+plt.legend()
+plt.show()
+#%%
+# Now denoise LS + 1-norm with FBPD
+x_fbpd1_denoise, itfbpd1_denoise, timingfbpd1_denoise, criterfbpd1_denoise = FBPD(x_init_denoise, None, f_denoise, g1_denoise)
+print(x_fbpd1_denoise)
+print(criterfbpd1_denoise[-1])
+
+plt.figure()
+plt.imshow(x_fbpd1_denoise.as_array())
+plt.title('FBPD LS+1')
+plt.show()
+
+
+# Now TV with FBPD
+lam_tv = 0.1
+gtv = TV2D(lam_tv)
+gtv(gtv.op.direct(x_init_denoise))
+
+opt_tv = {'tol': 1e-4, 'iter': 10000}
+
+x_fbpdtv_denoise, itfbpdtv_denoise, timingfbpdtv_denoise, criterfbpdtv_denoise = FBPD(x_init_denoise, None, f_denoise, gtv,opt=opt_tv)
+print(x_fbpdtv_denoise)
+print(criterfbpdtv_denoise[-1])
+
+plt.imshow(x_fbpdtv_denoise.as_array())
+plt.title('FBPD TV')
+plt.show()
diff --git a/Wrappers/Python/wip/old_demos/demo_test_sirt.py b/Wrappers/Python/wip/old_demos/demo_test_sirt.py
new file mode 100644
index 0000000..6f5a44d
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/demo_test_sirt.py
@@ -0,0 +1,176 @@
+# This demo illustrates how to use the SIRT algorithm without and with
+# nonnegativity and box constraints. The ASTRA 2D projectors are used.
+
+# First make all imports
+from ccpi.framework import ImageData, ImageGeometry, AcquisitionGeometry, \
+ AcquisitionData
+from ccpi.optimisation.algs import FISTA, FBPD, CGLS, SIRT
+from ccpi.optimisation.funcs import Norm2sq, Norm1, TV2D, IndicatorBox
+from ccpi.astra.ops import AstraProjectorSimple
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Choose either a parallel-beam (1=parallel2D) or fan-beam (2=cone2D) test case
+test_case = 1
+
+# Set up phantom size NxN by creating ImageGeometry, initialising the
+# ImageData object with this geometry and empty array and finally put some
+# data into its array, and display as image.
+N = 128
+ig = ImageGeometry(voxel_num_x=N,voxel_num_y=N)
+Phantom = ImageData(geometry=ig)
+
+x = Phantom.as_array()
+x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
+x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
+
+plt.imshow(x)
+plt.title('Phantom image')
+plt.show()
+
+# Set up AcquisitionGeometry object to hold the parameters of the measurement
+# setup geometry: # Number of angles, the actual angles from 0 to
+# pi for parallel beam and 0 to 2pi for fanbeam, set the width of a detector
+# pixel relative to an object pixel, the number of detector pixels, and the
+# source-origin and origin-detector distance (here the origin-detector distance
+# set to 0 to simulate a "virtual detector" with same detector pixel size as
+# object pixel size).
+angles_num = 20
+det_w = 1.0
+det_num = N
+SourceOrig = 200
+OrigDetec = 0
+
+if test_case==1:
+ angles = np.linspace(0,np.pi,angles_num,endpoint=False)
+ ag = AcquisitionGeometry('parallel',
+ '2D',
+ angles,
+ det_num,det_w)
+elif test_case==2:
+ angles = np.linspace(0,2*np.pi,angles_num,endpoint=False)
+ ag = AcquisitionGeometry('cone',
+ '2D',
+ angles,
+ det_num,
+ det_w,
+ dist_source_center=SourceOrig,
+ dist_center_detector=OrigDetec)
+else:
+ NotImplemented
+
+# Set up Operator object combining the ImageGeometry and AcquisitionGeometry
+# wrapping calls to ASTRA as well as specifying whether to use CPU or GPU.
+Aop = AstraProjectorSimple(ig, ag, 'gpu')
+
+# Forward and backprojection are available as methods direct and adjoint. Here
+# generate test data b and do simple backprojection to obtain z.
+b = Aop.direct(Phantom)
+z = Aop.adjoint(b)
+
+plt.imshow(b.array)
+plt.title('Simulated data')
+plt.show()
+
+plt.imshow(z.array)
+plt.title('Backprojected data')
+plt.show()
+
+# Using the test data b, different reconstruction methods can now be set up as
+# demonstrated in the rest of this file. In general all methods need an initial
+# guess and some algorithm options to be set:
+x_init = ImageData(np.zeros(x.shape),geometry=ig)
+opt = {'tol': 1e-4, 'iter': 1000}
+
+# First a CGLS reconstruction can be done:
+x_CGLS, it_CGLS, timing_CGLS, criter_CGLS = CGLS(x_init, Aop, b, opt)
+
+plt.imshow(x_CGLS.array)
+plt.title('CGLS')
+plt.colorbar()
+plt.show()
+
+plt.semilogy(criter_CGLS)
+plt.title('CGLS criterion')
+plt.show()
+
+# A SIRT unconstrained reconstruction can be done: similarly:
+x_SIRT, it_SIRT, timing_SIRT, criter_SIRT = SIRT(x_init, Aop, b, opt)
+
+plt.imshow(x_SIRT.array)
+plt.title('SIRT unconstrained')
+plt.colorbar()
+plt.show()
+
+plt.semilogy(criter_SIRT)
+plt.title('SIRT unconstrained criterion')
+plt.show()
+
+# A SIRT nonnegativity constrained reconstruction can be done using the
+# additional input "constraint" set to a box indicator function with 0 as the
+# lower bound and the default upper bound of infinity:
+x_SIRT0, it_SIRT0, timing_SIRT0, criter_SIRT0 = SIRT(x_init, Aop, b, opt,
+ constraint=IndicatorBox(lower=0))
+
+plt.imshow(x_SIRT0.array)
+plt.title('SIRT nonneg')
+plt.colorbar()
+plt.show()
+
+plt.semilogy(criter_SIRT0)
+plt.title('SIRT nonneg criterion')
+plt.show()
+
+# A SIRT reconstruction with box constraints on [0,1] can also be done:
+x_SIRT01, it_SIRT01, timing_SIRT01, criter_SIRT01 = SIRT(x_init, Aop, b, opt,
+ constraint=IndicatorBox(lower=0,upper=1))
+
+plt.imshow(x_SIRT01.array)
+plt.title('SIRT box(0,1)')
+plt.colorbar()
+plt.show()
+
+plt.semilogy(criter_SIRT01)
+plt.title('SIRT box(0,1) criterion')
+plt.show()
+
+# The indicator function can also be used with the FISTA algorithm to do
+# least squares with nonnegativity constraint.
+
+# Create least squares object instance with projector, test data and a constant
+# coefficient of 0.5:
+f = Norm2sq(Aop,b,c=0.5)
+
+# Run FISTA for least squares without constraints
+x_fista, it, timing, criter = FISTA(x_init, f, None,opt)
+
+plt.imshow(x_fista.array)
+plt.title('FISTA Least squares')
+plt.show()
+
+plt.semilogy(criter)
+plt.title('FISTA Least squares criterion')
+plt.show()
+
+# Run FISTA for least squares with nonnegativity constraint
+x_fista0, it0, timing0, criter0 = FISTA(x_init, f, IndicatorBox(lower=0),opt)
+
+plt.imshow(x_fista0.array)
+plt.title('FISTA Least squares nonneg')
+plt.show()
+
+plt.semilogy(criter0)
+plt.title('FISTA Least squares nonneg criterion')
+plt.show()
+
+# Run FISTA for least squares with box constraint [0,1]
+x_fista01, it01, timing01, criter01 = FISTA(x_init, f, IndicatorBox(lower=0,upper=1),opt)
+
+plt.imshow(x_fista01.array)
+plt.title('FISTA Least squares box(0,1)')
+plt.show()
+
+plt.semilogy(criter01)
+plt.title('FISTA Least squares box(0,1) criterion')
+plt.show() \ No newline at end of file
diff --git a/Wrappers/Python/wip/old_demos/multifile_nexus.py b/Wrappers/Python/wip/old_demos/multifile_nexus.py
new file mode 100755
index 0000000..d1ad438
--- /dev/null
+++ b/Wrappers/Python/wip/old_demos/multifile_nexus.py
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Wed Aug 15 16:00:53 2018
+
+@author: ofn77899
+"""
+
+import os
+from ccpi.io.reader import NexusReader
+
+from sys import getsizeof
+
+import matplotlib.pyplot as plt
+
+from ccpi.framework import DataProcessor, DataContainer
+from ccpi.processors import Normalizer
+from ccpi.processors import CenterOfRotationFinder
+import numpy
+
+
+class averager(object):
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.N = 0
+ self.avg = 0
+ self.min = 0
+ self.max = 0
+ self.var = 0
+ self.ske = 0
+ self.kur = 0
+
+ def add_reading(self, val):
+
+ if (self.N == 0):
+ self.avg = val;
+ self.min = val;
+ self.max = val;
+ elif (self.N == 1):
+ #//set min/max
+ self.max = val if val > self.max else self.max
+ self.min = val if val < self.min else self.min
+
+
+ thisavg = (self.avg + val)/2
+ #// initial value is in avg
+ self.var = (self.avg - thisavg)*(self.avg-thisavg) + (val - thisavg) * (val-thisavg)
+ self.ske = self.var * (self.avg - thisavg)
+ self.kur = self.var * self.var
+ self.avg = thisavg
+ else:
+ self.max = val if val > self.max else self.max
+ self.min = val if val < self.min else self.min
+
+ M = self.N
+
+ #// b-factor =(<v>_N + v_(N+1)) / (N+1)
+ #float b = (val -avg) / (M+1);
+ b = (val -self.avg) / (M+1)
+
+ self.kur = self.kur + (M *(b*b*b*b) * (1+M*M*M))- (4* b * self.ske) + (6 * b *b * self.var * (M-1))
+
+ self.ske = self.ske + (M * b*b*b *(M-1)*(M+1)) - (3 * b * self.var * (M-1))
+
+ #//var = var * ((M-1)/M) + ((val - avg)*(val - avg)/(M+1)) ;
+ self.var = self.var * ((M-1)/M) + (b * b * (M+1))
+
+ self.avg = self.avg * (M/(M+1)) + val / (M+1)
+
+ self.N += 1
+
+ def stats(self, vector):
+ i = 0
+ while i < vector.size:
+ self.add_reading(vector[i])
+ i+=1
+
+avg = averager()
+a = numpy.linspace(0,39,40)
+avg.stats(a)
+print ("average" , avg.avg, a.mean())
+print ("variance" , avg.var, a.var())
+b = a - a.mean()
+b *= b
+b = numpy.sqrt(sum(b)/(a.size-1))
+print ("std" , numpy.sqrt(avg.var), b)
+#%%
+
+class DataStatMoments(DataProcessor):
+ '''Normalization based on flat and dark
+
+ This processor read in a AcquisitionData and normalises it based on
+ the instrument reading with and without incident photons or neutrons.
+
+ Input: AcquisitionData
+ Parameter: 2D projection with flat field (or stack)
+ 2D projection with dark field (or stack)
+ Output: AcquisitionDataSetn
+ '''
+
+ def __init__(self, axis, skewness=False, kurtosis=False, offset=0):
+ kwargs = {
+ 'axis' : axis,
+ 'skewness' : skewness,
+ 'kurtosis' : kurtosis,
+ 'offset' : offset,
+ }
+ #DataProcessor.__init__(self, **kwargs)
+ super(DataStatMoments, self).__init__(**kwargs)
+
+
+ def check_input(self, dataset):
+ #self.N = dataset.get_dimension_size(self.axis)
+ return True
+
+ @staticmethod
+ def add_sample(dataset, N, axis, stats=None, offset=0):
+ #dataset = self.get_input()
+ if (N == 0):
+ # get the axis number along to calculate the stats
+
+
+ #axs = dataset.get_dimension_size(self.axis)
+ # create a placeholder for the output
+ if stats is None:
+ ax = dataset.get_dimension_axis(axis)
+ shape = [dataset.shape[i] for i in range(len(dataset.shape)) if i != ax]
+ # output has 4 components avg, std, skewness and kurtosis + last avg+ (val-thisavg)
+ shape.insert(0, 4+2)
+ stats = numpy.zeros(shape)
+
+ stats[0] = dataset.subset(**{axis:N-offset}).array[:]
+
+ #avg = val
+ elif (N == 1):
+ # val
+ stats[5] = dataset.subset(**{axis:N-offset}).array
+ stats[4] = stats[0] + stats[5]
+ stats[4] /= 2 # thisavg
+ stats[5] -= stats[4] # (val - thisavg)
+
+ #float thisavg = (avg + val)/2;
+
+ #// initial value is in avg
+ #var = (avg - thisavg)*(avg-thisavg) + (val - thisavg) * (val-thisavg);
+ stats[1] = stats[5] * stats[5] + stats[5] * stats[5]
+ #skewness = var * (avg - thisavg);
+ stats[2] = stats[1] * stats[5]
+ #kurtosis = var * var;
+ stats[3] = stats[1] * stats[1]
+ #avg = thisavg;
+ stats[0] = stats[4]
+ else:
+
+ #float M = (float)N;
+ M = N
+ #val
+ stats[4] = dataset.subset(**{axis:N-offset}).array
+ #// b-factor =(<v>_N + v_(N+1)) / (N+1)
+ stats[5] = stats[4] - stats[0]
+ stats[5] /= (M+1) # b factor
+ #float b = (val -avg) / (M+1);
+
+ #kurtosis = kurtosis + (M *(b*b*b*b) * (1+M*M*M))- (4* b * skewness) + (6 * b *b * var * (M-1));
+ #if self.kurtosis:
+ # stats[3] += (M * stats[5] * stats[5] * stats[5] * stats[5]) - \
+ # (4 * stats[5] * stats[2]) + \
+ # (6 * stats[5] * stats[5] * stats[1] * (M-1))
+
+ #skewness = skewness + (M * b*b*b *(M-1)*(M+1)) - (3 * b * var * (M-1));
+ #if self.skewness:
+ # stats[2] = stats[2] + (M * stats[5]* stats[5] * stats[5] * (M-1)*(M-1) ) -\
+ # 3 * stats[5] * stats[1] * (M-1)
+ #//var = var * ((M-1)/M) + ((val - avg)*(val - avg)/(M+1)) ;
+ #var = var * ((M-1)/M) + (b * b * (M+1));
+ stats[1] = ((M-1)/M) * stats[1] + (stats[5] * stats[5] * (M+1))
+
+ #avg = avg * (M/(M+1)) + val / (M+1)
+ stats[0] = stats[0] * (M/(M+1)) + stats[4] / (M+1)
+
+ N += 1
+ return stats , N
+
+
+ def process(self):
+
+ data = self.get_input()
+
+ #stats, i = add_sample(0)
+ N = data.get_dimension_size(self.axis)
+ ax = data.get_dimension_axis(self.axis)
+ stats = None
+ i = 0
+ while i < N:
+ stats , i = DataStatMoments.add_sample(data, i, self.axis, stats, offset=self.offset)
+ self.offset += N
+ labels = ['StatisticalMoments']
+
+ labels += [data.dimension_labels[i] \
+ for i in range(len(data.dimension_labels)) if i != ax]
+ y = DataContainer( stats[:4] , False,
+ dimension_labels=labels)
+ return y
+
+directory = r'E:\Documents\Dataset\CCPi\Nexus_test'
+data_path="entry1/instrument/pco1_hw_hdf_nochunking/data"
+
+reader = NexusReader(os.path.join( os.path.abspath(directory) , '74331.nxs'))
+
+print ("read flat")
+read_flat = NexusReader(os.path.join( os.path.abspath(directory) , '74240.nxs'))
+read_flat.data_path = data_path
+flatsslice = read_flat.get_acquisition_data_whole()
+avg = DataStatMoments('angle')
+
+avg.set_input(flatsslice)
+flats = avg.get_output()
+
+ave = averager()
+ave.stats(flatsslice.array[:,0,0])
+
+print ("avg" , ave.avg, flats.array[0][0][0])
+print ("var" , ave.var, flats.array[1][0][0])
+
+print ("read dark")
+read_dark = NexusReader(os.path.join( os.path.abspath(directory) , '74243.nxs'))
+read_dark.data_path = data_path
+
+## darks are very many so we proceed in batches
+total_size = read_dark.get_projection_dimensions()[0]
+print ("total_size", total_size)
+
+batchsize = 40
+if batchsize > total_size:
+ batchlimits = [batchsize * (i+1) for i in range(int(total_size/batchsize))] + [total_size-1]
+else:
+ batchlimits = [total_size]
+#avg.N = 0
+avg.offset = 0
+N = 0
+for batch in range(len(batchlimits)):
+ print ("running batch " , batch)
+ bmax = batchlimits[batch]
+ bmin = bmax-batchsize
+
+ darksslice = read_dark.get_acquisition_data_batch(bmin,bmax)
+ if batch == 0:
+ #create stats
+ ax = darksslice.get_dimension_axis('angle')
+ shape = [darksslice.shape[i] for i in range(len(darksslice.shape)) if i != ax]
+ # output has 4 components avg, std, skewness and kurtosis + last avg+ (val-thisavg)
+ shape.insert(0, 4+2)
+ print ("create stats shape ", shape)
+ stats = numpy.zeros(shape)
+ print ("N" , N)
+ #avg.set_input(darksslice)
+ i = bmin
+ while i < bmax:
+ stats , i = DataStatMoments.add_sample(darksslice, i, 'angle', stats, bmin)
+ print ("{0}-{1}-{2}".format(bmin, i , bmax ) )
+
+darks = stats
+#%%
+
+fig = plt.subplot(2,2,1)
+fig.imshow(flats.subset(StatisticalMoments=0).array)
+fig = plt.subplot(2,2,2)
+fig.imshow(numpy.sqrt(flats.subset(StatisticalMoments=1).array))
+fig = plt.subplot(2,2,3)
+fig.imshow(darks[0])
+fig = plt.subplot(2,2,4)
+fig.imshow(numpy.sqrt(darks[1]))
+plt.show()
+
+
+#%%
+norm = Normalizer(flat_field=flats.array[0,200,:], dark_field=darks[0,200,:])
+#norm.set_flat_field(flats.array[0,200,:])
+#norm.set_dark_field(darks.array[0,200,:])
+norm.set_input(reader.get_acquisition_data_slice(200))
+
+n = Normalizer.normalize_projection(norm.get_input().as_array(), flats.array[0,200,:], darks[0,200,:], 1e-5)
+#dn_n= Normalizer.estimate_normalised_error(norm.get_input().as_array(), flats.array[0,200,:], darks[0,200,:],
+# numpy.sqrt(flats.array[1,200,:]), numpy.sqrt(darks[1,200,:]))
+#%%
+
+
+#%%
+fig = plt.subplot(2,1,1)
+
+
+fig.imshow(norm.get_input().as_array())
+fig = plt.subplot(2,1,2)
+fig.imshow(n)
+
+#fig = plt.subplot(3,1,3)
+#fig.imshow(dn_n)
+
+
+plt.show()
+
+
+
+
+
+
diff --git a/Wrappers/Python/wip/pdhg_TV_denoising.py b/Wrappers/Python/wip/pdhg_TV_denoising.py
deleted file mode 100755
index a5cd1bf..0000000
--- a/Wrappers/Python/wip/pdhg_TV_denoising.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
- MixedL21Norm, FunctionOperatorComposition, BlockFunction, ScaledFunction
-
-from skimage.util import random_noise
-
-from timeit import default_timer as timer
-def dt(steps):
- return steps[-1] - steps[-2]
-
-# Create phantom for TV denoising
-
-N = 100
-
-data = np.zeros((N,N))
-data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-ag = ig
-
-# Create noisy data. Add Gaussian noise
-n1 = random_noise(data, mode = 'gaussian', mean=0, var = 0.05, seed=10)
-noisy_data = ImageData(n1)
-
-
-plt.imshow(noisy_data.as_array())
-plt.title('Noisy data')
-plt.show()
-
-# Regularisation Parameter
-alpha = 2
-
-#method = input("Enter structure of PDHG (0=Composite or 1=NotComposite): ")
-method = '1'
-
-if method == '0':
-
- # Create operators
- op1 = Gradient(ig)
- op2 = Identity(ig, ag)
-
- # Form Composite Operator
- operator = BlockOperator(op1, op2, shape=(2,1) )
-
- #### Create functions
-
- f1 = alpha * MixedL21Norm()
- f2 = 0.5 * L2NormSquared(b = noisy_data)
- f = BlockFunction(f1, f2)
-
- g = ZeroFunction()
-
-else:
-
- ###########################################################################
- # No Composite #
- ###########################################################################
- operator = Gradient(ig)
- f = alpha * MixedL21Norm()
- g = 0.5 * L2NormSquared(b = noisy_data)
-
-# Compute operator Norm
-normK = operator.norm()
-
-# Primal & dual stepsizes
-sigma = 1
-tau = 1/(sigma*normK**2)
-
-opt = {'niter':2000}
-opt1 = {'niter':2000, 'memopt': True}
-
-t1 = timer()
-res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-t2 = timer()
-
-print(" Run memopt")
-
-t3 = timer()
-res1, time1, primal1, dual1, pdgap1 = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt1)
-t4 = timer()
-
-#%%
-plt.figure(figsize=(15,15))
-plt.subplot(3,1,1)
-plt.imshow(res.as_array())
-plt.title('no memopt')
-plt.colorbar()
-plt.subplot(3,1,2)
-plt.imshow(res1.as_array())
-plt.title('memopt')
-plt.colorbar()
-plt.subplot(3,1,3)
-plt.imshow((res1 - res).abs().as_array())
-plt.title('diff')
-plt.colorbar()
-plt.show()
-#
-plt.plot(np.linspace(0,N,N), res1.as_array()[int(N/2),:], label = 'memopt')
-plt.plot(np.linspace(0,N,N), res.as_array()[int(N/2),:], label = 'no memopt')
-plt.legend()
-plt.show()
-
-print ("Time: No memopt in {}s, \n Time: Memopt in {}s ".format(t2-t1, t4 -t3))
-diff = (res1 - res).abs().as_array().max()
-
-print(" Max of abs difference is {}".format(diff))
-
-
diff --git a/Wrappers/Python/wip/pdhg_TV_denoising3D.py b/Wrappers/Python/wip/pdhg_TV_denoising3D.py
deleted file mode 100644
index 06ecfa2..0000000
--- a/Wrappers/Python/wip/pdhg_TV_denoising3D.py
+++ /dev/null
@@ -1,360 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
- MixedL21Norm, FunctionOperatorComposition, BlockFunction
-
-from skimage.util import random_noise
-
-from timeit import default_timer as timer
-def dt(steps):
- return steps[-1] - steps[-2]
-
-#%%
-
-# Create phantom for TV denoising
-
-import timeit
-import os
-from tomophantom import TomoP3D
-import tomophantom
-
-print ("Building 3D phantom using TomoPhantom software")
-tic=timeit.default_timer()
-model = 13 # select a model number from the library
-N_size = 64 # Define phantom dimensions using a scalar value (cubic phantom)
-path = os.path.dirname(tomophantom.__file__)
-path_library3D = os.path.join(path, "Phantom3DLibrary.dat")
-#This will generate a N_size x N_size x N_size phantom (3D)
-phantom_tm = TomoP3D.Model(model, N_size, path_library3D)
-#toc=timeit.default_timer()
-#Run_time = toc - tic
-#print("Phantom has been built in {} seconds".format(Run_time))
-#
-#sliceSel = int(0.5*N_size)
-##plt.gray()
-#plt.figure()
-#plt.subplot(131)
-#plt.imshow(phantom_tm[sliceSel,:,:],vmin=0, vmax=1)
-#plt.title('3D Phantom, axial view')
-#
-#plt.subplot(132)
-#plt.imshow(phantom_tm[:,sliceSel,:],vmin=0, vmax=1)
-#plt.title('3D Phantom, coronal view')
-#
-#plt.subplot(133)
-#plt.imshow(phantom_tm[:,:,sliceSel],vmin=0, vmax=1)
-#plt.title('3D Phantom, sagittal view')
-#plt.show()
-
-#%%
-
-N = N_size
-ig = ImageGeometry(voxel_num_x=N, voxel_num_y=N, voxel_num_z=N)
-
-n1 = random_noise(phantom_tm, mode = 'gaussian', mean=0, var = 0.001, seed=10)
-noisy_data = ImageData(n1)
-#plt.imshow(noisy_data.as_array()[:,:,32])
-
-#%%
-
-# Regularisation Parameter
-alpha = 0.02
-
-#method = input("Enter structure of PDHG (0=Composite or 1=NotComposite): ")
-method = '0'
-
-if method == '0':
-
- # Create operators
- op1 = Gradient(ig)
- op2 = Identity(ig)
-
- # Form Composite Operator
- operator = BlockOperator(op1, op2, shape=(2,1) )
-
- #### Create functions
-
- f1 = alpha * MixedL21Norm()
- f2 = 0.5 * L2NormSquared(b = noisy_data)
- f = BlockFunction(f1, f2)
-
- g = ZeroFunction()
-
-else:
-
- ###########################################################################
- # No Composite #
- ###########################################################################
- operator = Gradient(ig)
- f = alpha * FunctionOperatorComposition(operator, MixedL21Norm())
- g = L2NormSquared(b = noisy_data)
-
- ###########################################################################
-#%%
-
-# Compute operator Norm
-normK = operator.norm()
-
-# Primal & dual stepsizes
-sigma = 1
-tau = 1/(sigma*normK**2)
-
-opt = {'niter':2000}
-opt1 = {'niter':2000, 'memopt': True}
-
-#t1 = timer()
-#res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-#t2 = timer()
-
-
-t3 = timer()
-res1, time1, primal1, dual1, pdgap1 = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt1)
-t4 = timer()
-
-#import cProfile
-#cProfile.run('res1, time1, primal1, dual1, pdgap1 = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt1) ')
-###
-print ("No memopt in {}s, memopt in {}s ".format(t2-t1, t4 -t3))
-#
-##
-##%%
-#
-#plt.figure(figsize=(10,10))
-#plt.subplot(311)
-#plt.imshow(res1.as_array()[sliceSel,:,:])
-#plt.colorbar()
-#plt.title('3D Phantom, axial view')
-#
-#plt.subplot(312)
-#plt.imshow(res1.as_array()[:,sliceSel,:])
-#plt.colorbar()
-#plt.title('3D Phantom, coronal view')
-#
-#plt.subplot(313)
-#plt.imshow(res1.as_array()[:,:,sliceSel])
-#plt.colorbar()
-#plt.title('3D Phantom, sagittal view')
-#plt.show()
-#
-#plt.figure(figsize=(10,10))
-#plt.subplot(311)
-#plt.imshow(res.as_array()[sliceSel,:,:])
-#plt.colorbar()
-#plt.title('3D Phantom, axial view')
-#
-#plt.subplot(312)
-#plt.imshow(res.as_array()[:,sliceSel,:])
-#plt.colorbar()
-#plt.title('3D Phantom, coronal view')
-#
-#plt.subplot(313)
-#plt.imshow(res.as_array()[:,:,sliceSel])
-#plt.colorbar()
-#plt.title('3D Phantom, sagittal view')
-#plt.show()
-#
-#diff = (res1 - res).abs()
-#
-#plt.figure(figsize=(10,10))
-#plt.subplot(311)
-#plt.imshow(diff.as_array()[sliceSel,:,:])
-#plt.colorbar()
-#plt.title('3D Phantom, axial view')
-#
-#plt.subplot(312)
-#plt.imshow(diff.as_array()[:,sliceSel,:])
-#plt.colorbar()
-#plt.title('3D Phantom, coronal view')
-#
-#plt.subplot(313)
-#plt.imshow(diff.as_array()[:,:,sliceSel])
-#plt.colorbar()
-#plt.title('3D Phantom, sagittal view')
-#plt.show()
-#
-#
-#
-#
-##%%
-#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-#pdhg.max_iteration = 2000
-#pdhg.update_objective_interval = 100
-####
-#pdhgo = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
-#pdhgo.max_iteration = 2000
-#pdhgo.update_objective_interval = 100
-####
-#steps = [timer()]
-#pdhgo.run(2000)
-#steps.append(timer())
-#t1 = dt(steps)
-##
-#pdhg.run(2000)
-#steps.append(timer())
-#t2 = dt(steps)
-#
-#print ("Time difference {}s {}s {}s Speedup {:.2f}".format(t1,t2,t2-t1, t2/t1))
-#res = pdhg.get_output()
-#res1 = pdhgo.get_output()
-
-#%%
-#plt.figure(figsize=(15,15))
-#plt.subplot(3,1,1)
-#plt.imshow(res.as_array())
-#plt.title('no memopt')
-#plt.colorbar()
-#plt.subplot(3,1,2)
-#plt.imshow(res1.as_array())
-#plt.title('memopt')
-#plt.colorbar()
-#plt.subplot(3,1,3)
-#plt.imshow((res1 - res).abs().as_array())
-#plt.title('diff')
-#plt.colorbar()
-#plt.show()
-
-
-#plt.figure(figsize=(15,15))
-#plt.subplot(3,1,1)
-#plt.imshow(pdhg.get_output().as_array())
-#plt.title('no memopt class')
-#plt.colorbar()
-#plt.subplot(3,1,2)
-#plt.imshow(res.as_array())
-#plt.title('no memopt')
-#plt.colorbar()
-#plt.subplot(3,1,3)
-#plt.imshow((pdhg.get_output() - res).abs().as_array())
-#plt.title('diff')
-#plt.colorbar()
-#plt.show()
-#
-#
-#
-#plt.figure(figsize=(15,15))
-#plt.subplot(3,1,1)
-#plt.imshow(pdhgo.get_output().as_array())
-#plt.title('memopt class')
-#plt.colorbar()
-#plt.subplot(3,1,2)
-#plt.imshow(res1.as_array())
-#plt.title('no memopt')
-#plt.colorbar()
-#plt.subplot(3,1,3)
-#plt.imshow((pdhgo.get_output() - res1).abs().as_array())
-#plt.title('diff')
-#plt.colorbar()
-#plt.show()
-
-
-
-
-
-# print ("Time difference {}s {}s {}s Speedup {:.2f}".format(t1,t2,t2-t1, t2/t1))
-# res = pdhg.get_output()
-# res1 = pdhgo.get_output()
-#
-# diff = (res-res1)
-# print ("diff norm {} max {}".format(diff.norm(), diff.abs().as_array().max()))
-# print ("Sum ( abs(diff) ) {}".format(diff.abs().sum()))
-#
-#
-# plt.figure(figsize=(5,5))
-# plt.subplot(1,3,1)
-# plt.imshow(res.as_array())
-# plt.colorbar()
-# #plt.show()
-#
-# #plt.figure(figsize=(5,5))
-# plt.subplot(1,3,2)
-# plt.imshow(res1.as_array())
-# plt.colorbar()
-
-#plt.show()
-
-
-
-#=======
-## opt = {'niter':2000, 'memopt': True}
-#
-## res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-#
-#>>>>>>> origin/pdhg_fix
-#
-#
-## opt = {'niter':2000, 'memopt': False}
-## res1, time1, primal1, dual1, pdgap1 = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-#
-## plt.figure(figsize=(5,5))
-## plt.subplot(1,3,1)
-## plt.imshow(res.as_array())
-## plt.title('memopt')
-## plt.colorbar()
-## plt.subplot(1,3,2)
-## plt.imshow(res1.as_array())
-## plt.title('no memopt')
-## plt.colorbar()
-## plt.subplot(1,3,3)
-## plt.imshow((res1 - res).abs().as_array())
-## plt.title('diff')
-## plt.colorbar()
-## plt.show()
-#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-#pdhg.max_iteration = 2000
-#pdhg.update_objective_interval = 100
-#
-#
-#pdhgo = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
-#pdhgo.max_iteration = 2000
-#pdhgo.update_objective_interval = 100
-#
-#steps = [timer()]
-#pdhgo.run(200)
-#steps.append(timer())
-#t1 = dt(steps)
-#
-#pdhg.run(200)
-#steps.append(timer())
-#t2 = dt(steps)
-#
-#print ("Time difference {} {} {}".format(t1,t2,t2-t1))
-#sol = pdhg.get_output().as_array()
-##sol = result.as_array()
-##
-#fig = plt.figure()
-#plt.subplot(1,3,1)
-#plt.imshow(noisy_data.as_array())
-#plt.colorbar()
-#plt.subplot(1,3,2)
-#plt.imshow(sol)
-#plt.colorbar()
-#plt.subplot(1,3,3)
-#plt.imshow(pdhgo.get_output().as_array())
-#plt.colorbar()
-#
-#plt.show()
-###
-##
-####
-##plt.plot(np.linspace(0,N,N), data[int(N/2),:], label = 'GTruth')
-##plt.plot(np.linspace(0,N,N), sol[int(N/2),:], label = 'Recon')
-##plt.legend()
-##plt.show()
-#
-#
-##%%
-##
diff --git a/Wrappers/Python/wip/pdhg_TV_denoising_salt_pepper.py b/Wrappers/Python/wip/pdhg_TV_denoising_salt_pepper.py
deleted file mode 100644
index cec9770..0000000
--- a/Wrappers/Python/wip/pdhg_TV_denoising_salt_pepper.py
+++ /dev/null
@@ -1,180 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFun, L1Norm, \
- MixedL21Norm, FunctionOperatorComposition, BlockFunction
-
-
-from skimage.util import random_noise
-
-
-
-# ############################################################################
-# Create phantom for TV denoising
-
-N = 100
-data = np.zeros((N,N))
-data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-ag = ig
-
-# Create noisy data. Add Gaussian noise
-n1 = random_noise(data, mode = 's&p', salt_vs_pepper = 0.9, amount=0.2)
-noisy_data = ImageData(n1)
-
-plt.imshow(noisy_data.as_array())
-plt.colorbar()
-plt.show()
-
-#%%
-
-# Regularisation Parameter
-alpha = 2
-
-#method = input("Enter structure of PDHG (0=Composite or 1=NotComposite): ")
-method = '0'
-if method == '0':
-
- # Create operators
- op1 = Gradient(ig)
- op2 = Identity(ig, ag)
-
- # Form Composite Operator
- operator = BlockOperator(op1, op2, shape=(2,1) )
-
- #### Create functions
-# f = FunctionComposition_new(operator, mixed_L12Norm(alpha), \
-# L2NormSq(0.5, b = noisy_data) )
-
- f1 = alpha * MixedL21Norm()
- f2 = L1Norm(b = noisy_data)
-
- f = BlockFunction(f1, f2 )
- g = ZeroFun()
-
-else:
-
- ###########################################################################
- # No Composite #
- ###########################################################################
- operator = Gradient(ig)
- f = alpha * FunctionOperatorComposition(operator, MixedL21Norm())
- g = L1Norm(b = noisy_data)
- ###########################################################################
-#%%
-
-diag_precon = True
-
-if diag_precon:
-
- def tau_sigma_precond(operator):
-
- tau = 1/operator.sum_abs_row()
- sigma = 1/ operator.sum_abs_col()
-
- return tau, sigma
-
- tau, sigma = tau_sigma_precond(operator)
-
-else:
- # Compute operator Norm
- normK = operator.norm()
- print ("normK", normK)
- # Primal & dual stepsizes
- sigma = 1/normK
- tau = 1/normK
-# tau = 1/(sigma*normK**2)
-
-opt = {'niter':2000}
-
-res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-
-plt.figure(figsize=(5,5))
-plt.imshow(res.as_array())
-plt.colorbar()
-plt.show()
-
-#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-#pdhg.max_iteration = 2000
-#pdhg.update_objective_interval = 10
-#
-#pdhg.run(2000)
-
-
-
-#sol = pdhg.get_output().as_array()
-##sol = result.as_array()
-##
-#fig = plt.figure()
-#plt.subplot(1,2,1)
-#plt.imshow(noisy_data.as_array())
-##plt.colorbar()
-#plt.subplot(1,2,2)
-#plt.imshow(sol)
-##plt.colorbar()
-#plt.show()
-##
-
-##
-#plt.plot(np.linspace(0,N,N), data[int(N/2),:], label = 'GTruth')
-#plt.plot(np.linspace(0,N,N), sol[int(N/2),:], label = 'Recon')
-#plt.legend()
-#plt.show()
-
-
-#%% Compare with cvx
-
-try_cvx = input("Do you want CVX comparison (0/1)")
-
-if try_cvx=='0':
-
- from cvxpy import *
- import sys
- sys.path.insert(0,'/Users/evangelos/Desktop/Projects/CCPi/CCPi-Framework/Wrappers/Python/ccpi/optimisation/cvx_scripts')
- from cvx_functions import TV_cvx
-
- u = Variable((N, N))
- fidelity = pnorm( u - noisy_data.as_array(),1)
- regulariser = alpha * TV_cvx(u)
- solver = MOSEK
- obj = Minimize( regulariser + fidelity)
- constraints = []
- prob = Problem(obj, constraints)
-
- # Choose solver (SCS is fast but less accurate than MOSEK)
- result = prob.solve(verbose = True, solver = solver)
-
- print('Objective value is {} '.format(obj.value))
-
- diff_pdhg_cvx = np.abs(u.value - res.as_array())
- plt.imshow(diff_pdhg_cvx)
- plt.colorbar()
- plt.title('|CVX-PDHG|')
- plt.show()
-
- plt.plot(np.linspace(0,N,N), u.value[int(N/2),:], label = 'CVX')
- plt.plot(np.linspace(0,N,N), res.as_array()[int(N/2),:], label = 'PDHG')
- plt.legend()
- plt.show()
-
-else:
- print('No CVX solution available')
-
-
-
-
diff --git a/Wrappers/Python/wip/pdhg_TV_tomography2D.py b/Wrappers/Python/wip/pdhg_TV_tomography2D.py
deleted file mode 100644
index 3fec34e..0000000
--- a/Wrappers/Python/wip/pdhg_TV_tomography2D.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer, AcquisitionGeometry, AcquisitionData
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
- MixedL21Norm, BlockFunction, ScaledFunction
-
-from ccpi.astra.ops import AstraProjectorSimple
-from skimage.util import random_noise
-from timeit import default_timer as timer
-
-
-#%%###############################################################################
-# Create phantom for TV tomography
-
-#import os
-#import tomophantom
-#from tomophantom import TomoP2D
-#from tomophantom.supp.qualitymetrics import QualityTools
-
-#model = 1 # select a model number from the library
-#N = 150 # set dimension of the phantom
-## one can specify an exact path to the parameters file
-## path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
-#path = os.path.dirname(tomophantom.__file__)
-#path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
-##This will generate a N_size x N_size phantom (2D)
-#phantom_2D = TomoP2D.Model(model, N, path_library2D)
-#ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-#data = ImageData(phantom_2D, geometry=ig)
-
-N = 150
-x = np.zeros((N,N))
-x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-data = ImageData(x)
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-
-
-detectors = 150
-angles = np.linspace(0,np.pi,100)
-
-ag = AcquisitionGeometry('parallel','2D',angles, detectors)
-Aop = AstraProjectorSimple(ig, ag, 'gpu')
-sin = Aop.direct(data)
-
-plt.imshow(sin.as_array())
-plt.title('Sinogram')
-plt.colorbar()
-plt.show()
-
-# Add Gaussian noise to the sinogram data
-np.random.seed(10)
-n1 = np.random.random(sin.shape)
-
-noisy_data = sin + ImageData(5*n1)
-
-plt.imshow(noisy_data.as_array())
-plt.title('Noisy Sinogram')
-plt.colorbar()
-plt.show()
-
-
-#%% Works only with Composite Operator Structure of PDHG
-
-#ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-
-# Create operators
-op1 = Gradient(ig)
-op2 = Aop
-
-# Form Composite Operator
-operator = BlockOperator(op1, op2, shape=(2,1) )
-
-alpha = 50
-f = BlockFunction( alpha * MixedL21Norm(), \
- 0.5 * L2NormSquared(b = noisy_data) )
-g = ZeroFunction()
-
-# Compute operator Norm
-normK = operator.norm()
-
-## Primal & dual stepsizes
-diag_precon = False
-
-if diag_precon:
-
- def tau_sigma_precond(operator):
-
- tau = 1/operator.sum_abs_row()
- sigma = 1/ operator.sum_abs_col()
-
- return tau, sigma
-
- tau, sigma = tau_sigma_precond(operator)
-
-else:
- sigma = 1
- tau = 1/(sigma*normK**2)
-
-# Compute operator Norm
-normK = operator.norm()
-
-# Primal & dual stepsizes
-sigma = 1
-tau = 1/(sigma*normK**2)
-
-opt = {'niter':2000}
-opt1 = {'niter':2000, 'memopt': True}
-
-t1 = timer()
-res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-t2 = timer()
-
-
-t3 = timer()
-res1, time1, primal1, dual1, pdgap1 = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt1)
-t4 = timer()
-#
-print ("No memopt in {}s, memopt in {}s ".format(t2-t1, t4 -t3))
-
-
-#%%
-#sol = pdhg.get_output().as_array()
-#fig = plt.figure()
-#plt.subplot(1,2,1)
-#plt.imshow(noisy_data.as_array())
-##plt.colorbar()
-#plt.subplot(1,2,2)
-#plt.imshow(sol)
-##plt.colorbar()
-#plt.show()
-#
-#
-##%%
-#plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'GTruth')
-#plt.plot(np.linspace(0,N,N), sol[int(N/2),:], label = 'Recon')
-#plt.legend()
-#plt.show()
-
-
diff --git a/Wrappers/Python/wip/pdhg_TV_tomography2D_time.py b/Wrappers/Python/wip/pdhg_TV_tomography2D_time.py
deleted file mode 100644
index dea8e5c..0000000
--- a/Wrappers/Python/wip/pdhg_TV_tomography2D_time.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer, AcquisitionGeometry, AcquisitionData
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFun, L2NormSquared, \
- MixedL21Norm, BlockFunction, ScaledFunction
-
-from ccpi.astra.ops import AstraProjectorSimple, AstraProjectorMC
-from skimage.util import random_noise
-
-
-#%%###############################################################################
-# Create phantom for TV tomography
-
-import numpy as np
-import matplotlib.pyplot as plt
-import os
-import tomophantom
-from tomophantom import TomoP2D
-
-model = 102 # note that the selected model is temporal (2D + time)
-N = 150 # set dimension of the phantom
-# one can specify an exact path to the parameters file
-# path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
-path = os.path.dirname(tomophantom.__file__)
-path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
-#This will generate a N_size x N_size x Time frames phantom (2D + time)
-phantom_2Dt = TomoP2D.ModelTemporal(model, N, path_library2D)
-
-plt.close('all')
-plt.figure(1)
-plt.rcParams.update({'font.size': 21})
-plt.title('{}''{}'.format('2D+t phantom using model no.',model))
-for sl in range(0,np.shape(phantom_2Dt)[0]):
- im = phantom_2Dt[sl,:,:]
- plt.imshow(im, vmin=0, vmax=1)
- plt.pause(.1)
- plt.draw
-
-#N = 150
-#x = np.zeros((N,N))
-#x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-#x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-#%%
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N, channels = np.shape(phantom_2Dt)[0])
-data = ImageData(phantom_2Dt, geometry=ig)
-
-
-
-detectors = 150
-angles = np.linspace(0,np.pi,100)
-
-ag = AcquisitionGeometry('parallel','2D',angles, detectors, channels = np.shape(phantom_2Dt)[0])
-Aop = AstraProjectorMC(ig, ag, 'gpu')
-sin = Aop.direct(data)
-
-plt.imshow(sin.as_array()[10])
-plt.title('Sinogram')
-plt.colorbar()
-plt.show()
-
-# Add Gaussian noise to the sinogram data
-np.random.seed(10)
-n1 = np.random.random(sin.shape)
-
-noisy_data = sin + ImageData(5*n1)
-
-plt.imshow(noisy_data.as_array()[10])
-plt.title('Noisy Sinogram')
-plt.colorbar()
-plt.show()
-
-
-#%% Works only with Composite Operator Structure of PDHG
-
-#ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-
-# Create operators
-op1 = Gradient(ig)
-op2 = Aop
-
-# Form Composite Operator
-operator = BlockOperator(op1, op2, shape=(2,1) )
-
-alpha = 50
-f = BlockFunction( alpha * MixedL21Norm(), \
- 0.5 * L2NormSquared(b = noisy_data) )
-g = ZeroFun()
-
-# Compute operator Norm
-normK = operator.norm()
-
-## Primal & dual stepsizes
-
-sigma = 1
-tau = 1/(sigma*normK**2)
-
-#sigma = 1/normK
-#tau = 1/normK
-
-opt = {'niter':2000}
-
-res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-
-plt.figure(figsize=(5,5))
-plt.imshow(res.as_array())
-plt.colorbar()
-plt.show()
-
-#sigma = 10
-#tau = 1/(sigma*normK**2)
-#
-#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-#pdhg.max_iteration = 5000
-#pdhg.update_objective_interval = 20
-#
-#pdhg.run(5000)
-#
-##%%
-#sol = pdhg.get_output().as_array()
-#fig = plt.figure()
-#plt.subplot(1,2,1)
-#plt.imshow(noisy_data.as_array())
-##plt.colorbar()
-#plt.subplot(1,2,2)
-#plt.imshow(sol)
-##plt.colorbar()
-#plt.show()
-
-
-#%%
-plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'GTruth')
-plt.plot(np.linspace(0,N,N), sol[int(N/2),:], label = 'Recon')
-plt.legend()
-plt.show()
-
-
diff --git a/Wrappers/Python/wip/pdhg_tv_denoising_poisson.py b/Wrappers/Python/wip/pdhg_tv_denoising_poisson.py
deleted file mode 100644
index 9fad6f8..0000000
--- a/Wrappers/Python/wip/pdhg_tv_denoising_poisson.py
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFun, KullbackLeibler, \
- MixedL21Norm, FunctionOperatorComposition, BlockFunction
-
-
-from skimage.util import random_noise
-
-
-
-# ############################################################################
-# Create phantom for TV denoising
-
-N = 100
-data = np.zeros((N,N))
-data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-ag = ig
-
-# Create noisy data. Add Gaussian noise
-n1 = random_noise(data, mode = 'poisson')
-noisy_data = ImageData(n1)
-
-plt.imshow(noisy_data.as_array())
-plt.colorbar()
-plt.show()
-
-#%%
-
-# Regularisation Parameter
-alpha = 10
-
-#method = input("Enter structure of PDHG (0=Composite or 1=NotComposite): ")
-method = '1'
-if method == '0':
-
- # Create operators
- op1 = Gradient(ig)
- op2 = Identity(ig, ag)
-
- # Form Composite Operator
- operator = BlockOperator(op1, op2, shape=(2,1) )
-
- #### Create functions
-# f = FunctionComposition_new(operator, mixed_L12Norm(alpha), \
-# L2NormSq(0.5, b = noisy_data) )
-
- f1 = alpha * MixedL21Norm()
- f2 = KullbackLeibler(b = noisy_data)
-
- f = BlockFunction(f1, f2 )
- g = ZeroFun()
-
-else:
-
- ###########################################################################
- # No Composite #
- ###########################################################################
- operator = Gradient(ig)
- f = alpha * FunctionOperatorComposition(operator, MixedL21Norm())
- g = KullbackLeibler(noisy_data)
- ###########################################################################
-#%%
-
-# Compute operator Norm
-normK = operator.norm()
-print ("normK", normK)
-# Primal & dual stepsizes
-#sigma = 1
-#tau = 1/(sigma*normK**2)
-
-sigma = 1/normK
-tau = 1/normK
-
-opt = {'niter':2000}
-
-res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-
-plt.figure(figsize=(5,5))
-plt.imshow(res.as_array())
-plt.colorbar()
-plt.show()
-
-#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-#pdhg.max_iteration = 2000
-#pdhg.update_objective_interval = 10
-#
-#pdhg.run(2000)
-
-
-
-#sol = pdhg.get_output().as_array()
-##sol = result.as_array()
-##
-#fig = plt.figure()
-#plt.subplot(1,2,1)
-#plt.imshow(noisy_data.as_array())
-##plt.colorbar()
-#plt.subplot(1,2,2)
-#plt.imshow(sol)
-##plt.colorbar()
-#plt.show()
-##
-
-##
-#plt.plot(np.linspace(0,N,N), data[int(N/2),:], label = 'GTruth')
-#plt.plot(np.linspace(0,N,N), sol[int(N/2),:], label = 'Recon')
-#plt.legend()
-#plt.show()
-
-
-#%% Compare with cvx
-
-#try_cvx = input("Do you want CVX comparison (0/1)")
-#
-#if try_cvx=='0':
-#
-# from cvxpy import *
-# import sys
-# sys.path.insert(0,'/Users/evangelos/Desktop/Projects/CCPi/CCPi-Framework/Wrappers/Python/ccpi/optimisation/cvx_scripts')
-# from cvx_functions import TV_cvx
-#
-# u = Variable((N, N))
-# fidelity = pnorm( u - noisy_data.as_array(),1)
-# regulariser = alpha * TV_cvx(u)
-# solver = MOSEK
-# obj = Minimize( regulariser + fidelity)
-# constraints = []
-# prob = Problem(obj, constraints)
-#
-# # Choose solver (SCS is fast but less accurate than MOSEK)
-# result = prob.solve(verbose = True, solver = solver)
-#
-# print('Objective value is {} '.format(obj.value))
-#
-# diff_pdhg_cvx = np.abs(u.value - res.as_array())
-# plt.imshow(diff_pdhg_cvx)
-# plt.colorbar()
-# plt.title('|CVX-PDHG|')
-# plt.show()
-#
-# plt.plot(np.linspace(0,N,N), u.value[int(N/2),:], label = 'CVX')
-# plt.plot(np.linspace(0,N,N), res.as_array()[int(N/2),:], label = 'PDHG')
-# plt.legend()
-# plt.show()
-#
-#else:
-# print('No CVX solution available')
-
-
-
-
diff --git a/Wrappers/Python/wip/test_pdhg_gap.py b/Wrappers/Python/wip/test_pdhg_gap.py
deleted file mode 100644
index 6c7ccc9..0000000
--- a/Wrappers/Python/wip/test_pdhg_gap.py
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Tue Apr 2 12:26:24 2019
-
-@author: vaggelis
-"""
-
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer, AcquisitionGeometry, AcquisitionData
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFun, L2NormSquared, \
- MixedL21Norm, BlockFunction, ScaledFunction
-
-from ccpi.astra.ops import AstraProjectorSimple
-from skimage.util import random_noise
-
-
-#%%###############################################################################
-# Create phantom for TV tomography
-
-#import os
-#import tomophantom
-#from tomophantom import TomoP2D
-#from tomophantom.supp.qualitymetrics import QualityTools
-
-#model = 1 # select a model number from the library
-#N = 150 # set dimension of the phantom
-## one can specify an exact path to the parameters file
-## path_library2D = '../../../PhantomLibrary/models/Phantom2DLibrary.dat'
-#path = os.path.dirname(tomophantom.__file__)
-#path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
-##This will generate a N_size x N_size phantom (2D)
-#phantom_2D = TomoP2D.Model(model, N, path_library2D)
-#ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-#data = ImageData(phantom_2D, geometry=ig)
-
-N = 150
-x = np.zeros((N,N))
-x[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-x[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-data = ImageData(x)
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-
-
-detectors = 150
-angles = np.linspace(0,np.pi,100)
-
-ag = AcquisitionGeometry('parallel','2D',angles, detectors)
-Aop = AstraProjectorSimple(ig, ag, 'cpu')
-sin = Aop.direct(data)
-
-plt.imshow(sin.as_array())
-plt.title('Sinogram')
-plt.colorbar()
-plt.show()
-
-# Add Gaussian noise to the sinogram data
-np.random.seed(10)
-n1 = np.random.random(sin.shape)
-
-noisy_data = sin + ImageData(5*n1)
-
-plt.imshow(noisy_data.as_array())
-plt.title('Noisy Sinogram')
-plt.colorbar()
-plt.show()
-
-
-#%% Works only with Composite Operator Structure of PDHG
-
-#ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-
-# Create operators
-op1 = Gradient(ig)
-op2 = Aop
-
-# Form Composite Operator
-operator = BlockOperator(op1, op2, shape=(2,1) )
-
-alpha = 50
-f = BlockFunction( alpha * MixedL21Norm(), \
- 0.5 * L2NormSquared(b = noisy_data) )
-g = ZeroFun()
-
-# Compute operator Norm
-normK = operator.norm()
-
-## Primal & dual stepsizes
-
-sigma = 10
-tau = 1/(sigma*normK**2)
-
-pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-pdhg.max_iteration = 2000
-pdhg.update_objective_interval = 100
-
-pdhg.run(5000)
-#%%
-
-opt = {'niter':2000}
-
-res = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-
-#%%
-sol = pdhg.get_output().as_array()
-sol_old = res[0].as_array()
-fig = plt.figure(figsize=(20,10))
-plt.subplot(1,3,1)
-plt.imshow(noisy_data.as_array())
-#plt.colorbar()
-plt.subplot(1,3,2)
-plt.imshow(sol)
-#plt.colorbar()
-
-plt.subplot(1,3,3)
-plt.imshow(sol_old)
-plt.show()
-
-plt.imshow(np.abs(sol-sol_old))
-plt.colorbar()
-plt.show()
-
-
-#
-#
-##%%
-#plt.plot(np.linspace(0,N,N), data.as_array()[int(N/2),:], label = 'GTruth')
-#plt.plot(np.linspace(0,N,N), sol[int(N/2),:], label = 'Recon')
-#plt.legend()
-#plt.show()
-
-
diff --git a/Wrappers/Python/wip/test_pdhg_profile/profile_pdhg_TV_denoising.py b/Wrappers/Python/wip/test_pdhg_profile/profile_pdhg_TV_denoising.py
deleted file mode 100644
index e142d94..0000000
--- a/Wrappers/Python/wip/test_pdhg_profile/profile_pdhg_TV_denoising.py
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 22 14:53:03 2019
-
-@author: evangelos
-"""
-
-from ccpi.framework import ImageData, ImageGeometry, BlockDataContainer
-
-import numpy as np
-import matplotlib.pyplot as plt
-
-from ccpi.optimisation.algorithms import PDHG, PDHG_old
-
-from ccpi.optimisation.operators import BlockOperator, Identity, Gradient
-from ccpi.optimisation.functions import ZeroFunction, L2NormSquared, \
- MixedL21Norm, FunctionOperatorComposition, BlockFunction, ScaledFunction
-
-from skimage.util import random_noise
-
-from timeit import default_timer as timer
-def dt(steps):
- return steps[-1] - steps[-2]
-
-#%%
-
-# Create phantom for TV denoising
-
-N = 500
-
-data = np.zeros((N,N))
-data[round(N/4):round(3*N/4),round(N/4):round(3*N/4)] = 0.5
-data[round(N/8):round(7*N/8),round(3*N/8):round(5*N/8)] = 1
-
-ig = ImageGeometry(voxel_num_x = N, voxel_num_y = N)
-ag = ig
-
-# Create noisy data. Add Gaussian noise
-n1 = random_noise(data, mode = 'gaussian', mean=0, var = 0.05, seed=10)
-noisy_data = ImageData(n1)
-
-plt.imshow(noisy_data.as_array())
-plt.show()
-
-#%%
-
-# Regularisation Parameter
-alpha = 2
-
-#method = input("Enter structure of PDHG (0=Composite or 1=NotComposite): ")
-method = '0'
-
-if method == '0':
-
- # Create operators
- op1 = Gradient(ig)
- op2 = Identity(ig, ag)
-
- # Form Composite Operator
- operator = BlockOperator(op1, op2, shape=(2,1) )
-
- #### Create functions
-
- f1 = alpha * MixedL21Norm()
- f2 = 0.5 * L2NormSquared(b = noisy_data)
- f = BlockFunction(f1, f2)
-
- g = ZeroFunction()
-
-else:
-
- ###########################################################################
- # No Composite #
- ###########################################################################
- operator = Gradient(ig)
- f = alpha * FunctionOperatorComposition(operator, MixedL21Norm())
- g = L2NormSquared(b = noisy_data)
-
- ###########################################################################
-#%%
-
-# Compute operator Norm
-normK = operator.norm()
-
-# Primal & dual stepsizes
-sigma = 1
-tau = 1/(sigma*normK**2)
-
-opt = {'niter':2000}
-opt1 = {'niter':2000, 'memopt': True}
-
-t1 = timer()
-res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-t2 = timer()
-
-
-t3 = timer()
-res1, time1, primal1, dual1, pdgap1 = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt1)
-t4 = timer()
-#
-print ("No memopt in {}s, memopt in {}s ".format(t2-t1, t4 -t3))
-
-#
-#%%
-#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-#pdhg.max_iteration = 2000
-#pdhg.update_objective_interval = 100
-##
-#pdhgo = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
-#pdhgo.max_iteration = 2000
-#pdhgo.update_objective_interval = 100
-##
-#steps = [timer()]
-#pdhgo.run(2000)
-#steps.append(timer())
-#t1 = dt(steps)
-##
-#pdhg.run(2000)
-#steps.append(timer())
-#t2 = dt(steps)
-#
-#print ("Time difference {}s {}s {}s Speedup {:.2f}".format(t1,t2,t2-t1, t2/t1))
-#res = pdhg.get_output()
-#res1 = pdhgo.get_output()
-
-#%%
-#plt.figure(figsize=(15,15))
-#plt.subplot(3,1,1)
-#plt.imshow(res.as_array())
-#plt.title('no memopt')
-#plt.colorbar()
-#plt.subplot(3,1,2)
-#plt.imshow(res1.as_array())
-#plt.title('memopt')
-#plt.colorbar()
-#plt.subplot(3,1,3)
-#plt.imshow((res1 - res).abs().as_array())
-#plt.title('diff')
-#plt.colorbar()
-#plt.show()
-
-
-#plt.figure(figsize=(15,15))
-#plt.subplot(3,1,1)
-#plt.imshow(pdhg.get_output().as_array())
-#plt.title('no memopt class')
-#plt.colorbar()
-#plt.subplot(3,1,2)
-#plt.imshow(res.as_array())
-#plt.title('no memopt')
-#plt.colorbar()
-#plt.subplot(3,1,3)
-#plt.imshow((pdhg.get_output() - res).abs().as_array())
-#plt.title('diff')
-#plt.colorbar()
-#plt.show()
-#
-#
-#
-#plt.figure(figsize=(15,15))
-#plt.subplot(3,1,1)
-#plt.imshow(pdhgo.get_output().as_array())
-#plt.title('memopt class')
-#plt.colorbar()
-#plt.subplot(3,1,2)
-#plt.imshow(res1.as_array())
-#plt.title('no memopt')
-#plt.colorbar()
-#plt.subplot(3,1,3)
-#plt.imshow((pdhgo.get_output() - res1).abs().as_array())
-#plt.title('diff')
-#plt.colorbar()
-#plt.show()
-
-
-
-
-
-# print ("Time difference {}s {}s {}s Speedup {:.2f}".format(t1,t2,t2-t1, t2/t1))
-# res = pdhg.get_output()
-# res1 = pdhgo.get_output()
-#
-# diff = (res-res1)
-# print ("diff norm {} max {}".format(diff.norm(), diff.abs().as_array().max()))
-# print ("Sum ( abs(diff) ) {}".format(diff.abs().sum()))
-#
-#
-# plt.figure(figsize=(5,5))
-# plt.subplot(1,3,1)
-# plt.imshow(res.as_array())
-# plt.colorbar()
-# #plt.show()
-#
-# #plt.figure(figsize=(5,5))
-# plt.subplot(1,3,2)
-# plt.imshow(res1.as_array())
-# plt.colorbar()
-
-#plt.show()
-
-
-
-#=======
-## opt = {'niter':2000, 'memopt': True}
-#
-## res, time, primal, dual, pdgap = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-#
-#>>>>>>> origin/pdhg_fix
-#
-#
-## opt = {'niter':2000, 'memopt': False}
-## res1, time1, primal1, dual1, pdgap1 = PDHG_old(f, g, operator, tau = tau, sigma = sigma, opt = opt)
-#
-## plt.figure(figsize=(5,5))
-## plt.subplot(1,3,1)
-## plt.imshow(res.as_array())
-## plt.title('memopt')
-## plt.colorbar()
-## plt.subplot(1,3,2)
-## plt.imshow(res1.as_array())
-## plt.title('no memopt')
-## plt.colorbar()
-## plt.subplot(1,3,3)
-## plt.imshow((res1 - res).abs().as_array())
-## plt.title('diff')
-## plt.colorbar()
-## plt.show()
-#pdhg = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma)
-#pdhg.max_iteration = 2000
-#pdhg.update_objective_interval = 100
-#
-#
-#pdhgo = PDHG(f=f,g=g,operator=operator, tau=tau, sigma=sigma, memopt=True)
-#pdhgo.max_iteration = 2000
-#pdhgo.update_objective_interval = 100
-#
-#steps = [timer()]
-#pdhgo.run(200)
-#steps.append(timer())
-#t1 = dt(steps)
-#
-#pdhg.run(200)
-#steps.append(timer())
-#t2 = dt(steps)
-#
-#print ("Time difference {} {} {}".format(t1,t2,t2-t1))
-#sol = pdhg.get_output().as_array()
-##sol = result.as_array()
-##
-#fig = plt.figure()
-#plt.subplot(1,3,1)
-#plt.imshow(noisy_data.as_array())
-#plt.colorbar()
-#plt.subplot(1,3,2)
-#plt.imshow(sol)
-#plt.colorbar()
-#plt.subplot(1,3,3)
-#plt.imshow(pdhgo.get_output().as_array())
-#plt.colorbar()
-#
-#plt.show()
-###
-##
-####
-##plt.plot(np.linspace(0,N,N), data[int(N/2),:], label = 'GTruth')
-##plt.plot(np.linspace(0,N,N), sol[int(N/2),:], label = 'Recon')
-##plt.legend()
-##plt.show()
-#
-#
-##%%
-##
diff --git a/Wrappers/Python/wip/test_profile.py b/Wrappers/Python/wip/test_profile.py
deleted file mode 100644
index f14c0c3..0000000
--- a/Wrappers/Python/wip/test_profile.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Created on Mon Apr 8 13:57:46 2019
-
-@author: evangelos
-"""
-
-# profile direct, adjoint, gradient
-
-from ccpi.framework import ImageGeometry
-from ccpi.optimisation.operators import Gradient, BlockOperator, Identity
-from ccpi.optimisation.functions import MixedL21Norm, L2NormSquared, BlockFunction
-import numpy
-
-N, M, K = 2, 3, 2
-
-ig = ImageGeometry(N, M)
-b = ig.allocate('random_int')
-
-G = Gradient(ig)
-Id = Identity(ig)
-
-#operator = BlockOperator(G, Id)
-operator = G
-
-f1 = MixedL21Norm()
-f2 = L2NormSquared(b = b)
-
-f = BlockFunction( f1, f2)
-
-
-x_old = operator.domain_geometry().allocate()
-y_old = operator.range_geometry().allocate('random_int')
-
-
-xbar = operator.domain_geometry().allocate('random_int')
-
-x_tmp = x_old.copy()
-x = x_old.copy()
-
-y_tmp = operator.range_geometry().allocate()
-y = y_old.copy()
-
-y1 = y.copy()
-
-sigma = 20
-
-for i in range(100):
-
- operator.direct(xbar, out = y_tmp)
- y_tmp *= sigma
- y_tmp += y_old
-
-
- y_tmp1 = sigma * operator.direct(xbar) + y_old
-
- print(i)
- print(" y_old :", y_old[0].as_array(), "\n")
- print(" y_tmp[0] :", y_tmp[0].as_array(),"\n")
- print(" y_tmp1[0] :", y_tmp1[0].as_array())
-
-
- numpy.testing.assert_array_equal(y_tmp[0].as_array(), \
- y_tmp1[0].as_array())
-
- numpy.testing.assert_array_equal(y_tmp[1].as_array(), \
- y_tmp1[1].as_array())
-
-
- y1 = f.proximal_conjugate(y_tmp1, sigma)
- f.proximal_conjugate(y_tmp, sigma, y)
-
-
-
-
-
-
-
-
-
-
-
- \ No newline at end of file