def mesh_register(mesh, field, max_steps=<NUM_LIT>, max_step_size=<NUM_LIT>, max_pe_change=<NUM_LIT:1>,<EOL>method='<STR_LIT>', return_report=False, initial_coordinates=None):
<EOL>if not isinstance(mesh, geo.Mesh):<EOL><INDENT>raise RuntimeError('<STR_LIT>')<EOL><DEDENT>if not pimms.is_vector(max_steps): max_steps = [max_steps]<EOL>for ms in max_steps:<EOL><INDENT>if not pimms.is_int(ms) or ms < <NUM_LIT:0>:<EOL><INDENT>raise RuntimeError('<STR_LIT>')<EOL><DEDENT><DEDENT>if not pimms.is_vector(max_step_size): max_step_size = [max_step_size]<EOL>for mss in max_step_size:<EOL><INDENT>if not pimms.is_number(mss) or mss <= <NUM_LIT:0>:<EOL><INDENT>raise RuntimeError('<STR_LIT>')<EOL><DEDENT><DEDENT>if not pimms.is_number(max_pe_change) or max_pe_change <= <NUM_LIT:0> or max_pe_change > <NUM_LIT:1>:<EOL><INDENT>raise RuntimeError('<STR_LIT>')<EOL><DEDENT>if pimms.is_vector(method):<EOL><INDENT>if method[<NUM_LIT:0>].lower() == '<STR_LIT>' and len(method) > <NUM_LIT:1> and not pimms.is_str(method[<NUM_LIT:1>]):<EOL><INDENT>method = [method]<EOL><DEDENT><DEDENT>else: method = [method]<EOL>if initial_coordinates is None:<EOL><INDENT>init_coords = mesh.coordinates<EOL><DEDENT>else:<EOL><INDENT>init_coords = np.asarray(initial_coordinates)<EOL>if init_coords.shape[<NUM_LIT:0>] != mesh.coordinates.shape[<NUM_LIT:0>]:<EOL><INDENT>init_coords = init_coords.T<EOL><DEDENT><DEDENT>if np.sum(max_steps) == <NUM_LIT:0>:<EOL><INDENT>if return_report: return None<EOL>else: return init_coords<EOL><DEDENT>max_pe_change = float(max_pe_change)<EOL>nrounds = len(max_steps)<EOL>if nrounds > <NUM_LIT:1>:<EOL><INDENT>if len(max_step_size) == <NUM_LIT:1>: max_step_size = [max_step_size[<NUM_LIT:0>] for _ in max_steps]<EOL>if len(method) == <NUM_LIT:1>:        method        = [method[<NUM_LIT:0>]        for _ in max_steps]<EOL><DEDENT>faces  = to_java_ints(mesh.tess.indexed_faces)<EOL>edges  = to_java_ints(mesh.tess.indexed_edges)<EOL>coords = to_java_doubles(mesh.coordinates)<EOL>init_coords = coords if init_coords is mesh.coordinates else to_java_doubles(init_coords)<EOL>potential = _parse_field_arguments(field, faces, edges, coords)<EOL>rep = []<EOL>for (method,max_step_size,max_steps) in zip(method, max_step_size, max_steps):<EOL><INDENT>minimizer = java_link().jvm.nben.mesh.registration.Minimizer(potential, init_coords)<EOL>max_step_size = float(max_step_size)<EOL>max_steps = int(max_steps)<EOL>if pimms.is_str(method):<EOL><INDENT>method = method.lower()<EOL>if method == '<STR_LIT>': k = <NUM_LIT:4><EOL>else:                  k = <NUM_LIT:0><EOL><DEDENT>else:<EOL><INDENT>k = method[<NUM_LIT:1>]<EOL>method = method[<NUM_LIT:0>].lower()<EOL><DEDENT>if method == '<STR_LIT>':<EOL><INDENT>r = minimizer.step(max_pe_change, max_steps, max_step_size)<EOL><DEDENT>elif method == '<STR_LIT>':<EOL><INDENT>r = minimizer.randomStep(max_pe_change, max_steps, max_step_size, k == -<NUM_LIT:1>)<EOL><DEDENT>elif method == '<STR_LIT>':<EOL><INDENT>r = minimizer.nimbleStep(max_pe_change, max_steps, max_step_size, int(k))<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>' % method)<EOL><DEDENT>rep.append(r)<EOL>init_coords = minimizer.getX()<EOL><DEDENT>if return_report:<EOL><INDENT>return rep<EOL><DEDENT>else:<EOL><INDENT>result = init_coords<EOL>return np.asarray([[x for x in row] for row in result])<EOL><DEDENT>
mesh_register(mesh, field) yields the mesh that results from registering the given mesh by
minimizing the given potential field description over the position of the vertices in the
mesh. The mesh argument must be a Mesh object (see neuropythy.geometry) such as can be read
from FreeSurfer using the neuropythy.freesurfer_subject function. The field argument must be
a list of field names and arguments; with the exception of 'mesh' (or 'standard'), the 
arguments must be a list, the first element of which is the field type name, the second
element of which is the field shape name, and the final element of which is a dictionary of
arguments accepted by the field shape.

