Source code for ddg.optimize.ifs._wrappers

""" Module for IndexedFaceSet function(al)s wrappers.

In general optimization libraries expect functionals to be given in a
flattened input format, i.e.

    def functional(x):
        ...
        ...
        ...
        return value,

where x would be vector of some kind (e.g. numpy.ndarray).

For our purposes functionals are defined on an IndexedFaceSet, and thus
have to be wrapped to allow optimizations libraries to work with them.

For more information on the required signature and examples for functionals see
`ddg.optimize.ifs.functionals`.
"""
import numpy as np

from . import utils as ut


def _flatten_input_ifs_function_ignore(ifs, function, param_array, shape, mask, *args):
    """Converts an indexed faceset function into a flattened input format.

    Parameters
    ----------
    ifs : IndexedFaceSet
        Indexed face set to apply function on
    function : Callable
        Function to wrap into a flattened input format
    param_array : numpy.ndarray
        Array of the parameters
    shape : tuple of ints
        shape of the parameter array
    mask : tuple of bools
        True for each row to write to the parameter array, False otherwise

    Returns
    -------
    Callable
        Flattened function
    """

    def flattened_function(x):
        x = np.array(x).reshape(shape)[mask]
        param_array[mask] = x
        return function(ifs, *args)

    return flattened_function


def _flatten_input_ifs_function_remove(ifs, function, param_array, shape, mask, *args):
    """Converts an indexed faceset function into a flattened input format.

    Parameters
    ----------
    ifs : IndexedFaceSet
        Indexed face set to apply function on
    function : Callable
        Function to wrap into a flattened input format
    param_array : numpy.ndarray
        Array of the parameters
    shape : tuple of ints
        shape of the considered region of the parameter array
    mask : tuple of bools
        True for each row to write to the parameter array, False otherwise

    Returns
    -------
    Callable
        Flattened function
    """

    def flattened_function(x):
        x = np.array(x).reshape(shape)
        param_array[mask] = x
        return function(ifs, *args)

    return flattened_function


[docs]def flatten_functional_ignore(ifs, functional, cell_type, *attr_name, boundary=[]): """Returns a functional over ifs that takes a flat array as input. The resulting function will accept an array of length #cells*dim(attribute) and will ignore the values for boundary-cells. Parameters ---------- ifs : IndexedFaceSet IndexedFaceSet to apply the function on functional: Callable Functional to wrap into a flattened input format attr_name: str Name of the indexed faceset attribute to use cell_type: {'verts', 'edges', 'faces'} Cell type of the attribute boundary: Iterable (default=[]) Vertices to ignore for applying new values Returns ------- Callable Wrapped Function See Also -------- wrap_function_remove, wrap_functional """ array, mask, shape, _ = ut.attribute_helper(ifs, cell_type, attr_name[0], boundary) return _flatten_input_ifs_function_ignore( ifs, functional, array, shape, mask, *attr_name )
[docs]def flatten_functional(ifs, functional, cell_type, *attr_name, boundary=[]): """Returns a functional over ifs that takes a flat array as input. The resulting function will accept an array of length #(non-boundary-cells)*dim(attribute). Parameters ---------- ifs : IndexedFaceSet IndexedFaceSet to apply the function on functional: Callable Functional to wrap into a flattened input format cell_type: {'verts', 'edges', 'faces'} Cell type of the attribute attr_name: str Name of the indexed faceset attribute to use boundary: Iterable (default=[]) Vertices to ignore for applying new values Returns ------- Callable Wrapped Function See Also -------- wrap_function_ignore, wrap_functional """ array, mask, _, shape = ut.attribute_helper(ifs, cell_type, attr_name[0], boundary) return _flatten_input_ifs_function_remove( ifs, functional, array, shape, mask, *attr_name )
[docs]def flatten_gradient(ifs, gradient, cell_type, *attr_name, boundary=[]): """Returns a gradient function over ifs that takes a flat array as input. The resulting function will accept an array of length #(non-boundary-cells)*dim(attribute). Parameters ---------- ifs : IndexedFaceSet IndexedFaceSet to apply the function on gradient: Callable returning sparse matrices Gradient to wrap into a flattened input format cell_type: {'verts', 'edges', 'faces'} Cell type of the attribute attr_name: str Name of the indexed faceset attribute to use boundary: Iterable (default=[]) Vertices to ignore for applying new values Returns ------- Callable Wrapped Function See Also -------- flatten_gradient_ignore """ array, mask, _, shape = ut.attribute_helper(ifs, cell_type, attr_name[0], boundary) temp = _flatten_input_ifs_function_remove( ifs, gradient, array, shape, mask, *attr_name ) if np.size(boundary): if len(shape) != 1: temp = ut.remove_columns(temp, ut.nflat_index(boundary, shape[-1])) else: temp = ut.remove_columns(temp, boundary) return temp
[docs]def flatten_gradient_ignore(ifs, gradient, cell_type, *attr_name, boundary=[]): """Returns a gradient function over ifs that takes a flat array as input. The resulting function will accept an array of length #cells*dim(attribute) and will ignore the values for boundary-cells. Parameters ---------- ifs : IndexedFaceSet IndexedFaceSet to apply the function on gradient: Callable returning sparse matrices Gradient to wrap into a flattened input format cell_type: {'verts, 'edges', 'faces'} Cell type of the attribute attr_name: str Name of the indexed faceset attribute to use boundary: Iterable (default=[]) Vertices to ignore for applying new values Returns ------- Callable Wrapped Function See Also -------- flatten_gradient """ array, mask, shape, _ = ut.attribute_helper(ifs, cell_type, attr_name[0], boundary) temp = _flatten_input_ifs_function_ignore( ifs, gradient, array, shape, mask, *attr_name ) if np.size(boundary): if len(shape) != 1: temp = ut.zero_columns(temp, ut.nflat_index(boundary, shape[-1])) else: temp = ut.zero_columns(temp, boundary) return temp
[docs]def wrap_functional(ifs, functional, gradient, cell_type, *attr_name, boundary=[]): """Returns a functional and a gradient function over ifs, both taking a flat array as input. The resulting function will accept an array of length #(non-boundary-cells)*dim(attribute) and the columns corresponding to the boundary vertices of the jacobian/gradient will be removed. Parameters ---------- ifs : IndexedFaceSet IndexedFaceSet to apply the function on functional: Callable Functional to wrap into a flattened input format gradient: Callable Gradient of the functional cell_type: {'verts', 'edges', 'faces'} Cell type of the attribute *attr_name: tuple of str Name of the indexed faceset attribute(s) to use The first entry is assumed to be the parameters boundary: Iterable (default=[]) Vertices to ignore for applying new values Returns ------- wr_functional : Callable Wrapped functional wr_gradient : Callable Wrapped jacobian/gradient See Also -------- wrap_function_ignore, wrap_function_remove """ array, mask, _, shape = ut.attribute_helper(ifs, cell_type, attr_name[0], boundary) wr_functional = _flatten_input_ifs_function_remove( ifs, functional, array, shape, mask, *attr_name ) wr_gradient = _flatten_input_ifs_function_remove( ifs, gradient, array, shape, mask, *attr_name ) if boundary != []: if len(shape) != 1: wr_gradient = ut.remove_columns( wr_gradient, ut.nflat_index(boundary, shape[-1]) ) else: wr_gradient = ut.remove_columns(wr_gradient, boundary) return wr_functional, wr_gradient