Skip to content
Snippets Groups Projects
Forked from empyre / empyre
387 commits behind the upstream repository.
  • Jan Caron's avatar
    ce62f949
    Further optimization · ce62f949
    Jan Caron authored
    pep8: optimizations
    phasemapper_core: numcore module for phasemapper
    scripts: compatibility with new structure
    reconstruction: new scripts
    ce62f949
    History
    Further optimization
    Jan Caron authored
    pep8: optimizations
    phasemapper_core: numcore module for phasemapper
    scripts: compatibility with new structure
    reconstruction: new scripts
costfunction.py 5.36 KiB
# -*- coding: utf-8 -*-
"""This module provides the :class:`~.Costfunction` class which represents a strategy to calculate
the so called `cost` of a threedimensional magnetization distribution."""


import numpy as np

from scipy.sparse.linalg import LinearOperator
from scipy.sparse import eye

import logging


class Costfunction(object):

    '''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
    relation to two-dimensional phase maps. The `cost` is a measure for the difference of the
    simulated phase maps from the magnetic distributions to the given set of phase maps.
    Furthermore this class provides convenient methods for the calculation of the derivative
    :func:`~.jac` or the product with the Hessian matrix :func:`~.hess_dot` of the costfunction,
    which can be used by optimizers.

    Attributes
    ----------
    y : :class:`~numpy.ndarray` (N=1)
        Vector which lists all pixel values of all phase maps one after another.
    fwd_model : :class:`~.ForwardModel`
        The Forward model instance which should be used for the simulation of the phase maps which
        will be compared to `y`.
    lam : float, optional
        Regularization parameter used in the Hessian matrix multiplication. Default is 0.

    '''

    LOG = logging.getLogger(__name__+'.Costfunction')

    def __init__(self, y, fwd_model, lam=0):
        self.LOG.debug('Calling __init__')
        self.y = y
        self.fwd_model = fwd_model
        self.lam = lam
        self.Se_inv = eye(len(y))
        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))

    def __repr__(self):
        self.LOG.debug('Calling __repr__')
        return '%s(fwd_model=%r, lam=%r)' % (self.__class__, self.fwd_model, self.lam)

    def __str__(self):
        self.LOG.debug('Calling __str__')
        return 'Costfunction(fwd_model=%s, lam=%s)' % (self.fwd_model, self.lam)

    def jac(self, x):
        '''Calculate the derivative of the costfunction for a given magnetization distribution.

        Parameters
        ----------
        x : :class:`~numpy.ndarray` (N=1)
            Vectorized magnetization distribution, for which the Jacobi vector is calculated.

        Returns
        -------
        result : :class:`~numpy.ndarray` (N=1)
            Jacobi vector which represents the cost derivative of all voxels of the magnetization.

        '''
        self.LOG.debug('Calling jac')
        y = self.y
        F = self.fwd_model
        Se_inv = self.Se_inv
        return F.jac_T_dot(x, Se_inv.dot(F(x)-y))

    def hess_dot(self, x, vector):
        '''Calculate the product of a `vector` with the Hession matrix of the costfunction.

        Parameters
        ----------
        x : :class:`~numpy.ndarray` (N=1)
            Vectorized magnetization distribution at which the Hessian is calculated. The Hessian
            is constant in this case, thus `x` can be set to None (it is not used int the
            computation). It is implemented for the case that in the future nonlinear problems
            have to be solved.
        vector : :class:`~numpy.ndarray` (N=1)
            Vectorized magnetization distribution which is multiplied by the Hessian.

        Returns
        -------
        result : :class:`~numpy.ndarray` (N=1)
            Product of the input `vector` with the Hessian matrix of the costfunction.

        '''
        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


class CFAdapterScipyCG(LinearOperator):

    '''Adapter class making the :class:`~.Costfunction` class accessible for scipy cg methods.

    This class provides an adapter for the :class:`~.Costfunction` to be usable with the
    :func:`~.scipy.sparse.linalg.cg` function. the :func:`~.matvec` function is overwritten to
    implement a multiplication with the Hessian of the adapted costfunction. This is used in the
    :func:`~pyramid.reconstruction.optimice_sparse_cg` function of the
    :mod:`~pyramid.reconstruction` module.

    Attributes
    ----------
    cost : :class:`~.Costfunction`
        Costfunction which should be made usable in the :func:`~.scipy.sparse.linalg.cg` function.

    '''

    LOG = logging.getLogger(__name__+'.CFAdapterScipyCG')

    def __init__(self, cost):
        self.LOG.debug('Calling __init__')
        self.cost = cost

    def matvec(self, vector):
        '''Matrix-vector multiplication with the Hessian of the adapted costfunction.

        Parameters
        ----------
        vector : :class:`~numpy.ndarray` (N=1)
            Vector which will be multiplied by the Hessian matrix provided by the adapted
            costfunction.

        '''
        self.LOG.debug('Calling matvec')
        return self.cost.hess_dot(None, vector)

    @property
    def shape(self):
        return (3*self.cost.fwd_model.size_3d, 3*self.cost.fwd_model.size_3d)

    @property
    def dtype(self):
        return np.dtype("d")