From 325ea464526307d54d9fff2fdf221fe6e10c3c8c Mon Sep 17 00:00:00 2001
From: Jan Caron <j.caron@fz-juelich.de>
Date: Tue, 19 Aug 2014 08:42:43 +0200
Subject: [PATCH] Implementation of the Regularisator class regularisator: new
 module to represent different regularisation strategies costfunction: changed
 to use the new Regularisator classes, changed arguments! dataset: fixed bug
 that always displayed the own phase maps instead of provided reconstruction:
 changed to use the new Regularisator classes, changed arguments!
 scripts/rueffner_file: tried to avoid Memory Error (no success)
 scripts/zi_an: added holographic contour plots with overlayd magnetization
 scripts/reconstruction_sparse_cg_test: now uses Regularisator classes and
 plots                                        phase differences (original -
 reconstr.)

---
 pyramid/costfunction.py                       | 32 +++++----
 pyramid/dataset.py                            |  7 +-
 pyramid/forwardmodel.py                       |  4 +-
 pyramid/phasemap.py                           |  6 +-
 pyramid/reconstruction.py                     |  7 +-
 pyramid/regularisator.py                      | 71 +++++++++++++++++++
 scripts/collaborations/rueffner_file.py       | 12 +++-
 scripts/collaborations/zi_an.py               | 13 ++++
 .../reconstruction_sparse_cg_test.py          | 38 +++++-----
 scripts/test methods/compare_pixel_fields.py  | 58 +++++++--------
 10 files changed, 171 insertions(+), 77 deletions(-)
 create mode 100644 pyramid/regularisator.py

diff --git a/pyramid/costfunction.py b/pyramid/costfunction.py
index 275d908..940df35 100644
--- a/pyramid/costfunction.py
+++ b/pyramid/costfunction.py
@@ -8,11 +8,13 @@ import numpy as np
 from scipy.sparse.linalg import LinearOperator
 from scipy.sparse import eye
 
+from pyramid.regularisator import ZeroOrderRegularisator
+
 import logging
 
 
 class Costfunction(object):
-
+    # TODO: Docstrings!
     '''Class for calculating the cost of a 3D magnetic distributions in relation to 2D phase maps.
 
     Represents a strategy for the calculation of the `cost` of a 3D magnetic distribution in
@@ -36,28 +38,32 @@ class Costfunction(object):
 
     LOG = logging.getLogger(__name__+'.Costfunction')
 
-    def __init__(self, y, fwd_model, lam=0):
+    def __init__(self, y, fwd_model, Se_inv=None, regularisator=None):
         self.LOG.debug('Calling __init__')
         self.y = y
         self.fwd_model = fwd_model
-        self.lam = lam
-        self.Se_inv = eye(len(y))
+        if Se_inv is None:
+            Se_inv = eye(len(y))
+        self.Se_inv = Se_inv
+        if regularisator is None:
+            regularisator = ZeroOrderRegularisator(lam=1E-4, size=3*fwd_model.size_3d)
+        self.regularisator = regularisator
         self.LOG.debug('Created '+str(self))
 
     def __call__(self, x):
         self.LOG.debug('Calling __call__')
-        y = self.y
-        F = self.fwd_model
-        Se_inv = self.Se_inv
-        return (F(x)-y).dot(Se_inv.dot(F(x)-y)) + lam * x.dot(Sa_inv.dot(x))  # TODO: implement
+        return ((self.fwd_model(x)-self.y).dot(self.Se_inv.dot(self.fwd_model(x)-self.y))
+                + self.regularisator(x))
 
     def __repr__(self):
         self.LOG.debug('Calling __repr__')
-        return '%s(fwd_model=%r, lam=%r)' % (self.__class__, self.fwd_model, self.lam)
+        return '%s(fwd_model=%r, Se_inv=%r, regularisator=%r)' % \
+            (self.__class__, self.fwd_model, self.Se_inv, self.regularisator)
 
     def __str__(self):
         self.LOG.debug('Calling __str__')
-        return 'Costfunction(fwd_model=%s, lam=%s)' % (self.fwd_model, self.lam)
+        return 'Costfunction(fwd_model=%s, Se_inv=%s, regularisator=%s)' % \
+            (self.fwd_model, self.Se_inv, self.regularisator)
 
     def jac(self, x):
         '''Calculate the derivative of the costfunction for a given magnetization distribution.
