Solving Parametric Problems Using Physics-Informed Neural Networks

Hide code cell source

import jax
import equinox as eqx

class FourierEncoding(eqx.Module):
    B: jax.Array

    @property
    def num_fourier_features(self) -> int:
        return self.B.shape[0]

    @property
    def in_size(self) -> int:
        return self.B.shape[1]
    
    @property
    def out_size(self) -> int:
        return self.B.shape[0] * 2

    def __init__(self, 
                 in_size: int, 
                 num_fourier_features: int, 
                 key: jax.random.PRNGKey, 
                 sigma: float = 1.0):
        self.B = jax.random.normal(
            key, shape=(num_fourier_features, in_size),
            dtype=jax.numpy.float32) * sigma
    
    def __call__(self, x: jax.Array, **kwargs) -> jax.Array:
        return jax.numpy.concatenate(
            [jax.numpy.cos(jax.numpy.dot(self.B, x)),
             jax.numpy.sin(jax.numpy.dot(self.B, x))],
            axis=0)

Solving Parametric Problems Using Physics-Informed Neural Networks#

For a single forward solve, an established numerical solver is usually the practical starting point. A parametric problem asks for the solution of an ODE or PDE at many parameter values. A parametric PINN shares the training effort across these repeated solves: it learns the map from parameters to solutions with one trained model. Whether that investment pays off depends on the accuracy needed and the number of subsequent evaluations.

The standard PINN approach extends to parametric problems by treating the parameters as additional inputs. Suppose that the parameters are \(\boldsymbol{\xi}\), an \(\mathbb{R}^d\)-valued random vector with density \(p(\boldsymbol{\xi})\). For each fixed \(\boldsymbol{\xi}\), the integrated squared residual depends on the trainable network parameters \(\theta\):

\[ \mathcal{L}(\theta;\boldsymbol{\xi}) = \int_{\Omega} \left\|R_{\boldsymbol{\xi}}[u_\theta](\mathbf{x})\right\|^2 \,d\mathbf{x}. \]

The idea is three-fold. First, enforce the boundary and initial conditions through the parameterization. Second, parameterize the unknown part using a neural network \(N(\mathbf{x}, \boldsymbol{\xi};\theta)\) with parameters \(\theta\). A good choice for the parameterization is this structure:

\[ N(\mathbf{x}, \boldsymbol{\xi};\theta) = \sum_{i=1}^m b_i(\boldsymbol{\xi};\theta)\phi_i(\mathbf{x};\theta). \]

This is a good choice because it resembles an expansion in a basis of functions \(\phi_i(\mathbf{x};\theta)\) with coefficients \(b_i(\boldsymbol{\xi};\theta)\). The difference here is that the basis functions are learned—they are not fixed. Third, we find the parameters \(\theta\) by minimizing the residual loss averaged over the parameter distribution:

\[ \overline{\mathcal{L}}(\theta) = \int_{\mathbb{R}^d} \mathcal{L}(\theta;\boldsymbol{\xi}) p(\boldsymbol{\xi}) \,d\boldsymbol{\xi}. \]

We can do this using Adam, or any other optimization algorithm. All we need is to express the loss as an expectation and construct an unbiased estimator of the gradient of the loss with respect to the parameters \(\theta\).

Example: Parametric Poisson Equation#

Let’s consider the following parametric Poisson equation:

\[\begin{split} \begin{aligned} -\Delta u(\mathbf{x};\xi) &= f(\mathbf{x};\xi), \quad \mathbf{x} \in \Omega, \\ u(\mathbf{x};\xi) &= 0, \quad \mathbf{x}\in \partial\Omega, \end{aligned} \end{split}\]

where \(\Omega = [0,1]^2\). The source term is:

\[ f(\mathbf{x};\xi) = 2\pi^2\xi\sin(\pi x) \sin(\pi y). \]

The exact solution to this problem is:

\[ u(\mathbf{x};\xi) = \xi\sin(\pi x) \sin(\pi y). \]

Direct substitution shows that \(\Delta u + f = 0\).

import sympy as sp
from sympy import symbols

x, y, xi = symbols('x y xi')
f = 2 * sp.pi ** 2 * xi * sp.sin(sp.pi * x) * sp.sin(sp.pi * y)
u = xi * sp.sin(sp.pi * x) * sp.sin(sp.pi * y)
res = sp.simplify(sp.diff(u, x, x) + sp.diff(u, y, y) + f)
res
\[\displaystyle 0\]

I have purposely chosen a simple problem, so that we don’t have to deal with non-dimensionalization and scaling. A parameterization that satisfies the boundary conditions is:

\[ u(x,y;\xi;\theta) = x (1 - x) y (1 - y) N(x,y;\xi;\theta), \]

where \(N(x,y;\xi;\theta)\) is a neural network with parameters \(\theta\). For the neural network, we will use the structure suggested above.

First, some useful classes to avoid code repetition.

import equinox as eqx

class ParametricModel(eqx.Module):
    """This model captures a simple structure made out of branches and trunks."""
    branch: list  # These are the b's
    trunk: list   # These are the phi's

    def __init__(self, branch, trunk):
        self.branch = branch
        self.trunk = trunk

    def __call__(self, x, xi, **kwargs):
        res = 0.0
        for b, t in zip(self.branch, self.trunk):
            res += b(xi) * t(x)
        return res


class EnforceDirichletZeroBoundarySquare(eqx.Module):
    """This is an model that enforces zero Dirichlet boundary conditions on the square."""
    neural_net: eqx.Module

    def __init__(self, neural_net):
        self.neural_net = neural_net
    
    def __call__(self, x, y, xi, **kwargs):
        return self.neural_net(jnp.array([x, y]), xi) * (1 - x) * x * (1 - y) * y

Let’s now make the actual model we will use:

import jax
import jax.numpy as jnp
import jax.random as jrandom

key = jrandom.PRNGKey(0)
key1, key2, key = jrandom.split(key, 3)

# Make the parameterization
m = 10 # number of branches and trunks
# Branches are all MLP's
branch_width = 32
branch_depth = 3
branch = [eqx.nn.MLP('scalar', 'scalar', branch_width, branch_depth, jax.nn.tanh, key=k) for k in jrandom.split(key1, m)]
# Trunks have a FourierEncoding followed by an MLP
trunk_num_fourier_features = 32
trunk_width = 32
trunk_depth = 3
trunk = [eqx.nn.Sequential([
    FourierEncoding(2, trunk_num_fourier_features, key=k),
    eqx.nn.MLP(trunk_num_fourier_features * 2, 'scalar', trunk_width, trunk_depth, jax.nn.tanh, key=k)
]) for k in jrandom.split(key2, m)]
# Combine the branches and trunks into a ParametricModel
model = EnforceDirichletZeroBoundarySquare(ParametricModel(branch, trunk))

Here is how our model looks like as a PyTree:

model

Hide code cell output

