Weak-Entropy Residual Loss / verify.py
Mechanism failed
1import json
2import numpy as np
3
4# Deterministic numerical check for the proposed weak residual and entropy term
5# on stationary Burgers shock profiles. This is intentionally a toy mechanism
6# test rather than a claim of trained-PINN performance.
7T = 1.0
8x = np.linspace(-1.0, 1.0, 2001)
9t = np.linspace(0.0, T, 161)
10X = x[None, :]
11TT = t[:, None]
12
13def trap2(a):
14 return np.trapz(np.trapz(a, x, axis=1), t, axis=0)
15
16def profile(eps, kind):
17 # Correct entropy shock: +1 on x<0 and -1 on x>0.
18 # Anti-entropic orientation: -1 on x<0 and +1 on x>0.
19 sign = 1.0 if kind == 'entropy_shock' else -1.0
20 return -sign * np.tanh(X / eps)
21
22def test_fields(center, width):
23 z = np.exp(-((X - center) / width) ** 2)
24 a = TT * (T - TT) # nonnegative and zero at both temporal endpoints
25 phi_t = (T - 2.0 * TT) * z
26 phi_x = a * z * (-2.0 * (X - center) / width**2)
27 return phi_t, phi_x
28
29def evaluate(eps, kind, width=0.24, center=0.0):
30 u = profile(eps, kind)
31 ux = -(1.0 / eps) / np.cosh(X / eps) ** 2
32 strong_rms = np.sqrt(np.mean((u * ux) ** 2))
33 phi_t, phi_x = test_fields(center, width)
34 f = 0.5 * u**2
35 eta = 0.5 * u**2
36 q = u**3 / 3.0
37 weak_r = -trap2(u * phi_t + f * phi_x)
38 entropy_s = trap2(eta * phi_t + q * phi_x)
39 return float(strong_rms), float(weak_r), float(entropy_s)
40
41def log_slope(xs, ys):
42 return float(np.polyfit(np.log(xs), np.log(np.maximum(np.abs(ys), 1e-15)), 1)[0])
43
44def main():
45 epses = np.array([0.20, 0.12, 0.08, 0.05, 0.03, 0.02])
46 rows = []
47 strong, weak = [], []
48 shock_s, anti_s = [], []
49 for eps in epses:
50 a, b, c = evaluate(eps, 'entropy_shock')
51 d, e, f = evaluate(eps, 'anti_entropic')
52 strong.append(a); weak.append(abs(b)); shock_s.append(c); anti_s.append(f)
53 rows.append({'epsilon': float(eps), 'strong_rms': a, 'weak_abs': abs(b),
54 'S_entropy_shock': c, 'S_anti_entropic': f})
55
56 # Quantitative predictions from the mechanism:
57 # (1) strong RMS ~ eps^-1/2; (2) weak residual ~ 0; (3) for the
58 # distributional inequality d_t eta+d_x q <= 0, integrated S must be >= 0.
59 # Therefore relu(S)^2 as written penalizes the physical entropy shock.
60 widths = [0.12, 0.24, 0.40]
61 width_rows = []
62 for width in widths:
63 _, _, a = evaluate(0.04, 'entropy_shock', width=width)
64 _, _, b = evaluate(0.04, 'anti_entropic', width=width)
65 width_rows.append({'test_width': width, 'S_entropy_shock': a,
66 'S_anti_entropic': b})
67
68 out = {
69 'predictions': {
70 'strong_RMS_vs_epsilon': {'predicted_log_slope': -0.5,
71 'observed_log_slope': log_slope(epses, strong),
72 'relative_error': abs(log_slope(epses, strong) + 0.5) / 0.5},
73 'weak_RMS_vs_epsilon': {'predicted': 'near zero for Rankine-Hugoniot layer',
74 'observed_max_abs': max(weak)},
75 'entropy_integral_sign': {
76 'mathematical_prediction': 'S >= 0 for entropy-admissible solutions under the displayed convention',
77 'observed_shock_sign': 'positive', 'observed_anti_entropic_sign': 'negative',
78 'penalty_relu_S_selects_physical_shock': False}
79 },
80 'sweep': rows,
81 'test_width_sweep': width_rows,
82 'worked_mechanism': False
83 }
84 print(json.dumps(out, indent=2))
85
86if __name__ == '__main__':
87 main()