@@ -99,10 +105,8 @@ class Costfunction(object):
 
         '''
         self.LOG.debug('Calling hess_dot')
-        F = self.fwd_model
-        Se_inv = self.Se_inv
-        lam = self.lam
-        return F.jac_T_dot(x, Se_inv.dot(F.jac_dot(x, vector))) + lam*vector
+        return (self.fwd_model.jac_T_dot(x, self.Se_inv.dot(self.fwd_model.jac_dot(x, vector)))
+                + self.regularisator.jac_dot(vector))
 
 
 class CFAdapterScipyCG(LinearOperator):
diff --git a/pyramid/dataset.py b/pyramid/dataset.py
index f51ddfc..5496239 100644
--- a/pyramid/dataset.py
+++ b/pyramid/dataset.py
@@ -103,7 +103,8 @@ class DataSet(object):
         assert projector.dim_uv == self.dim_uv, 'Projector dimensions must match!'
         self.data.append((phase_map, projector))
 
-    def display_phase(self, phase_maps=None, title='Phase Map', cmap='RdBu', limit=None, norm=None):
+    def display_phase(self, phase_maps=None, title='Phase Map',
+                      cmap='RdBu', limit=None, norm=None):
         '''Display all phasemaps saved in the :class:`~.DataSet` as a colormesh.
 
         Parameters
@@ -134,7 +135,7 @@ class DataSet(object):
             phase_maps = self.phase_maps
         [phase_map.display_phase('{} ({})'.format(title, self.projectors[i].get_info()),
                                  cmap, limit, norm)
-            for (i, phase_map) in enumerate(self.phase_maps)]
+            for (i, phase_map) in enumerate(phase_maps)]
         plt.show()
 
     def display_combined(self, phase_maps=None, title='Combined Plot', cmap='RdBu', limit=None,
@@ -179,7 +180,7 @@ class DataSet(object):
             phase_maps = self.phase_maps
         [phase_map.display_combined('{} ({})'.format(title, self.projectors[i].get_info()),
                                     cmap, limit, norm, density, interpolation, grad_encode)
-            for (i, phase_map) in enumerate(self.phase_maps)]
+            for (i, phase_map) in enumerate(phase_maps)]
         plt.show()
 
     def create_phase_maps(self, mag_data):
diff --git a/pyramid/forwardmodel.py b/pyramid/forwardmodel.py
index d820f72..eeb4868 100644
--- a/pyramid/forwardmodel.py
+++ b/pyramid/forwardmodel.py
@@ -49,9 +49,9 @@ class ForwardModel(object):
         self.a = kernel.a
         self.projectors = projectors
         self.dim = self.projectors[0].dim
-        self.dim_uv = kernel.dim_uv
-        self.size_2d = self.projectors[0].size_2d
         self.size_3d = self.projectors[0].size_3d
+        self.dim_uv = kernel.dim_uv
+        self.size_2d = kernel.size
         self.LOG.debug('Creating '+str(self))
 
     def __call__(self, x):
diff --git a/pyramid/phasemap.py b/pyramid/phasemap.py
index a2f6f1a..061ade2 100644
--- a/pyramid/phasemap.py
+++ b/pyramid/phasemap.py
@@ -398,16 +398,16 @@ class PhaseMap(object):
         # Return plotting axis:
         return axis
 