EnforceDirichletZeroBoundarySquare(
  neural_net=ParametricModel(
    branch=[
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      ),
      MLP(
        layers=(
          Linear(
            weight=f32[32,1],
            bias=f32[32],
            in_features='scalar',
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[32,32],
            bias=f32[32],
            in_features=32,
            out_features=32,
            use_bias=True
          ),
          Linear(
            weight=f32[1,32],
            bias=f32[1],
            in_features=32,
            out_features='scalar',
            use_bias=True
          )
        ),
        activation=<PjitFunction of <function tanh at 0x117a57240>>,
        final_activation=<function <lambda>>,
        use_bias=True,
        use_final_bias=True,
        in_size='scalar',
        out_size='scalar',
        width_size=32,
        depth=3
      )
    ],
    trunk=[
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      ),
      Sequential(
        layers=(
          FourierEncoding(B=f32[32,2]),
          MLP(
            layers=(
              Linear(
                weight=f32[32,64],
                bias=f32[32],
                in_features=64,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[32,32],
                bias=f32[32],
                in_features=32,
                out_features=32,
                use_bias=True
              ),
              Linear(
                weight=f32[1,32],
                bias=f32[1],
                in_features=32,
                out_features='scalar',
                use_bias=True
              )
            ),
            activation=<PjitFunction of <function tanh at 0x117a57240>>,
            final_activation=<function <lambda>>,
            use_bias=True,
            use_final_bias=True,
            in_size=64,
            out_size='scalar',
            width_size=32,
            depth=3
          )
        )
      )
    ]
  )
)

Let’s ensure our model works:

model(0.5, 0.5, 0.5)
Array(0.00453925, dtype=float32)

Let’s create our loss function:

from jax import vmap, grad

def loss_density(model, x, y, xi):
    u_xx = grad(grad(model, 0), 0)(x, y, xi)
    u_yy = grad(grad(model, 1), 1)(x, y, xi)
    f = 2 * jnp.pi ** 2 * xi * jnp.sin(jnp.pi * x) * jnp.sin(jnp.pi * y)
    return (u_xx + u_yy + f) ** 2

# This evaluate the loss at multiple xs and ys, but at a single xi
loss_density_single_xi = vmap(loss_density, in_axes=(None, 0, 0, None))

# Τhis evaluates the loss at multiple xs, ys, and xis.
# But the xs and ys are the same for all xis
loss_density_many_xis = vmap(loss_density_single_xi, in_axes=(None, None, None, 0))

# And the final loss
loss = lambda model, xs, ys, xis: jnp.mean(loss_density_many_xis(model, xs, ys, xis))

Let’s make sure it works:

xs = jnp.linspace(0, 1, 100)
ys = jnp.linspace(0, 1, 100)
xis = jnp.linspace(0, 1, 20)
res = loss_density_many_xis(model, xs, ys, xis)
res.shape
(20, 100)

And:

loss(model, xs, ys, xis)
Array(48.873215, dtype=float32)

We are not going to worry about the Fourier features. We will let the model learn them. Here is the training code:

def train_parametric_pinn(
        loss,
        model,
        key,
        optimizer,
        Lx=1.0,
        Ly=1.0,
        num_collocation_residual=128,
        num_xis = 16,
        num_iter=10_000,
        freq=1,
    ):
    """Notice that it assumes the xi's are scalar and uniformly distributed in [0, 1]."""

    @eqx.filter_jit
    def step(opt_state, model, xs, ys, xis):
        value, grads = eqx.filter_value_and_grad(loss)(model, xs, ys, xis)
        updates, opt_state = optimizer.update(grads, opt_state)
        model = eqx.apply_updates(model, updates)
        return model, opt_state, value
    
    opt_state = optimizer.init(eqx.filter(model, eqx.is_inexact_array))
    
    losses = []
    for i in range(num_iter):
        key1, key2, key3, key = jrandom.split(key, 4)
        xs = jrandom.uniform(key1, (num_collocation_residual,), maxval=Lx)
        ys = jrandom.uniform(key2, (num_collocation_residual,), maxval=Ly)
        xis = jrandom.uniform(key3, (num_xis,))
        model, opt_state, value = step(opt_state, model, xs, ys, xis)
        if i % freq == 0:
            losses.append(value)
            print(f"Step {i}, residual loss {value:.3e}")
    return model, losses

Let’s train. Be patient. It takes a while to compile the model. Once the iterations start, it is faster. It takes about 2.5 minutes to run on my machine.

import optax

optimizer = optax.adam(1e-3)

model, losses = train_parametric_pinn(
    loss, model, key, optimizer,
    num_collocation_residual=128, num_xis=2,
    num_iter=1_000, freq=1)

Hide code cell output

