From 48b5c879ec0b63685e06ff56b2ccc2317ae36a21 Mon Sep 17 00:00:00 2001
From: Jan Caron <j.caron@fz-juelich.de>
Date: Thu, 5 Dec 2013 18:54:24 +0100
Subject: [PATCH] Implementation of jacobi multiplication for the projection.
 (At the moment a bit slow, optimization with Cython seems necessary).

---
 pyramid/kernel.py                    |  24 ++-
 pyramid/numcore/phase_mag_real.pyx   |  46 ++---
 pyramid/phasemapper.py               |  16 --
 pyramid/projector.py                 | 288 +++++++++++++++++++--------
 pyramid/reconstructor.py             |   6 +
 scripts/get_jacobi_projection.py     | 136 ++++++++++++-
 scripts/reconstruction/get_jacobi.py |  55 +++--
 7 files changed, 413 insertions(+), 158 deletions(-)

diff --git a/pyramid/kernel.py b/pyramid/kernel.py
index 51bf156..89bdac3 100644
--- a/pyramid/kernel.py
+++ b/pyramid/kernel.py
@@ -16,6 +16,8 @@ information.
 
 import numpy as np
 
+import pyramid.numcore as nc
+
 
 PHI_0 = -2067.83    # magnetic flux in T*nm²
 
@@ -166,11 +168,11 @@ class Kernel:
             i = s % u_dim
             j = int(s/u_dim)
             u_min = (u_dim-1) - i
-            u_max = (2*u_dim-1) - i
+            u_max = (2*u_dim-1) - i  # = u_min + u_dim
             v_min = (v_dim-1) - j
-            v_max = (2*v_dim-1) - j
+            v_max = (2*v_dim-1) - j  # = v_min + v_dim
             result += vector[s]*self.u[v_min:v_max, u_min:u_max].reshape(-1)  # u
-            result += vector[s+size]*-self.v[v_min:v_max, u_min:u_max].reshape(-1)  # v        
+            result -= vector[s+size]*self.v[v_min:v_max, u_min:u_max].reshape(-1)  # v        
         return result
 
     def multiply_jacobi_T(self, vector):
@@ -204,3 +206,19 @@ class Kernel:
             result[s] = np.sum(vector*self.u[v_min:v_max, u_min:u_max].reshape(-1))  # u
             result[s+size] = np.sum(vector*-self.v[v_min:v_max, u_min:u_max].reshape(-1))  # v        
         return result
