Note
Go to the end to download the full example code.
3.4.2 Divergence Operator
This tutorial introduces the divergence operator in CodPy, based on the transpose of the gradient operator. The divergence is a key concept in vector calculus and is fundamental in the formulation of many differential equations.
Overview
Let \(\nabla_k\) denote the kernel-based gradient operator defined as:
- where:
\(X \in \mathbb{R}^{N_x \times D}\) is the training set,
\(Y \in \mathbb{R}^{N_y \times D}\) is usually set equal to \(X\),
\(Z \in \mathbb{R}^{N_z \times D}\) is the evaluation set,
\(K(X, Y)^{-1}\) is the pseudo-inverse of the kernel Gram matrix of size \(\mathbb{R}^{N_x \times N_y}\),
\(\nabla_k \in \mathbb{R}^{D \times N_z \times N_y}\) is the kernel gradient with respect to \(Z\).
We define the transpose operator \(\nabla_k^T\) as the adjoint of the gradient operator under the standard \(L^2\) inner product. This transpose is consistent with the divergence operator and satisfies:
for any test functions \(f(X) \in \mathbb{R}^{N_x \times D_f}\) and \(g(Z) \in \mathbb{R}^{D \times N_z \times D_f}\).
Divergence Operator Definition
To compute the transpose of the operator, we consider the structure of the gradient operator \(\nabla_k\). Then its transpose is given by:
- where:
\(K(X, Y)^{-T}\) is the transpose of the inverse kernel matrix,
\(\nabla_k^T \in \mathbb{R}^{N_y \times (N_z D)}\) is the transpose of the kernel gradient matrix.
This operator allows one to compute divergence-like quantities in RKHS, and can be used for problems involving conservation laws, variational formulations, etc.
# Importing necessary modules
import os
import sys
from matplotlib import pyplot as plt
curr_f = os.path.join(os.getcwd(), "codpy-book", "utils")
sys.path.insert(0, curr_f)
import numpy as np
# from codpy.plotting import plot1D
# Lets import multi_plot function from codpy utils
from codpy.plot_utils import multi_plot
# Define the sinusoidal function
def periodic_fun(x):
"""
A sinusoidal function that generates a sum of sines based on the input ``x``.
"""
from math import pi
sinss = np.cos(2 * x * pi)
if x.ndim == 1:
sinss = np.prod(sinss, axis=0)
ress = np.sum(x, axis=0)
else:
sinss = np.prod(sinss, axis=1)
ress = np.sum(x, axis=1)
return ress + sinss
def nabla_my_fun(x):
from math import pi
import numpy as np
sinss = np.cos(2 * x * pi)
if x.ndim == 1:
sinss = np.prod(sinss, axis=0)
D = len(x)
out = np.ones((D))
def helper(d):
out[d] += 2.0 * sinss * pi * np.sin(2 * x[d] * pi) / np.cos(2 * x[d] * pi)
[helper(d) for d in range(0, D)]
else:
sinss = np.prod(sinss, axis=1)
N = x.shape[0]
D = x.shape[1]
out = np.ones((N, D))
def helper(d):
out[:, d] += (
2.0 * sinss * pi * np.sin(2 * x[:, d] * pi) / np.cos(2 * x[:, d] * pi)
)
[helper(d) for d in range(0, D)]
return out
# Function to generate periodic data
def generate_periodic_data_cartesian(size_x, size_z, fun=None, nabla_fun=None):
"""
Generates 2D structured Cartesian grid data for x and z domains,
and evaluates a given function and optionally its gradient.
Parameters:
- size_x: number of points per axis for x (grid will be size_x^2)
- size_z: number of points per axis for z (grid will be size_z^2)
- fun: function to evaluate at each point
- nabla_fun: optional gradient function to evaluate
Returns:
- x, z: 2D Cartesian grids of shape (N, 2)
- fx, fz: function values at x and z
- nabla_fx, nabla_fz (if nabla_fun is provided)
"""
def cartesian_grid(size, box):
lin = [np.linspace(box[0, d], box[1, d], size) for d in range(2)]
X, Y = np.meshgrid(*lin)
return np.stack([X.ravel(), Y.ravel()], axis=1)
# Define domain boxes
X_box = np.array([[-1, -1], [1, 1]])
Z_box = np.array([[-1.5, -1.5], [1.5, 1.5]])
# Generate Cartesian grids
x = cartesian_grid(size_x, X_box)
z = cartesian_grid(size_z, Z_box)
# Function evaluations
fx = fun(x).reshape(-1, 1) if fun else None
fz = fun(z).reshape(-1, 1) if fun else None
if nabla_fun:
nabla_fx = nabla_fun(x)
nabla_fz = nabla_fun(z)
return x, fx, z, fz, nabla_fx, nabla_fz
return x, fx, z, fz
# Lets define helper function to plot 3D projection of the function
def plot_trisurf(xfx, ax, legend="", elev=90, azim=-100, **kwargs):
from matplotlib import cm
"""
Helper function to plot a 3D surface using a trisurf plot.
Parameters:
- xfx: A tuple containing the x-coordinates (2D points) and their
corresponding function values.
- ax: The matplotlib axis object for plotting.
- legend: The legend/title for the plot.
- elev, azim: Elevation and azimuth angles for the 3D view.
- kwargs: Additional keyword arguments for further customization.
"""
xp, fxp = xfx[0], xfx[1]
x, fx = xp, fxp
X, Y = x[:, 0], x[:, 1]
Z = fx.flatten()
ax.plot_trisurf(X, Y, Z, antialiased=False, cmap=cm.jet)
ax.view_init(azim=azim, elev=elev)
ax.title.set_text(legend)
# import CodPy's core module and Kernel class
from codpy import core
from codpy.kernel import Kernel
Divergence operator
def fun_nablaTnabla(size_x=50, size_y=50):
"""
Runs the experiment applying CodPy and SciPy models on the data and plots the results.
Parameters:
- data_x: List of generated x arrays.
- data_fx: List of function values corresponding to each x.
- data_z: List of generated z arrays.
"""
# Apply CodPy and SciPy models for each (x, fx, z) pair
x, fx, z, fz, _, nabla_fz = generate_periodic_data_cartesian(
size_x, size_y, periodic_fun, nabla_fun=nabla_my_fun
)
nabla_fz = nabla_fz.reshape(-1, 2, 1)
kernel_ptr = Kernel(
x=x, fx=fx, set_kernel=core.kernel_setter("gaussianper", None,0, 1e-8)
).get_kernel()
nabla_f_x = core.DiffOps.nabla(
x=x,
z=x,
fx=fx,
kernel_ptr=kernel_ptr,
order=2,
regularization=1e-8,
)
nabla_t_f_x = core.DiffOps.nabla_t(
x=x,
y=x,
z=x,
fz=nabla_f_x,
kernel_ptr=kernel_ptr,
order=2,
regularization=1e-8,
)
nablaTnabla_f_x = core.DiffOps.nabla_t_nabla(
x=x,
y=x,
fx=fx,
kernel_ptr=kernel_ptr,
order=2,
regularization=1e-8,
)
multi_plot(
[
(x, nabla_t_f_x),
(x, nablaTnabla_f_x),
],
plot_trisurf,
projection="3d",
mp_max_items=4,
mp_ncols=4,
mp_nrows=1,
mp_figsize=(12, 3),
mp_title=[
"Comparison of the outer product of the gradient to Laplace operator"
],
)
plt.show()
fun_nablaTnabla()
pass
![['Comparison of the outer product of the gradient to Laplace operator']](../_images/sphx_glr_ch3_4_2_001.png)
Total running time of the script: (0 minutes 3.609 seconds)