Step 0, residual loss 2.934e+01
Step 1, residual loss 3.251e+01
Step 2, residual loss 2.580e+00
Step 3, residual loss 1.213e+01
Step 4, residual loss 1.659e+01
Step 5, residual loss 2.704e+01
Step 6, residual loss 8.957e+00
Step 7, residual loss 3.519e+01
Step 8, residual loss 1.269e+00
Step 9, residual loss 3.543e+00
Step 10, residual loss 2.486e+01
Step 11, residual loss 1.857e+01
Step 12, residual loss 1.566e+00
Step 13, residual loss 1.784e+01
Step 14, residual loss 1.313e+01
Step 15, residual loss 1.627e+01
Step 16, residual loss 3.932e+00
Step 17, residual loss 7.830e+00
Step 18, residual loss 1.468e+01
Step 19, residual loss 1.112e+01
Step 20, residual loss 1.499e+01
Step 21, residual loss 1.085e+01
Step 22, residual loss 1.079e+01
Step 23, residual loss 8.235e+00
Step 24, residual loss 9.370e+00
Step 25, residual loss 4.011e+00
Step 26, residual loss 6.044e+00
Step 27, residual loss 2.327e+01
Step 28, residual loss 3.591e+00
Step 29, residual loss 2.529e+01
Step 30, residual loss 1.264e+01
Step 31, residual loss 9.745e+00
Step 32, residual loss 3.285e+00
Step 33, residual loss 1.315e+01
Step 34, residual loss 3.477e+00
Step 35, residual loss 8.162e+00
Step 36, residual loss 3.958e+00
Step 37, residual loss 2.255e+00
Step 38, residual loss 6.086e+00
Step 39, residual loss 9.373e+00
Step 40, residual loss 6.502e+00
Step 41, residual loss 6.540e+00
Step 42, residual loss 4.818e+00
Step 43, residual loss 6.874e+00
Step 44, residual loss 2.326e+00
Step 45, residual loss 3.054e+00
Step 46, residual loss 1.641e+00
Step 47, residual loss 2.145e+00
Step 48, residual loss 1.461e+00
Step 49, residual loss 4.993e+00
Step 50, residual loss 5.748e+00
Step 51, residual loss 3.422e+00
Step 52, residual loss 8.000e-01
Step 53, residual loss 1.174e+00
Step 54, residual loss 3.382e-01
Step 55, residual loss 5.504e-01
Step 56, residual loss 7.046e-01
Step 57, residual loss 3.842e-01
Step 58, residual loss 1.910e+00
Step 59, residual loss 1.573e+00
Step 60, residual loss 3.190e-01
Step 61, residual loss 1.457e+00
Step 62, residual loss 1.029e+00
Step 63, residual loss 2.196e+00
Step 64, residual loss 8.197e-01
Step 65, residual loss 8.187e-01
Step 66, residual loss 4.875e-01
Step 67, residual loss 5.218e-01
Step 68, residual loss 1.999e-01
Step 69, residual loss 5.049e-01
Step 70, residual loss 1.786e-01
Step 71, residual loss 5.132e-01
Step 72, residual loss 4.690e-02
Step 73, residual loss 1.463e-01
Step 74, residual loss 4.817e-02
Step 75, residual loss 5.481e-01
Step 76, residual loss 1.531e-01
Step 77, residual loss 1.071e-01
Step 78, residual loss 1.522e-01
Step 79, residual loss 2.448e-01
Step 80, residual loss 3.280e-01
Step 81, residual loss 1.469e-01
Step 82, residual loss 2.182e-01
Step 83, residual loss 2.932e-01
Step 84, residual loss 4.526e-02
Step 85, residual loss 1.440e-01
Step 86, residual loss 5.864e-02
Step 87, residual loss 2.768e-01
Step 88, residual loss 3.171e-01
Step 89, residual loss 9.688e-02
Step 90, residual loss 3.487e-02
Step 91, residual loss 2.092e-01
Step 92, residual loss 3.495e-02
Step 93, residual loss 1.543e-01
Step 94, residual loss 1.083e-01
Step 95, residual loss 1.331e-02
Step 96, residual loss 8.650e-02
Step 97, residual loss 1.310e-02
Step 98, residual loss 7.619e-02
Step 99, residual loss 3.669e-02
Step 100, residual loss 2.980e-01
Step 101, residual loss 1.877e-02
Step 102, residual loss 1.665e-02
Step 103, residual loss 1.655e-01
Step 104, residual loss 3.257e-02
Step 105, residual loss 2.689e-02
Step 106, residual loss 2.386e-02
Step 107, residual loss 2.544e-02
Step 108, residual loss 2.289e-02
Step 109, residual loss 2.808e-02
Step 110, residual loss 3.080e-02
Step 111, residual loss 6.604e-02
Step 112, residual loss 1.093e-01
Step 113, residual loss 3.790e-02
Step 114, residual loss 6.480e-03
Step 115, residual loss 1.665e-02
Step 116, residual loss 1.373e-02
Step 117, residual loss 2.057e-01
Step 118, residual loss 3.292e-02
Step 119, residual loss 1.170e-02
Step 120, residual loss 8.952e-03
Step 121, residual loss 1.664e-01
Step 122, residual loss 7.689e-03
Step 123, residual loss 6.365e-02
Step 124, residual loss 2.739e-02
Step 125, residual loss 2.662e-02
Step 126, residual loss 9.115e-03
Step 127, residual loss 5.490e-02
Step 128, residual loss 6.693e-02
Step 129, residual loss 2.371e-02
Step 130, residual loss 4.297e-02
Step 131, residual loss 7.789e-03
Step 132, residual loss 1.598e-02
Step 133, residual loss 4.490e-02
Step 134, residual loss 1.999e-01
Step 135, residual loss 5.027e-02
Step 136, residual loss 1.350e-01
Step 137, residual loss 3.621e-02
Step 138, residual loss 4.103e-02
Step 139, residual loss 1.745e-01
Step 140, residual loss 6.562e-02
Step 141, residual loss 6.557e-02
Step 142, residual loss 6.470e-02
Step 143, residual loss 6.187e-03
Step 144, residual loss 2.980e-03
Step 145, residual loss 7.282e-02
Step 146, residual loss 1.210e-01
Step 147, residual loss 2.416e-01
Step 148, residual loss 4.900e-02
Step 149, residual loss 9.105e-02
Step 150, residual loss 1.391e-01
Step 151, residual loss 3.337e-02
Step 152, residual loss 4.934e-02
Step 153, residual loss 1.568e-01
Step 154, residual loss 2.254e-01
Step 155, residual loss 5.449e-02
Step 156, residual loss 4.938e-02
Step 157, residual loss 2.394e-01
Step 158, residual loss 1.375e-01
Step 159, residual loss 1.016e-02
Step 160, residual loss 3.447e-03
Step 161, residual loss 4.202e-03
Step 162, residual loss 1.301e-02
Step 163, residual loss 8.992e-03
Step 164, residual loss 8.184e-02
Step 165, residual loss 7.517e-02
Step 166, residual loss 1.215e-02
Step 167, residual loss 3.109e-02
Step 168, residual loss 1.106e-02
Step 169, residual loss 9.858e-03
Step 170, residual loss 3.189e-02
Step 171, residual loss 5.504e-03
Step 172, residual loss 3.512e-02
Step 173, residual loss 2.231e-02
Step 174, residual loss 2.161e-02
Step 175, residual loss 5.448e-02
Step 176, residual loss 2.736e-02
Step 177, residual loss 1.470e-01
Step 178, residual loss 1.350e-01
Step 179, residual loss 5.297e-02
Step 180, residual loss 8.784e-02
Step 181, residual loss 1.020e-01
Step 182, residual loss 8.530e-02
Step 183, residual loss 1.059e-01
Step 184, residual loss 3.319e-02
Step 185, residual loss 1.360e-01
Step 186, residual loss 1.016e-02
Step 187, residual loss 9.146e-02
Step 188, residual loss 1.143e-02
Step 189, residual loss 8.378e-02
Step 190, residual loss 2.038e-02
Step 191, residual loss 2.284e-02
Step 192, residual loss 4.058e-02
Step 193, residual loss 1.920e-02
Step 194, residual loss 3.079e-02
Step 195, residual loss 6.597e-02
Step 196, residual loss 5.809e-02
Step 197, residual loss 2.164e-02
Step 198, residual loss 6.393e-02
Step 199, residual loss 8.912e-02
Step 200, residual loss 7.121e-03
Step 201, residual loss 2.292e-02
Step 202, residual loss 4.724e-02
Step 203, residual loss 3.097e-02
Step 204, residual loss 8.991e-02
Step 205, residual loss 7.162e-02
Step 206, residual loss 1.601e-02
Step 207, residual loss 5.704e-03
Step 208, residual loss 1.048e-02
Step 209, residual loss 4.623e-02
Step 210, residual loss 2.830e-01
Step 211, residual loss 1.720e-02
Step 212, residual loss 4.066e-03
Step 213, residual loss 4.528e-03
Step 214, residual loss 2.219e-02
Step 215, residual loss 5.243e-02
Step 216, residual loss 3.846e-02
Step 217, residual loss 1.394e-02
Step 218, residual loss 3.575e-02
Step 219, residual loss 3.057e-02
Step 220, residual loss 1.201e-02
Step 221, residual loss 2.265e-02
Step 222, residual loss 2.288e-02
Step 223, residual loss 6.894e-03
Step 224, residual loss 6.945e-02
Step 225, residual loss 3.594e-02
Step 226, residual loss 8.473e-03
Step 227, residual loss 1.184e-02
Step 228, residual loss 9.340e-02
Step 229, residual loss 5.109e-03
Step 230, residual loss 4.076e-02
Step 231, residual loss 2.572e-02
Step 232, residual loss 4.064e-02
Step 233, residual loss 1.757e-02
Step 234, residual loss 7.167e-02
Step 235, residual loss 7.721e-02
Step 236, residual loss 8.578e-03
Step 237, residual loss 1.705e-02
Step 238, residual loss 1.402e-01
Step 239, residual loss 8.756e-03
Step 240, residual loss 1.510e-02
Step 241, residual loss 3.044e-03
Step 242, residual loss 1.515e-02
Step 243, residual loss 1.332e-02
Step 244, residual loss 7.204e-03
Step 245, residual loss 8.575e-03
Step 246, residual loss 6.140e-02
Step 247, residual loss 2.708e-03
Step 248, residual loss 3.808e-02
Step 249, residual loss 8.502e-02
Step 250, residual loss 1.178e-02
Step 251, residual loss 3.562e-03
Step 252, residual loss 1.577e-02
Step 253, residual loss 3.876e-02
Step 254, residual loss 5.033e-03
Step 255, residual loss 6.762e-02
Step 256, residual loss 3.264e-02
Step 257, residual loss 9.334e-03
Step 258, residual loss 4.966e-03
Step 259, residual loss 4.484e-02
Step 260, residual loss 1.365e-02
Step 261, residual loss 2.328e-02
Step 262, residual loss 2.284e-01
Step 263, residual loss 1.363e-01
Step 264, residual loss 4.291e-02
Step 265, residual loss 3.706e-02
Step 266, residual loss 5.389e-02
Step 267, residual loss 4.233e-01
Step 268, residual loss 3.118e-01
Step 269, residual loss 2.997e-02
Step 270, residual loss 2.793e-03
Step 271, residual loss 6.613e-02
Step 272, residual loss 1.050e-01
Step 273, residual loss 1.692e-01
Step 274, residual loss 1.108e-01
Step 275, residual loss 9.550e-02
Step 276, residual loss 3.977e-02
Step 277, residual loss 6.561e-03
Step 278, residual loss 5.122e-02
Step 279, residual loss 9.283e-02
Step 280, residual loss 1.579e-01
Step 281, residual loss 1.015e-01
Step 282, residual loss 2.337e-02
Step 283, residual loss 4.037e-02
Step 284, residual loss 3.178e-03
Step 285, residual loss 9.373e-02
Step 286, residual loss 1.690e-02
Step 287, residual loss 2.989e-01
Step 288, residual loss 1.993e-02
Step 289, residual loss 1.692e-02
Step 290, residual loss 1.797e-02
Step 291, residual loss 2.866e-02
Step 292, residual loss 3.738e-02
Step 293, residual loss 3.820e-02
Step 294, residual loss 4.940e-02
Step 295, residual loss 1.624e-02
Step 296, residual loss 2.906e-02
Step 297, residual loss 6.961e-02
Step 298, residual loss 1.178e-02
Step 299, residual loss 2.090e-01
Step 300, residual loss 1.231e-01
Step 301, residual loss 4.116e-02
Step 302, residual loss 8.245e-02
Step 303, residual loss 1.147e-01
Step 304, residual loss 1.087e-01
Step 305, residual loss 4.074e-02
Step 306, residual loss 6.977e-02
Step 307, residual loss 4.976e-03
Step 308, residual loss 2.565e-02
Step 309, residual loss 1.180e-01
Step 310, residual loss 1.678e-02
Step 311, residual loss 1.331e-02
Step 312, residual loss 1.947e-03
Step 313, residual loss 8.289e-03
Step 314, residual loss 8.090e-02
Step 315, residual loss 2.037e-02
Step 316, residual loss 1.569e-01
Step 317, residual loss 2.087e-02
Step 318, residual loss 1.535e-01
Step 319, residual loss 4.866e-02
Step 320, residual loss 2.759e-02
Step 321, residual loss 1.058e-02
Step 322, residual loss 3.152e-02
Step 323, residual loss 1.056e-02
Step 324, residual loss 3.001e-01
Step 325, residual loss 2.364e-02
Step 326, residual loss 3.095e-03
Step 327, residual loss 9.080e-03
Step 328, residual loss 4.045e-02
Step 329, residual loss 2.712e-02
Step 330, residual loss 5.797e-02
Step 331, residual loss 1.713e-02
Step 332, residual loss 3.744e-02
Step 333, residual loss 4.904e-02
Step 334, residual loss 3.035e-02
Step 335, residual loss 2.995e-02
Step 336, residual loss 2.655e-02
Step 337, residual loss 5.828e-02
Step 338, residual loss 1.769e-02
Step 339, residual loss 1.597e-02
Step 340, residual loss 3.912e-02
Step 341, residual loss 8.941e-03
Step 342, residual loss 1.523e-01
Step 343, residual loss 3.765e-02
Step 344, residual loss 3.471e-02
Step 345, residual loss 1.128e-02
Step 346, residual loss 1.483e-02
Step 347, residual loss 6.850e-03
Step 348, residual loss 2.909e-02
Step 349, residual loss 6.764e-02
Step 350, residual loss 2.213e-02
Step 351, residual loss 1.143e-02
Step 352, residual loss 1.660e-02
Step 353, residual loss 7.003e-03
Step 354, residual loss 1.654e-02
Step 355, residual loss 1.076e-02
Step 356, residual loss 7.804e-03
Step 357, residual loss 7.895e-03
Step 358, residual loss 2.028e-02
Step 359, residual loss 8.162e-03
Step 360, residual loss 4.092e-02
Step 361, residual loss 9.974e-03
Step 362, residual loss 3.642e-02
Step 363, residual loss 4.552e-02
Step 364, residual loss 1.553e-02
Step 365, residual loss 5.911e-02
Step 366, residual loss 1.294e-02
Step 367, residual loss 2.021e-03
Step 368, residual loss 2.777e-02
Step 369, residual loss 2.218e-01
Step 370, residual loss 3.953e-02
Step 371, residual loss 9.053e-02
Step 372, residual loss 1.050e-02
Step 373, residual loss 4.431e-02
Step 374, residual loss 1.057e-02
Step 375, residual loss 1.046e-01
Step 376, residual loss 9.480e-02
Step 377, residual loss 3.111e-02
Step 378, residual loss 8.449e-03
Step 379, residual loss 2.228e-02
Step 380, residual loss 7.831e-02
Step 381, residual loss 1.008e-02
Step 382, residual loss 1.308e-02
Step 383, residual loss 9.030e-03
Step 384, residual loss 2.334e-02
Step 385, residual loss 4.733e-03
Step 386, residual loss 6.612e-02
Step 387, residual loss 5.242e-02
Step 388, residual loss 8.527e-02
Step 389, residual loss 9.902e-02
Step 390, residual loss 2.801e-02
Step 391, residual loss 3.844e-02
Step 392, residual loss 5.269e-03
Step 393, residual loss 6.190e-03
Step 394, residual loss 8.125e-02
Step 395, residual loss 6.567e-02
Step 396, residual loss 1.518e-02
Step 397, residual loss 9.562e-02
Step 398, residual loss 1.023e-02
Step 399, residual loss 6.231e-02
Step 400, residual loss 5.112e-02
Step 401, residual loss 1.123e-01
Step 402, residual loss 5.605e-02
Step 403, residual loss 1.450e-02
Step 404, residual loss 8.021e-02
Step 405, residual loss 1.076e-01
Step 406, residual loss 1.929e-02
Step 407, residual loss 1.452e-02
Step 408, residual loss 1.723e-02
Step 409, residual loss 2.466e-02
Step 410, residual loss 2.248e-02
Step 411, residual loss 1.053e-02
Step 412, residual loss 9.555e-03
Step 413, residual loss 1.971e-03
Step 414, residual loss 1.073e-02
Step 415, residual loss 8.722e-03
Step 416, residual loss 1.078e-02
Step 417, residual loss 5.803e-03
Step 418, residual loss 7.217e-02
Step 419, residual loss 1.856e-02
Step 420, residual loss 5.694e-03
Step 421, residual loss 3.340e-02
Step 422, residual loss 5.968e-02
Step 423, residual loss 2.878e-02
Step 424, residual loss 3.918e-02
Step 425, residual loss 3.558e-02
Step 426, residual loss 9.861e-03
Step 427, residual loss 1.289e-02
Step 428, residual loss 5.764e-02
Step 429, residual loss 5.111e-02
Step 430, residual loss 1.253e-02
Step 431, residual loss 2.029e-02
Step 432, residual loss 1.773e-02
Step 433, residual loss 1.954e-02
Step 434, residual loss 2.062e-02
Step 435, residual loss 1.748e-02
Step 436, residual loss 2.893e-03
Step 437, residual loss 2.340e-03
Step 438, residual loss 1.918e-02
Step 439, residual loss 3.916e-03
Step 440, residual loss 4.225e-03
Step 441, residual loss 1.462e-02
Step 442, residual loss 1.448e-01
Step 443, residual loss 1.156e-02
Step 444, residual loss 4.075e-02
Step 445, residual loss 2.717e-03
Step 446, residual loss 6.013e-02
Step 447, residual loss 2.552e-02
Step 448, residual loss 3.262e-02
Step 449, residual loss 8.223e-03
Step 450, residual loss 6.323e-02
Step 451, residual loss 4.700e-03
Step 452, residual loss 4.642e-03
Step 453, residual loss 7.226e-02
Step 454, residual loss 5.424e-02
Step 455, residual loss 2.773e-02
Step 456, residual loss 7.941e-02
Step 457, residual loss 1.373e-01
Step 458, residual loss 3.152e-02
Step 459, residual loss 7.095e-02
Step 460, residual loss 2.915e-02
Step 461, residual loss 2.061e-02
Step 462, residual loss 9.094e-02
Step 463, residual loss 8.215e-02
Step 464, residual loss 3.109e-02
Step 465, residual loss 4.854e-02
Step 466, residual loss 6.091e-03
Step 467, residual loss 2.339e-02
Step 468, residual loss 7.834e-03
Step 469, residual loss 4.545e-02
Step 470, residual loss 8.155e-02
Step 471, residual loss 5.218e-02
Step 472, residual loss 3.828e-02
Step 473, residual loss 3.043e-02
Step 474, residual loss 9.229e-03
Step 475, residual loss 7.024e-03
Step 476, residual loss 2.634e-03
Step 477, residual loss 3.659e-02
Step 478, residual loss 1.368e-02
Step 479, residual loss 6.138e-03
Step 480, residual loss 1.777e-02
Step 481, residual loss 1.671e-02
Step 482, residual loss 1.009e-02
Step 483, residual loss 2.243e-03
Step 484, residual loss 1.783e-02
Step 485, residual loss 7.037e-02
Step 486, residual loss 8.298e-02
Step 487, residual loss 3.444e-02
Step 488, residual loss 1.549e-02
Step 489, residual loss 7.999e-02
Step 490, residual loss 7.786e-02
Step 491, residual loss 5.306e-02
Step 492, residual loss 9.988e-02
Step 493, residual loss 2.919e-02
Step 494, residual loss 2.780e-03
Step 495, residual loss 1.791e-01
Step 496, residual loss 1.453e-01
Step 497, residual loss 5.552e-02
Step 498, residual loss 5.788e-02
Step 499, residual loss 4.838e-02
Step 500, residual loss 4.581e-02
Step 501, residual loss 1.250e-01
Step 502, residual loss 1.019e-01
Step 503, residual loss 4.783e-03
Step 504, residual loss 2.134e-03
Step 505, residual loss 1.204e-02
Step 506, residual loss 3.369e-01
Step 507, residual loss 1.689e-03
Step 508, residual loss 3.220e-03
Step 509, residual loss 4.953e-02
Step 510, residual loss 1.774e-02
Step 511, residual loss 4.392e-02
Step 512, residual loss 4.526e-02
Step 513, residual loss 1.635e-02
Step 514, residual loss 7.215e-04
Step 515, residual loss 1.688e-02
Step 516, residual loss 4.377e-03
Step 517, residual loss 2.597e-02
Step 518, residual loss 2.913e-02
Step 519, residual loss 9.902e-03
Step 520, residual loss 1.143e-02
Step 521, residual loss 5.655e-02
Step 522, residual loss 1.825e-02
Step 523, residual loss 2.817e-03
Step 524, residual loss 4.746e-02
Step 525, residual loss 1.322e-02
Step 526, residual loss 4.515e-03
Step 527, residual loss 4.584e-02
Step 528, residual loss 5.538e-03
Step 529, residual loss 1.413e-02
Step 530, residual loss 3.093e-03
Step 531, residual loss 4.934e-03
Step 532, residual loss 1.356e-01
Step 533, residual loss 3.530e-02
Step 534, residual loss 3.909e-03
Step 535, residual loss 1.011e-02
Step 536, residual loss 4.052e-03
Step 537, residual loss 4.837e-02
Step 538, residual loss 5.438e-03
Step 539, residual loss 3.816e-02
Step 540, residual loss 8.374e-03
Step 541, residual loss 1.957e-03
Step 542, residual loss 2.556e-03
Step 543, residual loss 1.789e-02
Step 544, residual loss 1.331e-02
Step 545, residual loss 2.329e-02
Step 546, residual loss 1.241e-03
Step 547, residual loss 1.537e-03
Step 548, residual loss 9.967e-03
Step 549, residual loss 7.827e-03
Step 550, residual loss 3.075e-03
Step 551, residual loss 1.421e-02
Step 552, residual loss 2.788e-03
Step 553, residual loss 1.084e-02
Step 554, residual loss 6.657e-03
Step 555, residual loss 8.731e-03
Step 556, residual loss 5.179e-03
Step 557, residual loss 2.583e-02
Step 558, residual loss 5.539e-03
Step 559, residual loss 8.254e-04
Step 560, residual loss 1.639e-03
Step 561, residual loss 2.857e-03
Step 562, residual loss 9.820e-03
Step 563, residual loss 9.495e-03
Step 564, residual loss 1.205e-02
Step 565, residual loss 6.885e-02
Step 566, residual loss 5.038e-04
Step 567, residual loss 9.493e-03
Step 568, residual loss 2.327e-02
Step 569, residual loss 2.728e-02
Step 570, residual loss 5.442e-03
Step 571, residual loss 1.292e-02
Step 572, residual loss 5.815e-03
Step 573, residual loss 1.642e-03
Step 574, residual loss 3.494e-02
Step 575, residual loss 2.039e-03
Step 576, residual loss 1.769e-03
Step 577, residual loss 2.664e-03
Step 578, residual loss 2.665e-02
Step 579, residual loss 8.278e-03
Step 580, residual loss 2.286e-03
Step 581, residual loss 5.267e-03
Step 582, residual loss 1.389e-02
Step 583, residual loss 4.637e-03
Step 584, residual loss 6.603e-03
Step 585, residual loss 1.038e-02
Step 586, residual loss 7.868e-04
Step 587, residual loss 2.821e-03
Step 588, residual loss 1.923e-03
Step 589, residual loss 3.386e-03
Step 590, residual loss 1.360e-01
Step 591, residual loss 9.835e-03
Step 592, residual loss 4.252e-03
Step 593, residual loss 3.673e-02
Step 594, residual loss 4.303e-02
Step 595, residual loss 2.693e-02
Step 596, residual loss 7.311e-03
Step 597, residual loss 6.266e-02
Step 598, residual loss 2.554e-02
Step 599, residual loss 1.470e-03
Step 600, residual loss 1.240e-02
Step 601, residual loss 1.984e-02
Step 602, residual loss 1.438e-02
Step 603, residual loss 5.321e-03
Step 604, residual loss 6.457e-02
Step 605, residual loss 5.343e-03
Step 606, residual loss 1.387e-02
Step 607, residual loss 1.548e-03
Step 608, residual loss 8.360e-03
Step 609, residual loss 2.819e-03
Step 610, residual loss 5.241e-03
Step 611, residual loss 3.620e-03
Step 612, residual loss 8.262e-03
Step 613, residual loss 5.045e-03
Step 614, residual loss 4.173e-03
Step 615, residual loss 4.857e-02
Step 616, residual loss 3.353e-03
Step 617, residual loss 1.408e-03
Step 618, residual loss 1.260e-02
Step 619, residual loss 6.391e-04
Step 620, residual loss 3.927e-03
Step 621, residual loss 1.195e-02
Step 622, residual loss 8.908e-03
Step 623, residual loss 1.988e-03
Step 624, residual loss 5.978e-03
Step 625, residual loss 1.046e-02
Step 626, residual loss 2.064e-03
Step 627, residual loss 1.088e-02
Step 628, residual loss 1.752e-03
Step 629, residual loss 1.743e-03
Step 630, residual loss 1.548e-02
Step 631, residual loss 2.360e-02
Step 632, residual loss 2.972e-02
Step 633, residual loss 3.056e-02
Step 634, residual loss 9.034e-03
Step 635, residual loss 2.118e-02
Step 636, residual loss 1.058e-03
Step 637, residual loss 4.093e-03
Step 638, residual loss 1.589e-03
Step 639, residual loss 3.294e-02
Step 640, residual loss 5.039e-03
Step 641, residual loss 6.968e-03
Step 642, residual loss 1.785e-03
Step 643, residual loss 1.082e-02
Step 644, residual loss 2.555e-02
Step 645, residual loss 2.299e-02
Step 646, residual loss 4.958e-03
Step 647, residual loss 7.102e-03
Step 648, residual loss 1.302e-02
Step 649, residual loss 6.903e-03
Step 650, residual loss 4.947e-03
Step 651, residual loss 3.811e-03
Step 652, residual loss 1.013e-03
Step 653, residual loss 1.222e-02
Step 654, residual loss 7.228e-02
Step 655, residual loss 1.691e-03
Step 656, residual loss 2.093e-02
Step 657, residual loss 3.455e-03
Step 658, residual loss 3.246e-02
Step 659, residual loss 2.397e-02
Step 660, residual loss 6.952e-02
Step 661, residual loss 8.837e-02
Step 662, residual loss 2.279e-02
Step 663, residual loss 3.763e-02
Step 664, residual loss 3.712e-02
Step 665, residual loss 7.099e-02
Step 666, residual loss 3.299e-02
Step 667, residual loss 2.081e-03
Step 668, residual loss 5.202e-03
Step 669, residual loss 1.417e-02
Step 670, residual loss 4.896e-02
Step 671, residual loss 4.317e-02
Step 672, residual loss 1.385e-02
Step 673, residual loss 1.057e-03
Step 674, residual loss 9.787e-02
Step 675, residual loss 7.663e-03
Step 676, residual loss 4.061e-03
Step 677, residual loss 3.067e-03
Step 678, residual loss 2.548e-03
Step 679, residual loss 4.095e-03
Step 680, residual loss 6.093e-03
Step 681, residual loss 4.136e-03
Step 682, residual loss 1.313e-02
Step 683, residual loss 4.147e-03
Step 684, residual loss 8.072e-03
Step 685, residual loss 5.773e-03
Step 686, residual loss 6.166e-03
Step 687, residual loss 1.021e-02
Step 688, residual loss 5.614e-03
Step 689, residual loss 2.301e-02
Step 690, residual loss 5.721e-04
Step 691, residual loss 9.685e-03
Step 692, residual loss 5.157e-03
Step 693, residual loss 4.162e-02
Step 694, residual loss 1.520e-02
Step 695, residual loss 4.552e-03
Step 696, residual loss 8.829e-03
Step 697, residual loss 1.873e-02
Step 698, residual loss 9.986e-04
Step 699, residual loss 1.562e-02
Step 700, residual loss 3.220e-02
Step 701, residual loss 2.744e-02
Step 702, residual loss 4.300e-02
Step 703, residual loss 2.573e-03
Step 704, residual loss 1.922e-02
Step 705, residual loss 5.846e-02
Step 706, residual loss 1.969e-02
Step 707, residual loss 5.125e-03
Step 708, residual loss 1.256e-02
Step 709, residual loss 1.339e-02
Step 710, residual loss 8.386e-03
Step 711, residual loss 5.285e-02
Step 712, residual loss 4.809e-02
Step 713, residual loss 7.917e-03
Step 714, residual loss 4.317e-03
Step 715, residual loss 2.258e-03
Step 716, residual loss 2.468e-02
Step 717, residual loss 6.321e-02
Step 718, residual loss 5.603e-02
Step 719, residual loss 6.698e-04
Step 720, residual loss 1.807e-02
Step 721, residual loss 2.723e-02
Step 722, residual loss 1.894e-02
Step 723, residual loss 8.800e-03
Step 724, residual loss 1.062e-03
Step 725, residual loss 7.683e-04
Step 726, residual loss 4.834e-03
Step 727, residual loss 8.841e-03
Step 728, residual loss 1.244e-02
Step 729, residual loss 1.240e-02
Step 730, residual loss 9.393e-03
Step 731, residual loss 2.776e-03
Step 732, residual loss 1.684e-03
Step 733, residual loss 4.987e-02
Step 734, residual loss 3.579e-03
Step 735, residual loss 3.553e-03
Step 736, residual loss 2.308e-03
Step 737, residual loss 3.687e-03
Step 738, residual loss 2.383e-03
Step 739, residual loss 9.882e-03
Step 740, residual loss 5.243e-03
Step 741, residual loss 2.100e-02
Step 742, residual loss 3.961e-03
Step 743, residual loss 6.311e-03
Step 744, residual loss 7.387e-03
Step 745, residual loss 1.103e-03
Step 746, residual loss 5.534e-03
Step 747, residual loss 4.620e-03
Step 748, residual loss 4.167e-03
Step 749, residual loss 2.432e-02
Step 750, residual loss 2.690e-02
Step 751, residual loss 1.098e-02
Step 752, residual loss 4.672e-03
Step 753, residual loss 1.227e-02
Step 754, residual loss 5.710e-03
Step 755, residual loss 2.048e-02
Step 756, residual loss 1.656e-02
Step 757, residual loss 3.075e-02
Step 758, residual loss 2.315e-02
Step 759, residual loss 4.182e-02
Step 760, residual loss 3.819e-02
Step 761, residual loss 3.917e-02
Step 762, residual loss 2.767e-03
Step 763, residual loss 4.719e-03
Step 764, residual loss 3.955e-02
Step 765, residual loss 6.892e-02
Step 766, residual loss 1.894e-03
Step 767, residual loss 2.399e-02
Step 768, residual loss 8.890e-03
Step 769, residual loss 2.067e-02
Step 770, residual loss 1.233e-02
Step 771, residual loss 7.279e-03
Step 772, residual loss 1.131e-02
Step 773, residual loss 9.262e-03
Step 774, residual loss 1.045e-02
Step 775, residual loss 6.042e-03
Step 776, residual loss 5.044e-03
Step 777, residual loss 1.132e-02
Step 778, residual loss 6.353e-03
Step 779, residual loss 1.554e-02
Step 780, residual loss 1.085e-02
Step 781, residual loss 1.277e-02
Step 782, residual loss 1.802e-03
Step 783, residual loss 1.235e-01
Step 784, residual loss 9.309e-04
Step 785, residual loss 1.828e-03
Step 786, residual loss 5.775e-02
Step 787, residual loss 1.065e-03
Step 788, residual loss 1.311e-02
Step 789, residual loss 1.825e-02
Step 790, residual loss 2.312e-02
Step 791, residual loss 1.189e-02
Step 792, residual loss 4.851e-03
Step 793, residual loss 9.649e-03
Step 794, residual loss 1.647e-02
Step 795, residual loss 2.562e-03
Step 796, residual loss 3.368e-03
Step 797, residual loss 7.998e-03
Step 798, residual loss 3.334e-03
Step 799, residual loss 1.542e-02
Step 800, residual loss 2.252e-03
Step 801, residual loss 2.285e-03
Step 802, residual loss 5.632e-03
Step 803, residual loss 5.725e-02
Step 804, residual loss 1.304e-03
Step 805, residual loss 1.616e-03
Step 806, residual loss 1.772e-02
Step 807, residual loss 3.066e-03
Step 808, residual loss 4.907e-02
Step 809, residual loss 3.182e-03
Step 810, residual loss 1.455e-03
Step 811, residual loss 5.892e-02
Step 812, residual loss 3.962e-02
Step 813, residual loss 1.980e-02
Step 814, residual loss 2.694e-03
Step 815, residual loss 2.062e-02
Step 816, residual loss 2.701e-02
Step 817, residual loss 2.988e-02
Step 818, residual loss 2.285e-03
Step 819, residual loss 3.167e-03
Step 820, residual loss 1.915e-02
Step 821, residual loss 5.507e-02
Step 822, residual loss 1.073e-02
Step 823, residual loss 6.670e-04
Step 824, residual loss 5.778e-03
Step 825, residual loss 1.617e-02
Step 826, residual loss 1.261e-02
Step 827, residual loss 1.636e-02
Step 828, residual loss 1.533e-03
Step 829, residual loss 1.915e-02
Step 830, residual loss 1.628e-02
Step 831, residual loss 5.637e-03
Step 832, residual loss 4.513e-03
Step 833, residual loss 3.547e-02
Step 834, residual loss 2.316e-02
Step 835, residual loss 4.620e-02
Step 836, residual loss 1.856e-02
Step 837, residual loss 1.474e-02
Step 838, residual loss 9.622e-03
Step 839, residual loss 5.188e-02
Step 840, residual loss 1.193e-03
Step 841, residual loss 1.017e-02
Step 842, residual loss 2.319e-02
Step 843, residual loss 5.988e-02
Step 844, residual loss 4.282e-02
Step 845, residual loss 3.866e-02
Step 846, residual loss 8.753e-03
Step 847, residual loss 8.269e-03
Step 848, residual loss 2.452e-02
Step 849, residual loss 4.141e-02
Step 850, residual loss 1.405e-02
Step 851, residual loss 1.935e-02
Step 852, residual loss 4.558e-03
Step 853, residual loss 1.028e-02
Step 854, residual loss 2.839e-02
Step 855, residual loss 3.559e-02
Step 856, residual loss 2.184e-03
Step 857, residual loss 1.973e-03
Step 858, residual loss 6.280e-02
Step 859, residual loss 5.723e-03
Step 860, residual loss 1.106e-03
Step 861, residual loss 7.383e-03
Step 862, residual loss 7.106e-03
Step 863, residual loss 3.115e-03
Step 864, residual loss 1.412e-02
Step 865, residual loss 7.085e-03
Step 866, residual loss 1.990e-03
Step 867, residual loss 8.282e-03
Step 868, residual loss 9.692e-03
Step 869, residual loss 1.026e-03
Step 870, residual loss 1.522e-02
Step 871, residual loss 1.900e-02
Step 872, residual loss 1.085e-02
Step 873, residual loss 1.437e-03
Step 874, residual loss 1.066e-02
Step 875, residual loss 7.488e-03
Step 876, residual loss 8.720e-02
Step 877, residual loss 2.049e-03
Step 878, residual loss 1.787e-02
Step 879, residual loss 2.008e-02
Step 880, residual loss 9.027e-03
Step 881, residual loss 9.832e-03
Step 882, residual loss 5.913e-03
Step 883, residual loss 1.086e-03
Step 884, residual loss 2.429e-02
Step 885, residual loss 1.576e-02
Step 886, residual loss 7.317e-03
Step 887, residual loss 5.364e-03
Step 888, residual loss 2.129e-02
Step 889, residual loss 3.751e-03
Step 890, residual loss 7.249e-03
Step 891, residual loss 4.481e-03
Step 892, residual loss 2.104e-02
Step 893, residual loss 1.980e-03
Step 894, residual loss 2.817e-03
Step 895, residual loss 2.864e-03
Step 896, residual loss 3.409e-02
Step 897, residual loss 6.145e-04
Step 898, residual loss 5.931e-03
Step 899, residual loss 1.757e-02
Step 900, residual loss 9.128e-03
Step 901, residual loss 1.074e-03
Step 902, residual loss 6.492e-03
Step 903, residual loss 5.929e-03
Step 904, residual loss 3.693e-03
Step 905, residual loss 1.874e-02
Step 906, residual loss 1.538e-03
Step 907, residual loss 6.637e-03
Step 908, residual loss 7.022e-03
Step 909, residual loss 3.031e-03
Step 910, residual loss 7.563e-04
Step 911, residual loss 9.583e-04
Step 912, residual loss 1.861e-03
Step 913, residual loss 1.185e-03
Step 914, residual loss 2.239e-02
Step 915, residual loss 6.356e-03
Step 916, residual loss 2.933e-03
Step 917, residual loss 9.498e-03
Step 918, residual loss 2.161e-03
Step 919, residual loss 2.392e-03
Step 920, residual loss 1.379e-02
Step 921, residual loss 6.704e-03
Step 922, residual loss 1.638e-02
Step 923, residual loss 6.678e-03
Step 924, residual loss 7.254e-04
Step 925, residual loss 1.307e-03
Step 926, residual loss 8.416e-04
Step 927, residual loss 2.615e-02
Step 928, residual loss 5.143e-03
Step 929, residual loss 8.037e-03
Step 930, residual loss 1.166e-02
Step 931, residual loss 3.049e-02
Step 932, residual loss 9.853e-04
Step 933, residual loss 1.588e-03
Step 934, residual loss 4.229e-03
Step 935, residual loss 5.584e-03
Step 936, residual loss 2.160e-03
Step 937, residual loss 1.601e-02
Step 938, residual loss 2.990e-03
Step 939, residual loss 1.366e-02
Step 940, residual loss 4.375e-03
Step 941, residual loss 1.147e-02
Step 942, residual loss 3.803e-04
Step 943, residual loss 2.088e-02
Step 944, residual loss 1.921e-02
Step 945, residual loss 7.166e-03
Step 946, residual loss 1.279e-02
Step 947, residual loss 2.753e-02
Step 948, residual loss 3.278e-02
Step 949, residual loss 2.493e-03
Step 950, residual loss 1.000e-02
Step 951, residual loss 1.044e-02
Step 952, residual loss 1.126e-02
Step 953, residual loss 1.452e-03
Step 954, residual loss 2.552e-02
Step 955, residual loss 1.880e-03
Step 956, residual loss 3.906e-03
Step 957, residual loss 2.200e-03
Step 958, residual loss 1.683e-03
Step 959, residual loss 4.479e-03
Step 960, residual loss 2.250e-02
Step 961, residual loss 2.373e-03
Step 962, residual loss 4.336e-03
Step 963, residual loss 6.559e-03
Step 964, residual loss 4.696e-03
Step 965, residual loss 3.882e-02
Step 966, residual loss 1.339e-02
Step 967, residual loss 1.763e-03
Step 968, residual loss 4.061e-02
Step 969, residual loss 1.166e-02
Step 970, residual loss 2.396e-02
Step 971, residual loss 1.669e-03
Step 972, residual loss 2.009e-03
Step 973, residual loss 1.083e-02
Step 974, residual loss 1.071e-02
Step 975, residual loss 5.586e-02
Step 976, residual loss 5.062e-03
Step 977, residual loss 1.952e-02
Step 978, residual loss 5.170e-03
Step 979, residual loss 4.375e-02
Step 980, residual loss 3.481e-02
Step 981, residual loss 1.147e-02
Step 982, residual loss 1.219e-02
Step 983, residual loss 1.923e-02
Step 984, residual loss 1.634e-02
Step 985, residual loss 1.263e-02
Step 986, residual loss 2.723e-03
Step 987, residual loss 1.917e-03
Step 988, residual loss 2.373e-02
Step 989, residual loss 3.366e-02
Step 990, residual loss 9.081e-03
Step 991, residual loss 7.623e-04
Step 992, residual loss 6.797e-03
Step 993, residual loss 7.661e-03
Step 994, residual loss 1.393e-02
Step 995, residual loss 1.157e-02
Step 996, residual loss 4.650e-04
Step 997, residual loss 1.217e-03
Step 998, residual loss 1.686e-02
Step 999, residual loss 2.141e-02