The following are valid field type names:
  * 'mesh' : the standard mesh potential, which includes an edge potential, an angle
    potential, and a perimeter potential. Accepts no arguments, and must be passed as a
    single string instead of a list.
  * 'edge': an edge potential field in which the potential is a function of the change in the
    edge length, summed over each edge in the mesh.
  * 'angle': an angle potential field in which the potential is a function of the change in
    the angle measure, summed over all angles in the mesh.
  * 'perimeter': a potential that depends on the vertices on the perimeter of a 2D mesh
    remaining in place; the potential changes as a function of the distance of each perimeter
    vertex from its reference position.
  * 'anchor': a potential that depends on the distance of a set of vertices from fixed points
    in space. After the shape name second argument, an anchor must be followed by a list of
    vertex ids then a list of fixed points to which the vertex ids are anchored:
    ['anchor', shape_name, vertex_ids, fixed_points, args...].

The following are valid shape names:
  * 'harmonic': a harmonic function with the form (c/q) * abs(x - x0)^q.
    Parameters: 
      * 'scale', the scale parameter c; default: 1.
      * 'order', the order parameter q; default: 2.
  * 'Lennard-Jones': a Lennard-Jones function with the form c (1 + (r0/r)^q - 2(r0/r)^(q/2));
    Parameters:
      * 'scale': the scale parameter c; default: 1. 
      * 'order': the order parameter q; default: 2.
  * 'Gaussian': A Gaussian function with the form c (1 - exp(-0.5 abs((x - x0)/s)^q))
    Parameters:
      * 'scale': the scale parameter c; default: 1.
      * 'order': the order parameter q; default: 2.
      * 'sigma': the standard deviation parameter s; default: 1.
  * 'infinite-well': an infinite well function with the form 
    c ( (((x0 - m)/(x - m))^q - 1)^2 + (((M - x0)/(M - x))^q - 1)^2 )
    Parameters:
      * 'scale': the scale parameter c; default: 1.
      * 'order': the order parameter q; default: 0.5.
      * 'min': the minimum value m; default: 0.
      * 'max': the maximum value M; default: pi.

Options: The following optional arguments are accepted.
  * max_steps (default: 2000) the maximum number of steps to minimize for.
  * max_step_size (default: 0.1) the maximum distance to allow a vertex to move in a single
    minimization step.
  * max_pe_change: the maximum fraction of the initial potential value that the minimizer
    should minimize away before returning; i.e., 0 indicates that no minimization should be
    allowed while 0.9 would indicate that the minimizer should minimize until the potential
    is 10% or less of the initial potential.
  * return_report (default: False) indicates that instead of returning the registered data,
    mesh_register should instead return the Java Minimizer.Report object (for debugging).
  * method (default: 'random') specifies the search algorithm used; available options are 
    'random', 'nimble', and 'pure'. Generally all options will converge on a similar solution,
    but usually 'random' is fastest. The 'pure' option uses the nben library's step function,
    which performs straight-forward gradient descent. The 'nimble' option performs a gradient
    descent in which subsets of vertices in the mesh that have the highest gradients during the
    registration are updated more often than those vertices with small gradients; this can
    sometimes but not always increase the speed of the minimization. Note that instead of
    'nimble', one may alternately provide ('nimble', k) where k is the number of partitions that
    the vertices should be sorted into (by partition). 'nimble' by itself is equivalent to 
    ('nimble', 4). Note also that a single step of nimble minimization is equivalent to 2**k
    steps of 'pure' minimization. Finally, the 'random' option uses the nben library's
    randomStep function, which is a gradient descent algorithm that moves each vertex in the
    direction of its negative gradient during each step but which randomizes the length of the
    gradient at each individual vertex by drawing from an exponential distribution centered at
    the vertex's actual gradient length. In effect, this can prevent vertices with very large
    gradients from dominating the minimization and often results in the best results.
  * initial_coordinates (default: None) specifies the start coordinates of the registration;
    if None, uses those in the given mesh, which is generally desired.

Examples:
  registered_mesh = mesh_register(
     mesh,
     [['edge', 'harmonic', 'scale', 0.5], # slightly weak edge potential
      ['angle', 'infinite-well'], # default arguments for an infinite-well angle potential
      ['anchor', 'Gaussian', [1, 10, 50], [[0.0, 0.0], [1.1, 1.1], [2.2, 2.2]]]],
     max_step_size=0.05,
     max_steps=10000)