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