Let’s visualize the loss:

fig, ax = new_figure("half_standard")
steps = np.arange(len(losses))
ax.plot(steps, losses, color="black", linewidth=1.2)
ax.set_yscale("log")
ax.set(xlabel="optimization step", ylabel="residual loss")
finalize_axes(ax)
plt.show()
A black curve on a logarithmic vertical axis shows the parametric PINN residual loss decreasing over 1000 optimization steps.

Fig. 44 Residual loss during training of the parametric PINN. The logarithmic vertical axis exposes the overall decrease and the stochastic fluctuations.#

Let’s compare the prediction and exact solution at five reproducible values of \(\xi\):

rng = np.random.default_rng(1234)

@eqx.filter_jit
def predict_on_grid(model, x, y, xi):
    return vmap(lambda x_i, y_i: model(x_i, y_i, xi))(x, y)

exact_on_grid = jax.jit(
    vmap(
        lambda x, y, xi: xi * jnp.sin(jnp.pi * x) * jnp.sin(jnp.pi * y),
        in_axes=(0, 0, None),
    )
)

xs = np.linspace(0, 1, 100)
ys = np.linspace(0, 1, 100)
X, Y = np.meshgrid(xs, ys)
X_flat = X.flatten()
Y_flat = Y.flatten()

error_levels = np.linspace(0.0, 0.2, 9)
parameter_values = np.sort(rng.random(5))

