experiments/laser_and_mirrors_aux.py

changeset 12
a0d61ac3719e
parent 11
c4f923017349
child 18
ddd24b5c2715
--- a/experiments/laser_and_mirrors_aux.py	Mon Jun 29 00:20:42 2026 +0300
+++ b/experiments/laser_and_mirrors_aux.py	Sun Oct 04 13:02:26 2026 -0500
@@ -10,6 +10,7 @@
 from pointsource_pde import Problem, RegTerm
 from pointsource_pde.compose import (
     ComposeFnWithOperator,
+    ScaleAuxStepHack,
     SumOfSeparableFunctions,
 )
 from pointsource_pde.convection_diffusion import (
@@ -21,22 +22,25 @@
 from pointsource_pde.laser_sampling import LaserSampling
 from pointsource_pde.quadratic_dataterm import QuadraticDataTerm
 
+from dolfinx_access import eval_Function_f64
+
 # Give name to the problem
 name = "laser_and_mirrors_aux"
 
 # Fine-tune algorithms
 alg_finetune = {
-    "tau0": 0.99,
-    "tautheta": 100,
-    "gradv_lipest_mult": 1.3,
-    "transport_tolerance": 10,
-    "inner_method": "PP",
-    "inner_pdps_τσ0": (19.8, 0.05),
-    "inner_pp_τ": [1000.0, 1000.0],
+    "tau0": 0.02,
+    "sigmap0": 0.0001,
+    "tautheta": 0.07,
+    "gradv_lipest_mult": 1,
+    "transport_tolerance": 1e5,
+    "inner_method": "Exact",
     "merge": True,
     "fitness_merging": True,
     "merge_radius": 0.1,
     "merge_interp": False,
+    "scaling_heuristic": None,
+    "extra_weight_optimisation_steps": None,
 }
 
 algorithm_overrides = {
@@ -46,6 +50,22 @@
     "RadonSlidingPDPS": alg_finetune,
 }
 
+reg_params = {
+    "alpha": 2e-3,
+    "alpha_k": 60,
+    "alpha_c": 0.06,
+    "c_noise": 0.02,
+    "k_noise": 0.02,
+    "seed": 2393,
+    "true_init": False,
+    "true_target": False,
+    "noise": 0.01,
+    "sim_oversample": 2,
+    "sim_oversample_time": 2,
+    "scale_cstep": 300,
+    "do_non_sliding": True,
+}
+
 
 # Setup shared between experiment with known/unknown conductivity and diffusivity
 def generic_setup(
@@ -58,16 +78,39 @@
     k=0.01,
     r=0.5,
     θ=30 * np.pi / 180,
+    c_noise=reg_params["c_noise"],
+    k_noise=reg_params["k_noise"],
+    noise=reg_params["noise"],
+    sim_oversample=reg_params["sim_oversample"],
+    sim_oversample_time=reg_params["sim_oversample_time"],
+    scale_cstep=reg_params["scale_cstep"],
 ):
 
+    u0 = lambda x: 0.0 * x[1]
+    g = lambda x: 0.0 * x[1]
+    domain_dims = [[0, 0.5], [0, 0.5]]
+    num_steps = 50
+    res = 32
+
     # Create convection-diffusion PDE
     pde = ConvectionDiffusion(
-        u0=lambda x: 0.0 * x[1],
-        g=lambda x: 0.0 * x[1],
-        domain_size=[0, 0.5, 0, 0.5],
-        num_steps=50,
-        # override_lipschitz=4.0,
-        # override_lipschitz_pair=(4.0, 40.0),
+        u0=u0,
+        g=g,
+        domain_dims=domain_dims,
+        num_steps=num_steps,
+        nx=res,
+        ny=res,
+    )
+
+    # Create convection-diffusion PDE for simulation, to avoid inverse crimes
+    pde_sim = ConvectionDiffusion(
+        u0=u0,
+        g=g,
+        domain_dims=domain_dims,
+        # num_steps=(sim_oversample-1) * (num_steps + 1) + num_steps,
+        num_steps=sim_oversample_time * (num_steps - 1) + 1,
+        nx=res * sim_oversample,
+        ny=res * sim_oversample,
     )
 
     # Set up lasers and mirrors
@@ -79,10 +122,10 @@
     ]
     mirrors_per_side = 10
     x0, x1, y0, y1 = (
-        pde.domain_size[0],
-        pde.domain_size[1],
-        pde.domain_size[2],
-        pde.domain_size[3],
+        pde.domain_dims[0][0],
+        pde.domain_dims[0][1],
+        pde.domain_dims[1][0],
+        pde.domain_dims[1][1],
     )
     beams = [
         (src, np.array(dst))
@@ -97,6 +140,7 @@
         ]
     ]
     L_i = LaserSampling(pde.V, pde.domain, pde.nx, pde.ny, beams)
