From dc958934237623296deca7ff04f25451ced25055 Mon Sep 17 00:00:00 2001 From: Jakob Jorgensen Date: Mon, 9 Apr 2018 13:43:06 +0100 Subject: First opti doc --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4a14723..7d1da31 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,52 @@ In `ccpi.framework` we define a number of common classes normally used in tomogr ### `ccpi.optimisation` - This package allows writing of optimisation algorithms. The main actors here are: + This package allows rapid prototyping of optimisation-based reconstruction problems, + i.e. defining and solving different optimization problems to enforce different properties + on the reconstructed image. - * `Function` - * `Operator` + Firstly, it provides an object-oriented framework for defining mathematical operators and functions + as well a collection of useful example operators and functions. Both smooth and + non-smooth functions can be used. + + Further, it provides a number of high-level generic + implementations of optimisation algorithms to solve genericlly formulated + optimisation problems constructed from operator and function objects. + + The fundamental components are: + + * `Operator`: A class specifying a (currently linear) operator + * `Function`: A class specifying mathematical functions such as a least squares data fidelity. + * `Algorithm`: Implementation of an optimisation algorithm to solve a particular generic optimisation problem. These are currently python functions by may be changed to operators in another release. + + #### `Operator` + + The two most important methods are `direct` and `adjoint` methods that describe the result of + applying the operator, and its adjoint respectively, onto a compatible `DataContainer` input. + The output is another `DataContainer` object or subclass hereof. An important + special case is to represent the tomographic forward and backprojection operations. + + #### `Function` + + A `function` represents a mathematical function of one or more inputs is intended + to accept `DataContainer`s as input as well as any additional parameters. + Its methods reflect the properties of the function, for example, + if the function represented is differentiable + the `function` should contain a method `grad` which should return the gradient of the function evaluated at + an input point. If the function is not differentiable but allows a simple proximal operator, the method + `prox` should return the proxial operator evaluated at an input point. It is also possible + to evaluate the function value using the method `fun`. + + #### `Algorithm` + + A number of generic algorithm implementations are provided including CGLS and FISTA. An algorithm + is designed for a particular generic optimisation problem accepts and number of `function`s and/or + `operator`s as input to define a specific instance of the generic optimisation problem to be solved. + + #### Examples + + Please see the demos for examples of defining and using operators, functions and algorithms + to specify and solve optimisation-based reconstruction problems. -- cgit v1.2.3 From 783e5842055bc6991a1963c649fd96d012c27c98 Mon Sep 17 00:00:00 2001 From: Jakob Jorgensen Date: Tue, 10 Apr 2018 07:30:43 +0100 Subject: Replaces .fun in funcs by .__call__ --- Wrappers/Python/ccpi/optimisation/algs.py | 4 ++-- Wrappers/Python/ccpi/optimisation/funcs.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Wrappers/Python/ccpi/optimisation/algs.py b/Wrappers/Python/ccpi/optimisation/algs.py index 5942055..6cae65a 100755 --- a/Wrappers/Python/ccpi/optimisation/algs.py +++ b/Wrappers/Python/ccpi/optimisation/algs.py @@ -84,7 +84,7 @@ def FISTA(x_init, f=None, g=None, opt=None): # time and criterion timing[it] = time.time() - time0 - criter[it] = f.fun(x) + g.fun(x); + criter[it] = f(x) + g(x); # stopping rule #if np.linalg.norm(x - x_old) < tol * np.linalg.norm(x_old) and it > 10: @@ -156,7 +156,7 @@ def FBPD(x_init, f=None, g=None, h=None, opt=None): # time and criterion timing[it] = time.time() - t - criter[it] = f.fun(x) + g.fun(x) + h.fun(h.op.direct(x)); + criter[it] = f(x) + g(x) + h(h.op.direct(x)); # stopping rule #if np.linalg.norm(x - x_old) < tol * np.linalg.norm(x_old) and it > 10: diff --git a/Wrappers/Python/ccpi/optimisation/funcs.py b/Wrappers/Python/ccpi/optimisation/funcs.py index 0dbe28f..da6a3de 100755 --- a/Wrappers/Python/ccpi/optimisation/funcs.py +++ b/Wrappers/Python/ccpi/optimisation/funcs.py @@ -24,7 +24,7 @@ import numpy class BaseFunction(object): def __init__(self): self.op = Identity() - def fun(self,x): return 0 + def __call__(self,x): return 0 def grad(self,x): return 0 def prox(self,x,tau): return x @@ -37,7 +37,7 @@ class Norm2(BaseFunction): self.gamma = gamma; self.direction = direction; - def fun(self, x): + def __call__(self, x): xx = numpy.sqrt(numpy.sum(numpy.square(x.as_array()), self.direction, keepdims=True)) @@ -93,7 +93,7 @@ class Norm2sq(BaseFunction): #return 2*self.c*self.A.adjoint( self.A.direct(x) - self.b ) return 2.0*self.c*self.A.adjoint( self.A.direct(x) - self.b ) - def fun(self,x): + def __call__(self,x): #return self.c* np.sum(np.square((self.A.direct(x) - self.b).ravel())) return self.c*( ( (self.A.direct(x)-self.b)**2).sum() ) @@ -105,7 +105,7 @@ class ZeroFun(BaseFunction): self.L = L super(ZeroFun, self).__init__() - def fun(self,x): + def __call__(self,x): return 0 def prox(self,x,tau): @@ -121,7 +121,7 @@ class Norm1(BaseFunction): self.L = 1 super(Norm1, self).__init__() - def fun(self,x): + def __call__(self,x): return self.gamma*(x.abs().sum()) def prox(self,x,tau): -- cgit v1.2.3 From a6743609c13e58362da1bed3d932a514cb3d8b5f Mon Sep 17 00:00:00 2001 From: Jakob Jorgensen Date: Tue, 10 Apr 2018 07:37:34 +0100 Subject: Updated README --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7d1da31..06d30fe 100644 --- a/README.md +++ b/README.md @@ -68,14 +68,16 @@ In `ccpi.framework` we define a number of common classes normally used in tomogr #### `Function` - A `function` represents a mathematical function of one or more inputs is intended + A `function` represents a mathematical function of one or more inputs and is intended to accept `DataContainer`s as input as well as any additional parameters. - Its methods reflect the properties of the function, for example, + Fixed parameters can be passed in during the creation of the `function` object. + The methods of the `function` reflect the properties of it, for example, if the function represented is differentiable the `function` should contain a method `grad` which should return the gradient of the function evaluated at an input point. If the function is not differentiable but allows a simple proximal operator, the method - `prox` should return the proxial operator evaluated at an input point. It is also possible - to evaluate the function value using the method `fun`. + `prox` should return the proxial operator evaluated at an input point. The function value + is evaluated by calling the function itself, e.g. `f(x)` for a `function` + `f` and input point `x`. #### `Algorithm` -- cgit v1.2.3 From ac789a915849b6814f1bc9587c24b1320b532950 Mon Sep 17 00:00:00 2001 From: Jakob Jorgensen Date: Wed, 11 Apr 2018 12:01:40 +0100 Subject: Replaced BaseFunction by Function --- Wrappers/Python/ccpi/optimisation/algs.py | 12 ++++++------ Wrappers/Python/ccpi/optimisation/funcs.py | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Wrappers/Python/ccpi/optimisation/algs.py b/Wrappers/Python/ccpi/optimisation/algs.py index 6cae65a..a45100c 100755 --- a/Wrappers/Python/ccpi/optimisation/algs.py +++ b/Wrappers/Python/ccpi/optimisation/algs.py @@ -20,7 +20,7 @@ import numpy import time -from ccpi.optimisation.funcs import BaseFunction +from ccpi.optimisation.funcs import Function def FISTA(x_init, f=None, g=None, opt=None): '''Fast Iterative Shrinkage-Thresholding Algorithm @@ -37,8 +37,8 @@ def FISTA(x_init, f=None, g=None, opt=None): opt: additional algorithm ''' # default inputs - if f is None: f = BaseFunction() - if g is None: g = BaseFunction() + if f is None: f = Function() + if g is None: g = Function() # algorithmic parameters if opt is None: @@ -108,9 +108,9 @@ def FBPD(x_init, f=None, g=None, h=None, opt=None): opt: additional algorithm ''' # default inputs - if f is None: f = BaseFunction() - if g is None: g = BaseFunction() - if h is None: h = BaseFunction() + if f is None: f = Function() + if g is None: g = Function() + if h is None: h = Function() # algorithmic parameters if opt is None: diff --git a/Wrappers/Python/ccpi/optimisation/funcs.py b/Wrappers/Python/ccpi/optimisation/funcs.py index da6a3de..d11d6c3 100755 --- a/Wrappers/Python/ccpi/optimisation/funcs.py +++ b/Wrappers/Python/ccpi/optimisation/funcs.py @@ -21,14 +21,14 @@ from ccpi.optimisation.ops import Identity, FiniteDiff2D import numpy -class BaseFunction(object): +class Function(object): def __init__(self): self.op = Identity() def __call__(self,x): return 0 def grad(self,x): return 0 def prox(self,x,tau): return x -class Norm2(BaseFunction): +class Norm2(Function): def __init__(self, gamma=1.0, @@ -63,7 +63,7 @@ class TV2D(Norm2): # Define a class for squared 2-norm -class Norm2sq(BaseFunction): +class Norm2sq(Function): ''' f(x) = c*||A*x-b||_2^2 @@ -98,7 +98,7 @@ class Norm2sq(BaseFunction): return self.c*( ( (self.A.direct(x)-self.b)**2).sum() ) -class ZeroFun(BaseFunction): +class ZeroFun(Function): def __init__(self,gamma=0,L=1): self.gamma = gamma @@ -113,7 +113,7 @@ class ZeroFun(BaseFunction): # A more interesting example, least squares plus 1-norm minimization. # Define class to represent 1-norm including prox function -class Norm1(BaseFunction): +class Norm1(Function): def __init__(self,gamma): # Do nothing -- cgit v1.2.3