Domain: Physics — Physics (general)

Problem:
Consider two linearly coupled non-uniform classical strings on $x\in[-50,50]$ with Dirichlet boundary conditions. Each string $i=1,2$ is governed by a standard self-adjoint Sturm–Liouville operator with variable mass $\mu_i(x)$ and potential $V_i(x)$, coupled symmetrically by $W(x)$. Use a conservative midpoint finite-difference discretization with a uniform grid of $N=2000$ points (including endpoints) that preserves self-adjointness, and form the full $2\times2$ block operator. The parameter profiles are
$$\mu_1(x)=1+0.5e^{-x^2/4},\quad \mu_2(x)=1+0.3e^{-(x-2)^2/3},$$
$$V_1(x)=-5\,\mathrm{sech}^2\!\left(\tfrac{x}{1.5}\right)$$, $$V_2(x)=-4\,\mathrm{sech}^2\!\left(\tfrac{x-2}{1.2}\right)$$, 
$$ W(x)=1.2e^{-x^2/8}$$. Compute the total number of eigenvalues $E< 0$ of this coupled operator using a sparse eigensolver and print that integer as the final output.

Answer: 6

Solution code:
import numpy as np
from scipy import sparse
from scipy.sparse import linalg as sp_linalg

# Grid parameters
N = 2000
x = np.linspace(-50, 50, N)
h = 100 / (N - 1)

# Parameter profiles
mu1 = 1 + 0.5 * np.exp(-x**2 / 4)
mu2 = 1 + 0.3 * np.exp(-(x - 2)**2 / 3)
V1 = -5 / np.cosh(x / 1.5)**2  # sech^2(x) = 1/cosh^2(x)
V2 = -4 / np.cosh((x - 2) / 1.2)**2
W = 1.2 * np.exp(-x**2 / 8)

# Work with interior points only (Dirichlet BC)
n = N - 2  # Number of interior points
interior = slice(1, N-1)

# Construct discrete Laplacian using centered finite differences
# This preserves self-adjointness
Lap = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n, n), format='csr') / h**2

# Build Hamiltonian blocks
H11 = Lap + sparse.diags(V1[interior], 0, format='csr')
H22 = Lap + sparse.diags(V2[interior], 0, format='csr')
H12 = sparse.diags(W[interior], 0, format='csr')  # Coupling term

# Assemble full 2×2 block Hamiltonian (symmetric)
H = sparse.bmat([[H11, H12], 
                 [H12, H22]], format='csr')

# Construct mass matrix
M1 = sparse.diags(mu1[interior], 0, format='csr')
M2 = sparse.diags(mu2[interior], 0, format='csr')
M = sparse.block_diag([M1, M2], format='csr')

# Solve generalized eigenvalue problem H·v = E·M·v
# Find smallest eigenvalues using shift-invert mode
num_eigs = 400  # Start with a reasonable number
eigvals = sp_linalg.eigsh(H, M=M, k=num_eigs, which='SA', return_eigenvectors=False)

# If the largest found eigenvalue is still negative, compute more
while eigvals[-1] < -1e-10 and num_eigs < min(2*n - 1, 1000):
    num_eigs = min(num_eigs + 200, 2*n - 1)
    eigvals = sp_linalg.eigsh(H, M=M, k=num_eigs, which='SA', return_eigenvectors=False)

# Count eigenvalues E < 0
num_negative = np.sum(eigvals < 0)
print(num_negative)