for xi in parameter_values:
    fig = plt.figure(
        figsize=FIGURE_SIZES["full_landscape"], layout="constrained"
    )
    grid = fig.add_gridspec(
        2, 3, height_ratios=(1.0, 0.09), hspace=0.08, wspace=0.10
    )
    axes = np.array([fig.add_subplot(grid[0, i]) for i in range(3)])
    solution_cax = fig.add_subplot(grid[1, :2])
    error_cax = fig.add_subplot(grid[1, 2])
    Z_p = np.asarray(
        predict_on_grid(model, X_flat, Y_flat, xi)
    ).reshape(X.shape)
    Z_t = np.asarray(exact_on_grid(X_flat, Y_flat, xi)).reshape(X.shape)
    solution_levels = np.linspace(0.0, float(xi), 9)

    c_solution = grayscale_contourf(
        axes[0], X, Y, Z_p, levels=solution_levels, extend="both"
    )
    grayscale_contourf(
        axes[1], X, Y, Z_t, levels=solution_levels
    )
    denominator = float(np.max(np.abs(Z_t)))
    relative_error = np.abs(Z_p - Z_t) / denominator
    c_error = grayscale_contourf(
        axes[2], X, Y, relative_error, levels=error_levels, extend="max"
    )

    axes[0].set_title("PINN")
    axes[1].set_title("exact")
    axes[2].set_title("normalized error")
    for axis in axes:
        axis.set(xlabel=r'$x$', xticks=[0.0, 0.5, 1.0], yticks=[0.0, 0.5, 1.0])
        axis.set_aspect("equal")
    axes[0].set_ylabel(r'$y$')
    axes[1].tick_params(labelleft=False)
    axes[2].tick_params(labelleft=False)

    fig.colorbar(
        c_solution, cax=solution_cax, orientation="horizontal",
        ticks=solution_levels[::2], format="%.2f", label=r'$u(x,y;\xi)$'
    )
    fig.colorbar(
        c_error, cax=error_cax, orientation="horizontal",
        ticks=error_levels[::4], format="%.2f", label="normalized error"
    )
    fig.suptitle(fr'$\xi = {xi:.3f}$', fontsize=9)
    label_panels(axes)
    finalize_axes(axes, keep_box=True)
    plt.show()