+
+    def multiply_jacobi_core(self, vector):
+        # TODO: Docstring!
+        v_dim, u_dim = self.dim
+        size = v_dim * u_dim
+        result = np.zeros(size)
+        nc.multiply_jacobi_core(v_dim, u_dim, self.v, self.u, vector, result)
+        return result
+
+    def multiply_jacobi_core2(self, vector):
+        # TODO: Docstring!
+        v_dim, u_dim = self.dim
+        size = v_dim * u_dim
+        result = np.zeros(size)
+        nc.multiply_jacobi_core2(v_dim, u_dim, self.v, self.u, vector, result)
+        return result
diff --git a/pyramid/numcore/phase_mag_real.pyx b/pyramid/numcore/phase_mag_real.pyx
index 2fa38de..4171ce5 100644
--- a/pyramid/numcore/phase_mag_real.pyx
+++ b/pyramid/numcore/phase_mag_real.pyx
@@ -5,7 +5,7 @@ Provides a helper function to speed up :func:`~pyramid.phasemapper.phase_mag_rea
 :mod:`~pyramid.phasemapper`, by using C-speed for the for-loops and by omitting boundary and
 wraparound checks.
 
-"""
+""" # TODO: Docstring!
 
 
 import numpy as np
@@ -104,7 +104,7 @@ def get_jacobi_core(
 
 @cython.boundscheck(False)
 @cython.wraparound(False)
-def get_jacobi_core(
+def get_jacobi_core2(
     unsigned int v_dim, unsigned int u_dim,
     double[:, :] v_phi, double[:, :] u_phi,
     double[:, :] jacobi):
@@ -128,22 +128,23 @@ def multiply_jacobi_core(
     double[:, :] v_phi, double[:, :] u_phi,
     double[:] vector,
     double[:] result):
-
-    cdef unsigned int s, i, j, u_min, u_max, v_min, v_max, ri, u, v, siz
+    '''DOCSTRING!'''
+    # TODO: Docstring!!! Iterate over magnetization and then kernel
+    cdef unsigned int s, i, j, u_min, u_max, v_min, v_max, ri, u, v, size
     cdef double v0, v1
-    siz = u_dim * v_dim
-
-    s = 0
+    size = u_dim * v_dim  # Number of pixels
+    s = 0  # Current pixel (numbered consecutively)
+    # Go over all pixels:
     for i in range(u_dim):
         for j in range(v_dim):
-            u_min = (u_dim - 1) - i
-            v_min = (v_dim - 1) - j
-
-            ri = 0
+            u_min = (u_dim - 1) - i  # u_max = u_min + u_dim
+            v_min = (v_dim - 1) - j  # v_max = v_min + v_dim
+            ri = 0  # Current result component (numbered consecutively)
+            # Go over the current kernel cutout [v_min:v_max, u_min:u_max]:
             for v in range(v_min, v_min + v_dim):
                 for u in range(u_min, u_min + u_dim):
                     result[ri] += vector[s] * u_phi[v, u]
-                    result[ri] -= vector[s + siz] * v_phi[v, u]
+                    result[ri] -= vector[s + size] * v_phi[v, u]
                     ri += 1
             s += 1
 
@@ -155,22 +156,21 @@ def multiply_jacobi_core2(
     double[:, :] v_phi, double[:, :] u_phi,
     double[:] vector,
     double[:] result):
-
-    cdef int s1, s2, i, j, u_min, u_max, v_min, v_max, ri, u, v, siz, j_min, j_max, i_min, i_max
-
-    siz = u_dim * v_dim
-
+    '''DOCSTRING!'''
+    # TODO: Docstring!!! Iterate over kernel and then magnetization
+    cdef int s1, s2, i, j, u_min, u_max, v_min, v_max, ri, u, v, size, j_min, j_max, i_min, i_max
+    size = u_dim * v_dim  # Number of pixels
+    # Go over complete kernel:
     for v in range(2 * v_dim - 1):
         for u in range(2 * u_dim - 1):
             i_min = max(0, (u_dim - 1) - u)
             i_max = min(u_dim, ((2 * u_dim) - 1) - u)
+            j_min = max(0, (v_dim - 1) - v)
+            j_max = min(v_dim, ((2 * v_dim) - 1) - v)
+            # Iterate over 
             for i in range(i_min, i_max):
-                s1 = i * u_dim
-                s2 = s1 + siz
-
-                j_min = max(0, (v_dim - 1) - v)
-                j_max = min(v_dim, ((2 * v_dim) - 1) - v)
-
+                s1 = i * u_dim  # u-column
+                s2 = s1 + size  # v-column
                 u_min = (u_dim - 1) - i
                 v_min = (v_dim - 1) - j_min
                 ri = (v - ((v_dim - 1) - j_min)) * u_dim + (u - u_min)
diff --git a/pyramid/phasemapper.py b/pyramid/phasemapper.py
index d74495e..d69f262 100644
--- a/pyramid/phasemapper.py
+++ b/pyramid/phasemapper.py
@@ -25,22 +25,6 @@ C = 2.998E8        # speed of light in m/s
 
 def phase_mag(a, projection, b_0=1, kernel=None):
     '''Calculate the magnetic phase from magnetization data.
-    def multiply_jacobi_core(self, vector):
-        v_dim, u_dim = self.dim
-        size = v_dim * u_dim
-        result = np.zeros(size)
-        nc.multiply_jacobi_core(
-            v_dim, u_dim, self.v, self.u, vector, result)
-        return result
-
-    def multiply_jacobi_core2(self, vector):
-        v_dim, u_dim = self.dim
-        size = v_dim * u_dim
-        result = np.zeros(size)
-        nc.multiply_jacobi_core2(
-            v_dim, u_dim, self.v, self.u, vector, result)
-        return result
-
 
     Parameters
     ----------
diff --git a/pyramid/projector.py b/pyramid/projector.py
index 9fa94d2..3c5fab3 100644
--- a/pyramid/projector.py
+++ b/pyramid/projector.py
@@ -182,7 +182,7 @@ class Projection:
 
     ''' # TODO: Docstring!
     
-    def __init__(self, u_mag, v_mag, thickness, weights):
+    def __init__(self, dim_proj, dim_rot, dim_perp, v_mag, u_mag, thickness, weights, tilt):
         '''Constructor for a :class:`~.Kernel` object for representing a kernel matrix.
 
         Parameters
@@ -197,12 +197,153 @@ class Projection:
             The elementary geometry of the single magnetized pixel.
 
         ''' # TODO: Docstring!
-        self.dim = np.shape(u_mag)
+        self.dim_proj = dim_proj  # dimension along the projection axis
+        self.dim_rot = dim_rot    # dimension along the rotation axis
+        self.dim_perp = dim_perp  # dimension along the axis perpendicular to proj. and rotation
 #        self.a = a
 #        self.b_0 = b_0
         self.u = u_mag
         self.v = v_mag
+        self.tilt = tilt
         self.weights = weights
+        self.weights_inv = {}
+        # TODO: Not necessary:
+        for key, value_list in weights.iteritems():
+            for new_key, value in value_list:
+                self.weights_inv.setdefault(new_key, []).append((key, value))
+
+    @classmethod
+    def single_tilt_projection(cls, mag_data, tilt=0):
+        '''
+        Project a magnetization distribution which is tilted around the centered y-axis.
+    
+        Parameters
+        ----------
+        mag_data : :class:`~pyramid.magdata.MagData`
+            A :class:`~pyramid.magdata.MagData` object storing the magnetization distribution,
+            which should be projected.
+        tilt : float, optional
+            The counter-clockwise tilt angle around the y-axis. Default is 1.
+    
+        Returns
+        -------
+        projection : tuple (N=3) of :class:`~numpy.ndarray` (N=2)
+            The in-plane projection of the magnetization as a tuple, storing the `u`- and `v`-component
+            of the magnetization and the thickness projection for the resulting 2D-grid. The latter
+            has to be multiplied with the grid spacing for a value in nm.
+    
+        '''
+        assert isinstance(mag_data, MagData), 'Parameter mag_data has to be a MagData object!'
+    
+        def get_position(p, m, b, size):
+            y, x = np.array(p)[:, 0]+0.5, np.array(p)[:, 1]+0.5
+            return (y-m*x-b)/np.sqrt(m**2+1) + size/2.
+    
+        def get_impact(pos, r, size):
+            return [x for x in np.arange(np.floor(pos-r), np.floor(pos+r)+1, dtype=int)
+                    if 0 <= x < size]
+    
+        def get_weight(delta, rho):  # use circles to represent the voxels
+            lo, up = delta-rho, delta+rho
+            # Upper boundary:
+            if up >= 1:
+                w_up = 0.5
+            else:
+                w_up = (up*np.sqrt(1-up**2) + np.arctan(up/np.sqrt(1-up**2))) / pi
+            # Lower boundary:
+            if lo <= -1:
+                w_lo = -0.5
+            else:
+                w_lo = (lo*np.sqrt(1-lo**2) + np.arctan(lo/np.sqrt(1-lo**2))) / pi
+            return w_up - w_lo
+    
+        # Set starting variables:
+        # length along projection (proj, z), rotation (rot, y) and perpendicular (perp, x) axis:
+        dim_proj, dim_rot, dim_perp = mag_data.dim
+        z_mag, y_mag, x_mag = mag_data.magnitude
+        mask = mag_data.get_mask()
+        projection = (np.zeros((dim_rot, dim_perp)),
+                      np.zeros((dim_rot, dim_perp)),
+                      np.zeros((dim_rot, dim_perp)))
+        # Creating coordinate list of all voxels:
+        voxels = list(itertools.product(range(dim_proj), range(dim_perp)))
+    
+        # Calculate positions along the projected pixel coordinate system:
+        center = (dim_proj/2., dim_perp/2.)
+        m = np.where(tilt<=pi, -1/np.tan(tilt+1E-30), 1/np.tan(tilt+1E-30))
+        b = center[0] - m * center[1]
+        positions = get_position(voxels, m, b, dim_perp)
+    
+        # Calculate weights:
+        r = 1/np.sqrt(np.pi)  # radius of the voxel circle
+        rho = 0.5 / r
+        weights = {}
+        for i, voxel in enumerate(voxels):
+            voxel_weights = []
+            impacts = get_impact(positions[i], r, dim_perp)
+            for impact in impacts:
+                distance = np.abs(impact+0.5 - positions[i])
+                delta = distance / r
+                voxel_weights.append((impact, get_weight(delta, rho)))
+            weights[voxel] = voxel_weights
+        
+        # Calculate projection with the calculated weights for the voxels (rotation around y-axis):
+        for i, voxel in enumerate(weights):
+            voxel_weights = weights[voxel]
+            for pixel, weight in voxel_weights:
+                # Component parallel to tilt axis (':' goes over all slices):
+                projection[0][:, pixel] += weight * y_mag[voxel[0], :, voxel[1]]
+                # Component perpendicular to tilt axis:
+                projection[1][:, pixel] += weight * (x_mag[voxel[0], :, voxel[1]]*np.cos(tilt)
+                                                   + z_mag[voxel[0], :, voxel[1]]*np.sin(tilt))
+                # Thickness profile:
+                projection[2][:, pixel] += weight * mask[voxel[0], :, voxel[1]]
+        
+        return Projection(dim_proj, dim_rot, dim_perp, projection[0], projection[1], projection[2],
+                          weights, tilt)
+
+    def get_weight_matrix(self):
+        dim_proj, dim_rot, dim_perp = self.dim_proj, self.dim_rot, self.dim_perp
+        weights = self.weights
+        size_2d = dim_rot * dim_perp
+        size_3d = dim_rot * dim_perp * dim_proj
+        weight_matrix = np.zeros((size_2d, size_3d))
+        for voxel in weights:
+            voxel_weights = weights[voxel]
+            for pixel, weight in voxel_weights:
+                for i in range(dim_rot):
+                    pixel_pos = i*dim_rot + pixel
+                    voxel_pos = voxel[0]*dim_proj*dim_rot + i*dim_rot + voxel[1]
+                    weight_matrix[pixel_pos, voxel_pos] = weight
+        return weight_matrix
+
+    def multiply_weight_matrix(self, vector):
+        dim_proj, dim_rot, dim_perp = self.dim_proj, self.dim_rot, self.dim_perp
+        weights = self.weights
+        size_2d = dim_rot * dim_perp
+        result = np.zeros(size_2d)
+        for voxel in weights:
+            voxel_weights = weights[voxel]
+            for pixel, weight in voxel_weights:
+                for i in range(dim_rot):
+                    pixel_pos = i*dim_rot + pixel
+                    voxel_pos = voxel[0]*dim_proj*dim_rot + i*dim_rot + voxel[1]
+                    result[pixel_pos] += weight * vector[voxel_pos]
+        return result
+
+    def multiply_weight_matrix_T(self, vector):
+        dim_proj, dim_rot, dim_perp = self.dim_proj, self.dim_rot, self.dim_perp
+        weights = self.weights
+        size_3d = dim_proj * dim_rot * dim_perp
+        result = np.zeros(size_3d)
+        for voxel in weights:
+            voxel_weights = weights[voxel]
+            for pixel, weight in voxel_weights:
+                for i in range(dim_rot):
+                    pixel_pos = i*dim_rot + pixel
+                    voxel_pos = voxel[0]*dim_proj*dim_rot + i*dim_rot + voxel[1]
+                    result[voxel_pos] += weight * vector[pixel_pos]
+        return result
 
     def get_jacobi(self):
         '''Calculate the Jacobi matrix for the phase calculation from a projected magnetization.
@@ -224,57 +365,28 @@ class Projection:
         Just use for small dimensions, Jacobi Matrix scales with order of ``N**4``.
 
         ''' # TODO: Docstring!
-        v_dim, u_dim = self.dim
-        size = v_dim * u_dim
-        jacobi = np.zeros((v_dim*u_dim, 2*v_dim*u_dim))  
-        weight_matrix = np.zeros(dim)  # Check if correct (probably not, x and z instead of x and y)
-        voxels = list(itertools.product(range(dim_proj), range(dim_perp)))
-        for i, voxel in enumerate(self.weights):
-            voxel_weights = self.weights[voxel]
-            for pixel, weight in voxel_weights:
-                # Component parallel to tilt axis (':' goes over all slices):
-                projection[0][:, pixel] += weight * y_mag[voxel[0], :, voxel[1]]
-                # Component perpendicular to tilt axis:
-                projection[1][:, pixel] += weight * (x_mag[voxel[0], :, voxel[1]]*np.cos(tilt)
-                                                   + z_mag[voxel[0], :, voxel[1]]*np.sin(tilt))
-                # Thickness profile:
-                projection[2][:, pixel] += weight * mask[voxel[0], :, voxel[1]]
-        
-        for i, vx in enumerate(self.weights):
-            vx_pos = vx[1]*3 + vx[0]
-            for px, weight in self.weights[voxel]:
-                px_pos = (px[2]*3 + px[1])*3 + px[0]
-                weight_matrix[vx_pos, px_pos] = weight
-            
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        
-        v_dim, u_dim = self.dim
-        jacobi = np.zeros((v_dim*u_dim, 2*v_dim*u_dim))  
-#       nc.get_jacobi_core(dim[0], dim[1], v_phi, u_phi, jacobi)
-#       return jacobi
-        for j in range(v_dim):
-            for i in range(u_dim):
-                u_column = i + u_dim*j
-                v_column = i + u_dim*j + u_dim*v_dim
-                u_min = (u_dim-1) - i
-                u_max = (2*u_dim-1) - i
-                v_min = (v_dim-1) - j
-                v_max = (2*v_dim-1) - j
-                # u_dim*v_dim columns for the u-component:
-                jacobi[:, u_column] = self.u[v_min:v_max, u_min:u_max].reshape(-1)
-                # u_dim*v_dim columns for the v-component (note the minus!):
-                jacobi[:, v_column] = -self.v[v_min:v_max, u_min:u_max].reshape(-1)
+        dim_proj, dim_rot, dim_perp = self.dim_proj, self.dim_rot, self.dim_perp
+        size_2d = dim_rot * dim_perp
+        size_3d = dim_rot * dim_perp * dim_proj
+        weights = self.weights
+        tilt = self.tilt
+
+        def get_weight_matrix():
+            weight_matrix = np.zeros((size_2d, size_3d))
+            for voxel in weights:
+                voxel_weights = weights[voxel]
+                for pixel, weight in voxel_weights:
+                    for i in range(dim_rot):
+                        pixel_pos = i*dim_rot + pixel
+                        voxel_pos = voxel[0]*dim_proj*dim_rot + i*dim_rot + voxel[1]
+                        weight_matrix[pixel_pos, voxel_pos] = weight
+            return weight_matrix
+
+        weight_matrix = get_weight_matrix()
+        jacobi = np.zeros((2*size_2d, 3*size_3d))
+        jacobi[:size_2d, :size_3d] = np.cos(tilt) * weight_matrix
+        jacobi[:size_2d, 2*size_3d:] = np.sin(tilt) * weight_matrix
+        jacobi[size_2d:, size_3d:2*size_3d] = weight_matrix
         return jacobi
 
     def multiply_jacobi(self, vector):
@@ -293,19 +405,28 @@ class Projection:
             Product of the Jacobi matrix (which is not explicitely calculated) with the vector.
 
         ''' # TODO: Docstring!
-        v_dim, u_dim = self.dim
-        size = v_dim * u_dim
-        assert len(vector) == 2*size, 'vector size not compatible!'
-        result = np.zeros(size)
-        for s in range(size):  # column-wise (two columns at a time, u- and v-component)
-            i = s % u_dim
-            j = int(s/u_dim)
-            u_min = (u_dim-1) - i
-            u_max = (2*u_dim-1) - i
-            v_min = (v_dim-1) - j
-            v_max = (2*v_dim-1) - j
-            result += vector[s]*self.u[v_min:v_max, u_min:u_max].reshape(-1)  # u
-            result += vector[s+size]*-self.v[v_min:v_max, u_min:u_max].reshape(-1)  # v        
+        dim_proj, dim_rot, dim_perp = self.dim_proj, self.dim_rot, self.dim_perp
+        size_2d = dim_rot * dim_perp
+        size_3d = dim_rot * dim_perp * dim_proj
+        weights = self.weights
+        tilt = self.tilt
+        assert len(vector) == 3*size_3d
+
+        def multiply_weight_matrix(vector):
+            result = np.zeros(size_2d)
+            for voxel in weights:
+                voxel_weights = weights[voxel]
+                for pixel, weight in voxel_weights:
+                    for i in range(dim_rot):
+                        pixel_pos = i*dim_rot + pixel
+                        voxel_pos = voxel[0]*dim_proj*dim_rot + i*dim_rot + voxel[1]
+                        result[pixel_pos] += weight * vector[voxel_pos]
+            return result
+        
+        result = np.zeros(2*size_2d)
+        result[:size_2d] = (np.cos(tilt) * multiply_weight_matrix(vector[:size_3d])
+                          + np.sin(tilt) * multiply_weight_matrix(vector[2*size_3d:]))
+        result[size_2d:] = multiply_weight_matrix(vector[size_3d:2*size_3d])
         return result
 
     def multiply_jacobi_T(self, vector):
@@ -325,17 +446,26 @@ class Projection:
             the vector.
 
         ''' # TODO: Docstring!
-        v_dim, u_dim = self.dim
-        size = v_dim * u_dim
-        assert len(vector) == size, 'vector size not compatible!'
-        result = np.zeros(2*size)
-        for s in range(size):  # row-wise (two rows at a time, u- and v-component)
-            i = s % u_dim
-            j = int(s/u_dim)
-            u_min = (u_dim-1) - i
-            u_max = (2*u_dim-1) - i
-            v_min = (v_dim-1) - j
-            v_max = (2*v_dim-1) - j
-            result[s] = np.sum(vector*self.u[v_min:v_max, u_min:u_max].reshape(-1))  # u
-            result[s+size] = np.sum(vector*-self.v[v_min:v_max, u_min:u_max].reshape(-1))  # v        
+        dim_proj, dim_rot, dim_perp = self.dim_proj, self.dim_rot, self.dim_perp
+        size_2d = dim_rot * dim_perp
+        size_3d = dim_rot * dim_perp * dim_proj
+        weights = self.weights
+        tilt = self.tilt
+        assert len(vector) == 2*size_2d
+
+        def multiply_weight_matrix_T(vector):
+            result = np.zeros(size_3d)
+            for voxel in weights:
+                voxel_weights = weights[voxel]
+                for pixel, weight in voxel_weights:
+                    for i in range(dim_rot):
+                        pixel_pos = i*dim_rot + pixel
+                        voxel_pos = voxel[0]*dim_proj*dim_rot + i*dim_rot + voxel[1]
+                        result[voxel_pos] += weight * vector[pixel_pos]
+            return result
+
+        result = np.zeros(3*size_3d)
+        result[:size_3d] = np.cos(tilt) * multiply_weight_matrix_T(vector[:size_2d])
+        result[size_3d:2*size_3d] = multiply_weight_matrix_T(vector[size_2d:])
+        result[2*size_3d:] = np.sin(tilt) * multiply_weight_matrix_T(vector[:size_2d])
         return result
diff --git a/pyramid/reconstructor.py b/pyramid/reconstructor.py
index 7c995dc..5196cd0 100644
--- a/pyramid/reconstructor.py
+++ b/pyramid/reconstructor.py
@@ -19,6 +19,8 @@ from scipy.optimize import leastsq
 import pyramid.projector as pj
 import pyramid.phasemapper as pm
 from pyramid.magdata import MagData
+from pyramid.projector import Projection
+from pyramid.kernel import Kernel
 
 
 def reconstruct_simple_leastsq(phase_map, mask, b_0=1):
@@ -73,3 +75,7 @@ def reconstruct_simple_leastsq(phase_map, mask, b_0=1):
     x_rec, _ = leastsq(J, np.zeros(3*count))
     mag_data_rec.set_vector(mask, x_rec)
     return mag_data_rec
+
+def reconstruct_test():
+    product = (kernel.multiply_jacobi_T(projection.multiply_jacobi_T(x))
+             * kernel.multiply_jacobi(projection.multiply_jacobi(x)))
\ No newline at end of file
diff --git a/scripts/get_jacobi_projection.py b/scripts/get_jacobi_projection.py
index 079c39a..ecff0f2 100644
--- a/scripts/get_jacobi_projection.py
+++ b/scripts/get_jacobi_projection.py
@@ -7,14 +7,140 @@ Created on Fri Nov 29 16:45:13 2013
 
 import pyramid.magcreator as mc
 from pyramid.magdata import MagData
-import pyramid.projector as pj
 from pyramid.projector import Projection
+from pyramid.kernel import Kernel
+
+import numpy as np
+from numpy import pi
+
+import time
+
 
 
 a = 1.0
-dim = (2, 2, 2)
-px = (0, 0, 0)
-mag_data = MagData(a, mc.create_mag_dist_homog(mc.Shapes.pixel(dim, px), 0))
+dim = (3, 3, 3)
+px = (1, 1, 1)
+tilt = pi/3
+
+mag_data = MagData(a, mc.create_mag_dist_homog(mc.Shapes.pixel(dim, px), phi=pi/4, theta=pi/4))
+
+proj = Projection.single_tilt_projection(mag_data, tilt)
+
+size_2d = dim[1] * dim[2]
+size_3d = dim[0] * dim[1] * dim[2]
+
+ref_u = proj.u
+ref_v = proj.v
+
+z_mag_vec = np.asmatrix(mag_data.magnitude[0].reshape(-1)).T
+y_mag_vec = np.asmatrix(mag_data.magnitude[1].reshape(-1)).T
+x_mag_vec = np.asmatrix(mag_data.magnitude[2].reshape(-1)).T
+
+mag_vec = np.concatenate((x_mag_vec, y_mag_vec, z_mag_vec))
+
+
+
+# Via full multiplicatoin with weight-matrix:
+
+start = time.clock()
+
+weight_matrix = np.asmatrix(proj.get_weight_matrix())
+
+test_u_wf = (np.cos(tilt) * np.asarray(weight_matrix * x_mag_vec).reshape(dim[1], dim[2])
+           + np.sin(tilt) * np.asarray(weight_matrix * z_mag_vec).reshape(dim[1], dim[2]))
+
+test_v_wf = np.asarray(weight_matrix * y_mag_vec).reshape(dim[1], dim[2])
+
+print 'Time for calculation via full weight-matrix multiplication:  ', time.clock() - start
+
+np.testing.assert_almost_equal(test_u_wf, ref_u, err_msg='u-part does not match!')
+np.testing.assert_almost_equal(test_v_wf, ref_v, err_msg='v-part does not match!')
+
+
+
+# Via direct multiplication with weight_matrix:
+
+start = time.clock()
+
+test_u_wd = (np.cos(tilt) * proj.multiply_weight_matrix(x_mag_vec).reshape(dim[1], dim[2])
+           + np.sin(tilt) * proj.multiply_weight_matrix(z_mag_vec).reshape(dim[1], dim[2]))
+
+test_v_wd = proj.multiply_weight_matrix(y_mag_vec).reshape(dim[1], dim[2])
+
+print 'Time for calculation via direct weight-matrix multiplication:', time.clock() - start
+
+np.testing.assert_almost_equal(test_u_wd, ref_u, err_msg='u-part does not match!')
+np.testing.assert_almost_equal(test_v_wd, ref_v, err_msg='v-part does not match!')
+
+
+
+# Via full multiplication with jacobi-matrix:
+
+start = time.clock()
+
+jacobi = np.asmatrix(proj.get_jacobi())
+projected_mag = np.asarray(jacobi * mag_vec).reshape(2, dim[1], dim[2])
+
+test_u_jf = projected_mag[0, ...]
+test_v_jf = projected_mag[1, ...]
+
+print 'Time for calculation via full jacobi-matrix multiplication:  ', time.clock() - start
+
+np.testing.assert_almost_equal(test_u_jf, ref_u, err_msg='u-part does not match!')
+np.testing.assert_almost_equal(test_v_jf, ref_v, err_msg='v-part does not match!')
+
+
+
+# Via direct multiplication with jacobi-matrix:
+
+start = time.clock()
+
+projected_mag = proj.multiply_jacobi(mag_vec).reshape(2, dim[1], dim[2])
+
+test_u_jd = projected_mag[0, ...]
+test_v_jd = projected_mag[1, ...]
+
+print 'Time for calculation via direct jacobi-matrix multiplication:', time.clock() - start
+
+np.testing.assert_almost_equal(test_u_jd, ref_u, err_msg='u-part does not match!')
+np.testing.assert_almost_equal(test_v_jd, ref_v, err_msg='v-part does not match!')
+
+
+
+# Via full multiplication with transposed jacobi-matrix:
+
+start = time.clock()
+
+jacobi_T = np.asmatrix(proj.get_jacobi()).T
+
+test_T_jf = np.asarray(jacobi_T * np.asmatrix(np.ones(2*size_2d)).T).reshape(-1)
+
+print 'Time for calculation via full transposed multiplication:     ', time.clock() - start
+
+
+
+
+# Via direct multiplication with transposed jacobi-matrix:
+
+start = time.clock()
+
+jacobi_T = np.asmatrix(proj.get_jacobi()).T
+
+test_complete_T = np.asarray(jacobi_T * np.asmatrix(np.ones(2*size_2d)).T).reshape(-1)
+
+test_T_jd = proj.multiply_jacobi_T(np.ones(2*size_2d))
+
+print 'Time for calculation via direct transposed multiplication:   ', time.clock() - start
+
+np.testing.assert_almost_equal(test_T_jd, test_T_jf, err_msg='Transposed vector does not match!')
+
 
-proj = pj.simple_axis_projection(mag_data)
 
+## Cost function testing:
+#
+#kern = Kernel((dim[1], dim[2]), a)
+#
+#identity = np.eye(5)
+#
+#right = kern.multiply_jacobi(proj.multiply_jacobi(mag_vec))
+#cost = kern.multiply_jacobi_T(proj.multiply_jacobi_T(right))
\ No newline at end of file
diff --git a/scripts/reconstruction/get_jacobi.py b/scripts/reconstruction/get_jacobi.py
index c491900..b963050 100644
--- a/scripts/reconstruction/get_jacobi.py
+++ b/scripts/reconstruction/get_jacobi.py
@@ -23,7 +23,7 @@ if not os.path.exists(directory):
     os.makedirs(directory)
 filename = directory + '/jacobi.npy'
 b_0 = 1.0  # in T
-dim = (1, 8, 8)  # in px (y,x)
+dim = (1, 64, 64)  # in px (y,x)
 res = 10.0  # in nm
 phi = pi/4
 
@@ -32,51 +32,42 @@ width = (0, 1, 1)  # in px (y,x)
 
 mag_data = MagData(res, mc.create_mag_dist_homog(mc.Shapes.slab(dim, center, width), phi))
 projection = pj.simple_axis_projection(mag_data)
-print 'Projection calculated!'
+print 'Compare times for multiplication of a vector (ones) with the jacobi matrix!'
 
-'''NUMERICAL SOLUTION'''
-# numerical solution Real Space:
+# Prepare kernel and vector:
 dim_proj = np.shape(projection[0])
 size = np.prod(dim_proj)
 kernel = pm.Kernel(dim_proj, res, 'disc')
+vector = np.ones(2*size)
 
+# Calculation with kernel (core):
 tic = time.clock()
-kernel.multiply_jacobi(np.ones(2*size))  # column-wise
+result_core = kernel.multiply_jacobi_core(vector)
 toc = time.clock()
-print 'Time for one multiplication with the Jacobi-Matrix:      ', toc - tic
+print 'Calculation with kernel (core):   ', toc - tic
 
-jacobi = np.zeros((size, 2*size))
+# Calculation with kernel (core2):
 tic = time.clock()
-phase_map = PhaseMap(res, pm.phase_mag_real(res, projection, b_0, 'disc', jacobi=jacobi))
+result_core2 = kernel.multiply_jacobi_core2(vector)
 toc = time.clock()
-phase_map.display()
-np.savetxt(filename, jacobi)
-print 'Time for Jacobi-Matrix during phase calculation:         ', toc - tic
+print 'Calculation with kernel (core2):  ', toc - tic
 
+# Calculation with kernel:
 tic = time.clock()
-jacobi_test = kernel.get_jacobi()
+result_kernel = kernel.multiply_jacobi(vector)
 toc = time.clock()
-print 'Time for Jacobi-Matrix from the Kernel:                  ', toc - tic
+print 'Calculation with kernel:          ', toc - tic
 
-unity = np.eye(2*size)
-jacobi_test2 = np.zeros((size, 2*size))
+# Calculation during phasemapping:
 tic = time.clock()
-for i in range(unity.shape[1]):
-    jacobi_test2[:, i] = kernel.multiply_jacobi(unity[:, i])  # column-wise
-toc = time.clock()
-print 'Time for getting the Jacobi-Matrix (vector-wise):        ', toc - tic
-
-unity_transp = np.eye(size)
-jacobi_transp = np.zeros((2*size, size))
-tic = time.clock()
-for i in range(unity_transp.shape[1]):
-    jacobi_transp[:, i] = kernel.multiply_jacobi_T(unity_transp[:, i])  # column-wise
+jacobi = np.zeros((size, 2*size))
+PhaseMap(res, pm.phase_mag_real(res, projection, b_0, 'disc', jacobi=jacobi))
+result_during = np.asarray(np.asmatrix(jacobi)*np.asmatrix(vector).T).reshape(-1)
 toc = time.clock()
-print 'Time for getting the transp. Jacobi-Matrix (vector-wise):', toc - tic
+print 'Calculation during phasemapping:  ', toc - tic
 
-print 'Methods (during vs kernel) in accordance?                ', \
-    np.logical_not(np.all(jacobi-jacobi_test))
-print 'Methods (during vs vector-wise) in accordance?           ', \
-    np.logical_not(np.all(jacobi-jacobi_test2))
-print 'Methods (transponed Jacobi) in accordance?               ', \
-    np.logical_not(np.all(jacobi.T-jacobi_transp))
+# Test if calculations match:
+np.testing.assert_almost_equal(result_kernel, result_during)
+np.testing.assert_almost_equal(result_core, result_during)
+np.testing.assert_almost_equal(result_core2, result_during)
+print 'Calculations give the same result!'
-- 
GitLab