+    L_i_sim = LaserSampling(pde_sim.V, pde_sim.domain, pde_sim.nx, pde_sim.ny, beams)
 
     v0 = 1.0
 
@@ -106,6 +150,9 @@
         c2 = r * np.sin(θ)
         # k = 0.001  # high wind
         print("True aux variables: k = %f, c1 = %f, c2 = %f" % (k, c1, c2))
+        k_sim = k
+        c1_sim = c1
+        c2_sim = c2
     else:
         # Convectivity and diffusivity fields
         def velocity_x(x):
@@ -124,13 +171,33 @@
         k0 = 1.0
         k.interpolate(lambda x: k0 + 0.2 * np.sin(5 * x[0]) * np.cos(5 * x[1]))  # k is
 
+        c1_sim = fem.Function(pde_sim.V)
+        c1_sim.interpolate(velocity_x)
+        c2_sim = fem.Function(pde_sim.V)
+        c2_sim.interpolate(velocity_y)
+        k_sim = fem.Function(pde_sim.V)
+        k0 = 1.0
+        k_sim.interpolate(
+            lambda x: k0 + 0.2 * np.sin(5 * x[0]) * np.cos(5 * x[1])
+        )  # k is
+
     auxtrue = (k, (c1, c2))
 
     # Simulate measurements
+    u_n_list_sim = pde_sim.apply((μ_true, (k_sim, (c1_sim, c2_sim))))
     u_n_list = pde.apply((μ_true, (k, (c1, c2))))
-    b_true = [L_i.apply(u) for u in u_n_list]
+
+    b_true = [L_i_sim.apply(u) for u in u_n_list_sim[0::sim_oversample_time]]
+
+    # Add noise
+    level_k = abs(k)
+    level_c = r
+    k_noisy = max(0.001, relnoise(k, k_noise, rng, level=level_k))
+    c1_noisy, c2_noisy = relnoise((c1, c2), c_noise, rng, level=level_c)
+    aux_noisy = (k_noisy, (c1_noisy, c2_noisy))
     b = [
-        b_i + L_i.noise(0.01 * norm(b_i) / np.sqrt(np.size(b_i)), rng) for b_i in b_true
+        b_i + L_i_sim.noise(noise * norm(b_i) / np.sqrt(np.size(b_i)), rng)
+        for b_i in b_true
     ]
 
     # Estimate bounds on domains and values
@@ -154,62 +221,118 @@
         ),
         pde,
     )
+    if scale_cstep is not None:
+        dat = ComposeFnWithOperator(dat, ScaleAuxStepHack((1.0, scale_cstep)))
+
     dat.curvature_bound_components = lambda: (None, None, None)
 
     # Save true and initial data
     plot_factory = PlotFactory(pde)
     plotter = plot_factory.produce(prefix)
     ω, _ = dat.diff((DiscreteMeasure_2_f64([]), auxtrue))
-    ω_true, _ = dat.diff((μ_true, auxtrue))
+    ω_true, (dk, (dc1, dc2)) = dat.diff((μ_true, auxtrue))
     np.savez_compressed(
         "%s/omega_0.npz" % prefix,
         true_mu=[[point[0], point[1], alpha] for point, alpha in μ_true.iter_padded()],
         omega0=ω.x.array,
         true_omega=ω_true.x.array,
-        k=k,
-        c1=c1,
-        c2=c2,
+        k_true=k,
+        c1_true=c1,
+        c2_true=c2,
+        k_noisy=k_noisy,
+        c1_noisy=c1_noisy,
+        c2_noisy=c2_noisy,
         true_u_n_list_array=[u_n_list[i].x.array for i in plotter.plot_frames],
     )
 