Three grayscale contour panels compare the parametric PINN prediction, the manufactured solution, and their normalized pointwise absolute error for the displayed parameter value.

Fig. 45 Parametric Poisson solution at the parameter value printed above the panels. (a) PINN prediction. (b) Manufactured solution. (c) Pointwise absolute error divided by the maximum manufactured solution. The first two panels share one solution scale; the third uses a fixed error scale from 0 to 0.2. At this parameter value the normalized error stays below the lowest level, 0.025, so panel (c) is uniformly light. Black contour boundaries preserve the levels in print.#

Three grayscale contour panels compare the parametric PINN prediction, the manufactured solution, and their normalized pointwise absolute error for the displayed parameter value.

Fig. 46 Parametric Poisson solution at the parameter value printed above the panels. (a) PINN prediction. (b) Manufactured solution. (c) Pointwise absolute error divided by the maximum manufactured solution. The first two panels share one solution scale; the third uses a fixed error scale from 0 to 0.2. At this parameter value the normalized error stays below the lowest level, 0.025, so panel (c) is uniformly light. Black contour boundaries preserve the levels in print.#

Three grayscale contour panels compare the parametric PINN prediction, the manufactured solution, and their normalized pointwise absolute error for the displayed parameter value.

Fig. 47 Parametric Poisson solution at the parameter value printed above the panels. (a) PINN prediction. (b) Manufactured solution. (c) Pointwise absolute error divided by the maximum manufactured solution. The first two panels share one solution scale; the third uses a fixed error scale from 0 to 0.2. At this parameter value the normalized error stays below the lowest level, 0.025, so panel (c) is uniformly light. Black contour boundaries preserve the levels in print.#

Three grayscale contour panels compare the parametric PINN prediction, the manufactured solution, and their normalized pointwise absolute error for the displayed parameter value.

Fig. 48 Parametric Poisson solution at the parameter value printed above the panels. (a) PINN prediction. (b) Manufactured solution. (c) Pointwise absolute error divided by the maximum manufactured solution. The first two panels share one solution scale; the third uses a fixed error scale from 0 to 0.2. At this parameter value the normalized error stays below the lowest level, 0.025, so panel (c) is uniformly light. Black contour boundaries preserve the levels in print.#

Three grayscale contour panels compare the parametric PINN prediction, the manufactured solution, and their normalized pointwise absolute error for the displayed parameter value.

Fig. 49 Parametric Poisson solution at the parameter value printed above the panels. (a) PINN prediction. (b) Manufactured solution. (c) Pointwise absolute error divided by the maximum manufactured solution. The first two panels share one solution scale; the third uses a fixed error scale from 0 to 0.2. At this parameter value the normalized error stays below the lowest level, 0.025, so panel (c) is uniformly light. Black contour boundaries preserve the levels in print.#

The model captures the main spatial structure at this parameter value. Further training may reduce the remaining discrepancy; a held-out comparison is needed to check whether it does.