Notebook source code: notebooks/how_to/07_functional_map.ipynb
Run it yourself on binder Binder badge

How to compute a functional map?#

 In [1]:
import geomstats.backend as gs

from geomfum.dataset import NotebooksDataset
from geomfum.descriptor.pipeline import (
    ArangeSubsampler,
    DescriptorPipeline,
    L2InnerNormalizer,
)
from geomfum.descriptor.spectral import HeatKernelSignature, WaveKernelSignature
from geomfum.functional_map import (
    FactorSum,
    LBCommutativityEnforcing,
    OperatorCommutativityEnforcing,
    SpectralDescriptorPreservation,
)
from geomfum.numerics.optimization import ScipyMinimize
from geomfum.shape import TriangleMesh

Load meshes.

 In [2]:
dataset = NotebooksDataset()

mesh_a = TriangleMesh.from_file(dataset.get_filename("cat-00"))
mesh_b = TriangleMesh.from_file(dataset.get_filename("lion-00"))

Set Laplace eigenbasis for each mesh.

 In [3]:
mesh_a.laplacian.find_spectrum(spectrum_size=10, set_as_basis=True)
mesh_b.laplacian.find_spectrum(spectrum_size=10, set_as_basis=True)

# I decide to visualize just the first 8 eigenfunctions

mesh_b.basis.use_k = 8

Set a descriptor pipeline and apply it to both shapes.

 In [4]:
steps = [
    HeatKernelSignature.from_registry(n_domain=4),
    ArangeSubsampler(subsample_step=2),
    WaveKernelSignature.from_registry(n_domain=3),
    L2InnerNormalizer(),
]

pipeline = DescriptorPipeline(steps)
 In [5]:
descr_a = pipeline.apply(mesh_a)
descr_b = pipeline.apply(mesh_b)

Create objective function:

The optimization of the functional map can be performed considering different constraints, for example:

  1. SpectralDescriptorPreservation: the functional map needs to align spectral coefficients of descriptors.

  2. LBCommutativityEnforcing: the functional map needs to commute with the Laplace Beltrami operator.

  3. OperatorCommutativityEnforcing: the functional map needs to commute with a chosen operator defined on meshes.

Details about these energies can be found in https://dl.acm.org/doi/10.1145/3084873.3084877.

 In [6]:
factors = [
    SpectralDescriptorPreservation(
        mesh_a.basis.project(descr_a),
        mesh_b.basis.project(descr_b),
        weight=1.0,
    ),
    LBCommutativityEnforcing.from_bases(
        mesh_a.basis,
        mesh_b.basis,
        weight=1e-2,
    ),
    OperatorCommutativityEnforcing.from_multiplication(
        mesh_a.basis, descr_a, mesh_b.basis, descr_b, weight=1e-1
    ),
    OperatorCommutativityEnforcing.from_orientation(
        mesh_a, descr_a, mesh_b, descr_b, weight=1e-1
    ),
]

objective = FactorSum(factors)

Instantiate an Optimizer and solve for the functional map matrix.

 In [7]:
optimizer = ScipyMinimize(
    method="L-BFGS-B",
)
 In [8]:
x0 = gs.zeros((mesh_b.basis.spectrum_size, mesh_a.basis.spectrum_size))

res = optimizer.minimize(
    objective,
    x0,
    fun_jac=objective.gradient,
)

fmap = res.x.reshape(x0.shape)

fmap.shape
 Out [8]:
(8, 10)

Further reading#