Cheeger problems#

\[\renewcommand{\div}{\operatorname{div}} \newcommand{\bsig}{\boldsymbol{\sigma}}\]

Dual problem#

The Cheeger problem dual formulation reads as:

\[\begin{split}\begin{equation} \begin{array}{rl} \displaystyle{\sup_{\lambda\in \mathbb{R}, \bsig\in W}} & \displaystyle{\lambda} \\ \text{s.t.} & \lambda f = \div\bsig \quad \text{in }\Omega \\ & \|\bsig\|_2 \leq 1 \end{array} \label{Cheeger-dual} \end{equation}\end{split}\]

A natural discretization strategy for such a problem is to use \(H(\div)\)-conforming elements such as the Raviart-Thomas element \(RT_1\). Two minimization variables are therefore defined: \(\lambda\) belonging to a scalar Real function space and \(\bsig \in RT_1\). Since for \(\bsig \in RT_1\), \(\div \bsig \in \mathbb{P}^0\), we write the constraint equation using \(\mathbb{P}^0\) Lagrange multipliers:

Implementation#

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Dual convex optimization formulation of the Cheeger problem.

Discretization of the dual field sigma using Raviart-Thomas elements.

@author: Jeremy Bleyer, Ecole des Ponts ParisTech,
Laboratoire Navier (ENPC, Univ Gustave Eiffel, CNRS, UMR 8205)
@email: jeremy.bleyer@enpc.fr
"""
from ufl import div
from dolfin import UnitSquareMesh, FunctionSpace, Constant, dx, plot
from fenics_optim import MosekProblem, L2Ball
import matplotlib.pyplot as plt

N = 25
mesh = UnitSquareMesh(N, N, "crossed")

VRT = FunctionSpace(mesh, "RT", 1)
R = FunctionSpace(mesh, "Real", 0)
# FunctionSpace for the Lagrange multiplier associated with "constraint"
VDG0 = FunctionSpace(mesh, "DG", 0)

prob = MosekProblem("Cheeger dual")
lamb, sig = prob.add_var([R, VRT])

f = Constant(1.0)


def constraint(u):
    """Equilibrium constraint."""
    return (lamb * f - div(sig)) * u * dx


prob.add_eq_constraint(VDG0, A=constraint, name="u")

# We add the L2-Ball constraint ||sigma||_2 <= 1
F = L2Ball(sig, quadrature_scheme="vertex")
prob.add_convex_term(F)

# objective function is c(lambda, sigma) = lambda = [1, 0]*(lambda, sigma)
prob.add_obj_func(lamb * dx)

prob.parameters["presolve"] = True

# we enforce maximization
prob.optimize(sense="max")

# Dual variables are in prob.y
# The first constraint defined being lambda*f+div(sig)=0
# we get its associated Lagrange multiplier u using its name
u = prob.get_lagrange_multiplier("u")

plt.figure()
plot(u, cmap="Blues")
plt.show()
../../_images/Cheeger_RT.png