-    return dat, auxtrue, μ_bound, μ_true, plot_factory, pde
+    delta = 0.001 * k
+    dk_est = (
+        dat.apply((μ_true, (k + delta, (c1, c2)))) - dat.apply((μ_true, auxtrue))
+    ) / delta
+    print("k-diff-test:", dk, ", ", dk_est)
+    delta = 0.001 * c1
+    dc1_est = (
+        dat.apply((μ_true, (k, (c1 + delta, c2)))) - dat.apply((μ_true, auxtrue))
+    ) / delta
+    print("c1-diff-test:", dc1, ", ", dc1_est)
+    delta = 0.001
+    μ_perturb = DiscreteMeasure_2_f64(
+        [([0.1, 0.3], 0.08 + delta), ([0.4, 0.25], 0.05), ([0.25, 0.13], 0.06)]
+    )
+    dcμ_est = (dat.apply((μ_perturb, auxtrue)) - dat.apply((μ_true, auxtrue))) / delta
+    print(
+        "μ-diff-test:",
+        eval_Function_f64(ω_true._cpp_object, [0.1, 0.3, 0.0]),
+        ", ",
+        dcμ_est,
+    )
+
+    return dat, auxtrue, aux_noisy, μ_bound, μ_true, plot_factory, pde
 
 
 def relnoise(v, σ, rng, level=None):
-    if level is None:
-        level = abs(v)
-    return float(rng.normal(v, σ * level))
+    if isinstance(v, float):
+        return float(rng.normal(v, σ * (abs(v) if level is None else level)))
+    else:
+        return map(
+            float, rng.normal(v, σ * (np.linalg.norm(v) if level is None else level))
+        )
 
 
 # Setup the problem
-def setup(prefix):
-    rng = np.random.default_rng(seed=31337)
-
-    dat, auxtrue, _μ_bound, μ_true, plot_factory, pde = generic_setup(prefix, rng=rng)
+def setup(
+    prefix,
+    alpha=reg_params["alpha"],
+    alpha_k=reg_params["alpha_k"],
+    alpha_c=reg_params["alpha_c"],
+    c_noise=reg_params["c_noise"],
+    k_noise=reg_params["k_noise"],
+    noise=reg_params["noise"],
+    seed=reg_params["seed"],
+    sim_oversample=reg_params["sim_oversample"],
+    sim_oversample_time=reg_params["sim_oversample_time"],
+    true_init=False,
+    true_target=False,
+    scale_cstep=reg_params["scale_cstep"],
+):
+    rng = np.random.default_rng(seed=seed)
 
-    # Override Lipschitz parameter
-    pde.override_lipschitz = (4.0,)
-    pde.override_lipschitz_pair = (4.0, 4.0)
-    # print("diff_chain_lipschitz_factor (modified)", pde.diff_chain_lipschitz_factor())
-    # l1, l2 = pde.diff_chain_lipschitz_factor_pair()
-    # print("diff_chain_lipschitz_factor_pair (modified) ", l1, ", ", l2)
+    dat, auxtrue, aux0, _μ_bound, μ_true, plot_factory, pde = generic_setup(
+        prefix,
+        rng=rng,
+        c_noise=c_noise,
+        k_noise=k_noise,
+        noise=noise,
+        sim_oversample=sim_oversample,
+        sim_oversample_time=sim_oversample_time,
+        scale_cstep=scale_cstep,
+    )
 
-    reg = RegTerm.NonnegRadon(1.5e-6)
+    # Override Lipschitz parameter to use direct step length choice
+    dat.override_lipschitz = 1.0
+    dat.override_lipschitz_pair = (0.0, 0.0)
+
+    reg = RegTerm.NonnegRadon(alpha)
 
     μ0 = DiscreteMeasure_2_f64([])
-    (k, (c1, c2)) = auxtrue
-    aux0 = (
-        max(0.001, relnoise(k, 0.02, rng)),
-        (
-            relnoise(c1, 0.2, rng),
-            relnoise(c2, 0.2, rng),
-        ),
+
+    print("aux init ", aux0)
+    aux = BoxedQuadraticRegularisation(
+        (0.005, -1.0),
+        (1.0, 1.0),
+        (alpha_k, alpha_c),
+        auxtrue if true_target else aux0,
+        (1, scale_cstep) if scale_cstep is not None else None,
     )
-    print("aux init ", aux0)
-    aux = BoxedQuadraticRegularisation((0.001, -1.0), (1.0, 1.0), (3.0, 0.0005), aux0)
-    ax = aux.apply(aux0)
-    inival = dat.apply((μ0, aux0))
-    print("Initial data term value:", inival + ax)
-    print("Data term value at true μ:", dat.apply((μ_true, auxtrue)) + ax)
+    print("Initial value:", dat.apply((μ0, aux0)) + aux.apply(aux0))
+    print("Value at true μ:", dat.apply((μ_true, auxtrue)) + aux.apply(auxtrue))
 
-    return Problem(dat, reg, aux, aux0, μ0, plot_factory=plot_factory)
+    return Problem(
+        dat,
+        reg,
+        aux,
+        auxtrue if true_init else aux0,
+        μ0,
+        plot_factory=plot_factory,
+        domain=pde.domain_dims,
+    )

mercurial