-    def display_holo(self, density=1, title='Holographic Contour Map',
+    def display_holo(self, title='Holographic Contour Map', density=1,
                      axis=None, grad_encode='bright', interpolation='none', show=True):
         '''Display the color coded holography image.
 
         Parameters
         ----------
-        density : float, optional
-            The gain factor for determining the number of contour lines. The default is 1.
         title : string, optional
             The title of the plot. The default is 'Holographic Contour Map'.
+        density : float, optional
+            The gain factor for determining the number of contour lines. The default is 1.
         axis : :class:`~matplotlib.axes.AxesSubplot`, optional
             Axis on which the graph is plotted. Creates a new figure if none is specified.
         grad_encode: {'bright', 'dark', 'color', 'none'}, optional
diff --git a/pyramid/reconstruction.py b/pyramid/reconstruction.py
index e251d28..22fd35b 100644
--- a/pyramid/reconstruction.py
+++ b/pyramid/reconstruction.py
@@ -83,7 +83,8 @@ class PrintIterator(object):
         return self.iteration
 
 
-def optimize_sparse_cg(data, verbosity=0):
+def optimize_sparse_cg(data, Se_inv=None, regularisator=None, verbosity=0):
+    # TODO: Docstring!
     '''Reconstruct a three-dimensional magnetic distribution from given phase maps via the
     conjugate gradient optimizaion method :func:`~.scipy.sparse.linalg.cg`.
 
@@ -109,7 +110,7 @@ def optimize_sparse_cg(data, verbosity=0):
     y = data.phase_vec
     kernel = Kernel(data.a, data.dim_uv, data.b_0)
     fwd_model = ForwardModel(data.projectors, kernel)
-    cost = Costfunction(y, fwd_model, lam=10**-10)
+    cost = Costfunction(y, fwd_model, Se_inv, regularisator)
     # Optimize:
     A = CFAdapterScipyCG(cost)
     b = fwd_model.jac_T_dot(None, y)
@@ -225,8 +226,6 @@ def optimize_simple_leastsq(phase_map, mask, b_0=1, lam=1E-4, order=0):
         return np.concatenate([term1, term2])
 
     J_DICT = [J_0, J_1]  # list of cost-functions with different regularisations
-    print J_DICT
-    print J_DICT[order]
     # Reconstruct the magnetization components:
     x_rec, _ = leastsq(J_DICT[order], np.zeros(3*count))
     mag_data_rec.set_vector(mask, x_rec)
diff --git a/pyramid/regularisator.py b/pyramid/regularisator.py
new file mode 100644
index 0000000..b3621b0
--- /dev/null
+++ b/pyramid/regularisator.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Aug 18 09:24:58 2014
+
+@author: Jan
+"""  # TODO: Docstring!
+
+
+import abc
+
+import numpy as np
+
+from scipy.sparse import eye
+
+import logging
+
+
+# TODO: Fragen für Jörn: Macht es Sinn, x_a standardmäßig auf den Nullvektor zu setzen? Ansonsten
+#       besser im jeweiligen Konstruktor setzen, nicht im abstrakten!
+#       Wie kommt man genau an die Ableitungen (Normen sind nicht unproblematisch)?
+#       Wie implementiert man am besten verschiedene Normen?
+
+
+class Regularisator(object):
+    # TODO: Docstring!
+
+    __metaclass__ = abc.ABCMeta
+    LOG = logging.getLogger(__name__+'.Regularisator')
+
+    @abc.abstractmethod
+    def __init__(self, Sa_inv, x_a=None):
+        self.LOG.debug('Calling __init__')
+        self.Sa_inv = Sa_inv
+        if x_a is None:
+            x_a = np.zeros(np.shape(Sa_inv)[1])
+        self.x_a = x_a
+        self.LOG.debug('Created '+str(self))
+
+    def __call__(self, x, x_a=None):
+        self.LOG.debug('Calling __call__')
+        return (x-self.x_a).dot(self.Sa_inv.dot(x-self.x_a))
+
+    def __repr__(self):
+        self.LOG.debug('Calling __repr__')
+        return '%s(Sa_inv=%r, x_a=%r)' % (self.__class__, self.Sa_inv, self.x_a)
+
+    def __str__(self):
+        self.LOG.debug('Calling __str__')
+        return 'Regularisator(Sa_inv=%s, x_a=%s)' % (self.Sa_inv, self.x_a)
+
+    def jac_dot(self, vector):
+        # TODO: Docstring!
+        return self.Sa_inv.dot(vector-self.x_a)
+
+
+class ZeroOrderRegularisator(Regularisator):
+    # TODO: Docstring!
+
+    def __init__(self, lam, size, x_a=None):
+        Sa_inv = lam * eye(size)
+        super(ZeroOrderRegularisator, self).__init__(Sa_inv, x_a)
+        self.LOG.debug('Created '+str(self))
+
+
+class FirstOrderRegularisator(Regularisator):
+    # TODO: Docstring!
+
+    def __init__(self, lam, size, x_a=None):
+        Sa_inv = lam * eye(size)
+        super(FirstOrderRegularisator, self).__init__(Sa_inv, x_a)
+        self.LOG.debug('Created '+str(self))
diff --git a/scripts/collaborations/rueffner_file.py b/scripts/collaborations/rueffner_file.py
index 6b37fea..ef0c2ac 100644
--- a/scripts/collaborations/rueffner_file.py
+++ b/scripts/collaborations/rueffner_file.py
@@ -65,8 +65,9 @@ xs = np.arange(-dim[2]/2, dim[2]/2)
 ys = np.arange(-dim[1]/2, dim[1]/2)
 zs = np.arange(-dim[0]/2, dim[0]/2)
 xx, yy = np.meshgrid(xs, ys)
-# Interpolation and phase calculation for all timesteps:
-for t in np.arange(865, 1001, 5):
+
+
+def calculate(t):  # TODO: Somehow avoid memory error :-(...
     print 't =', t
     vectors = h5file.root.data.fields.m.read(field='m_CoFeb')[t, ...]
     data = np.hstack((points, vectors))
@@ -92,5 +93,12 @@ for t in np.arange(865, 1001, 5):
     plt.savefig(PATH+'rueffner/phase_map_z_t_'+str(t)+'.png')
     phase_map_x.display_combined(density=dens_x, interpolation='bilinear', limit=lim_x)
     plt.savefig(PATH+'rueffner/phase_map_x_t_'+str(t)+'.png')
+    vectors, data, magnitude, z_slice, weights, grid_x, grid_y, grid_z, grid, mag_data, \
+        phase_map_z, phase_map_x = [None] * 12
     # Close all plots to avoid clutter:
     plt.close('all')
+
+
+# Interpolation and phase calculation for all timesteps:
+for t in np.arange(0, 1001, 5):
+    calculate(t)
diff --git a/scripts/collaborations/zi_an.py b/scripts/collaborations/zi_an.py
index e03e2d3..85df772 100644
--- a/scripts/collaborations/zi_an.py
+++ b/scripts/collaborations/zi_an.py
@@ -95,3 +95,16 @@ plt.savefig(PATH+'phase_map_4part_diff.png')
 # Get the average difference from the experimental results:
 print 'Average difference (2 cubes):', np.average(phase_diff_2.phase)
 print 'Average difference (4 cubes):', np.average(phase_diff_4.phase)
+# Plot holographic contour maps with overlayed magnetic distributions:
+axis = phase_map_rec_2.display_holo('Magnetization Overlay', density=density, interpolation=inter)
+(mag_data_rec_2*(1/mag_data_rec_2.magnitude.max())).quiver_plot(axis=axis)
+axis = plt.gca()
+axis.set_xlim(20, 45)
+axis.set_ylim(20, 45)
+plt.savefig(PATH+'phase_map_2part_holo.png')
+axis = phase_map_rec_4.display_holo('Magnetization Overlay', density=density, interpolation=inter)
+(mag_data_rec_4*(1/mag_data_rec_4.magnitude.max())).quiver_plot(axis=axis)
+axis = plt.gca()
+axis.set_xlim(20, 45)
+axis.set_ylim(20, 45)
+plt.savefig(PATH+'phase_map_4part_holo.png')
diff --git a/scripts/reconstruction/reconstruction_sparse_cg_test.py b/scripts/reconstruction/reconstruction_sparse_cg_test.py
index c5a6fa1..a4bb6a9 100644
--- a/scripts/reconstruction/reconstruction_sparse_cg_test.py
+++ b/scripts/reconstruction/reconstruction_sparse_cg_test.py
@@ -15,11 +15,8 @@ from pyramid.phasemap import PhaseMap
 from pyramid.projector import YTiltProjector, XTiltProjector
 from pyramid.phasemapper import PMConvolve
 from pyramid.dataset import DataSet
+from pyramid.regularisator import ZeroOrderRegularisator
 import pyramid.magcreator as mc
-
-from pyramid.kernel import Kernel
-from pyramid.forwardmodel import ForwardModel
-from pyramid.costfunction import Costfunction
 import pyramid.reconstruction as rc
 
 import logging
@@ -85,24 +82,25 @@ else:
 print('--Setting up data collection')
 
 dim_uv = dim[1:3]
-
-lam = 10**-10
-
 data = DataSet(a, dim_uv, b_0)
 [data.append((phase_maps[i], projectors[i])) for i in range(len(projectors))]
-y = data.phase_vec
-kern = Kernel(data.a, data.dim_uv, data.b_0)
-F = ForwardModel(data.projectors, kern)
-C = Costfunction(y, F, lam)
 
-data.display()
+###################################################################################################
+print('--Test simple solver')
+
+lam = 1E-10
+reg = ZeroOrderRegularisator(lam, 3*np.prod(dim))
+start = clock()
+mag_data_opt = rc.optimize_sparse_cg(data, regularisator=reg, verbosity=1)
+print 'Time:', str(clock()-start)
+mag_data_opt.quiver_plot3d()
+(mag_data_opt - mag_data).quiver_plot3d()
 
 ###################################################################################################
-#print('--Test simple solver')
-#
-#start = clock()
-#mag_data_opt = rc.optimize_sparse_cg(data, verbosity=1)
-#print 'Time:', str(clock()-start)
-#mag_data_opt.quiver_plot3d()
-#(mag_data_opt - mag_data).quiver_plot3d()
-##data.display(data.create_phase_maps(mag_data_opt))
+print('--Plot stuff')
+
+phase_maps_opt = data.create_phase_maps(mag_data_opt)
+#data.display_phase()
+#data.display_phase(phase_maps_opt)
+phase_diffs = [(phase_maps[i]-phase_maps_opt[i]) for i in range(len(data.data))]
+data.display_phase(phase_diffs)
diff --git a/scripts/test methods/compare_pixel_fields.py b/scripts/test methods/compare_pixel_fields.py
index bda9f0c..c26bb99 100644
--- a/scripts/test methods/compare_pixel_fields.py	
+++ b/scripts/test methods/compare_pixel_fields.py	
@@ -75,32 +75,32 @@ print 'Fourier Kernel, direct and indirect method are identical:', \
       np.all(phase_map_fft_kernel.phase - phase_map_fft.phase) == 0
 (phase_map_disc-phase_map_fft).display_phase('Phase difference of one Pixel (Disc - Fourier)')
 
-## Cross section plots of real space kernels:
-#fig = plt.figure()
-#axis = fig.add_subplot(1, 1, 1)
-#x = np.linspace(-dim[1]/a/2, dim[1]/a/2-1, dim[1])
-#y_ft = phase_map_fft.phase[:, dim[1]/2]
-#y_re = phase_map_disc.phase[:, dim[1]/2]
-#axis.axhline(0, color='k')
-#axis.plot(x, y_re, label='Real space method')
-#axis.plot(x, y_ft, label='Fourier method')
-#axis.grid()
-#axis.legend()
-#axis.set_title('Real Space Kernel')
-#axis.set_xlim(-dim[1]/2, dim[1]/2-1)
-#axis.xaxis.set_major_locator(IndexLocator(base=dim[1]/8, offset=0))
-#
-## Cross section plots of Fourier space kernels:
-#fig = plt.figure()
-#axis = fig.add_subplot(1, 1, 1)
-#x = range(dim[1])
-#k_re = np.abs(np.fft.ifftshift(np.fft.rfft2(phase_map_disc.phase), axes=0))[:, 0]**2
-#k_ft = np.abs(np.fft.ifftshift(np.fft.rfft2(phase_map_fft.phase), axes=0))[:, 0]**2
-#axis.axhline(0, color='k')
-#axis.plot(x, k_re, label='Real space method')
-#axis.plot(x, k_ft, label='Fourier method')
-#axis.grid()
-#axis.legend()
-#axis.set_title('Fourier Space Kernel')
-#axis.set_xlim(0, dim[1]-1)
-#axis.xaxis.set_major_locator(IndexLocator(base=dim[1]/8, offset=0))
+# Cross section plots of real space kernels:
+fig = plt.figure()
+axis = fig.add_subplot(1, 1, 1)
+x = np.linspace(-dim[1]/a/2, dim[1]/a/2-1, dim[1])
+y_ft = phase_map_fft.phase[:, dim[1]/2]
+y_re = phase_map_disc.phase[:, dim[1]/2]
+axis.axhline(0, color='k')
+axis.plot(x, y_re, label='Real space method')
+axis.plot(x, y_ft, label='Fourier method')
+axis.grid()
+axis.legend()
+axis.set_title('Real Space Kernel')
+axis.set_xlim(-dim[1]/2, dim[1]/2-1)
+axis.xaxis.set_major_locator(IndexLocator(base=dim[1]/8, offset=0))
+
+# Cross section plots of Fourier space kernels:
+fig = plt.figure()
+axis = fig.add_subplot(1, 1, 1)
+x = range(dim[1])
+k_re = np.abs(np.fft.ifftshift(np.fft.rfft2(phase_map_disc.phase), axes=0))[:, 0]**2
+k_ft = np.abs(np.fft.ifftshift(np.fft.rfft2(phase_map_fft.phase), axes=0))[:, 0]**2
+axis.axhline(0, color='k')
+axis.plot(x, k_re, label='Real space method')
+axis.plot(x, k_ft, label='Fourier method')
+axis.grid()
+axis.legend()
+axis.set_title('Fourier Space Kernel')
+axis.set_xlim(0, dim[1]-1)
+axis.xaxis.set_major_locator(IndexLocator(base=dim[1]/8, offset=0))
-- 
GitLab