Add maximum iteration setting for scaling heuristic

Sun, 26 Jul 2026 17:30:51 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Sun, 26 Jul 2026 17:30:51 -0500
changeset 78
2a122736e91c
parent 77
be105c046777
child 79
b6af27f72492

Add maximum iteration setting for scaling heuristic

src/fb.rs file | annotate | diff | comparison | revisions
src/forward_pdps.rs file | annotate | diff | comparison | revisions
src/lib.rs file | annotate | diff | comparison | revisions
src/pdps.rs file | annotate | diff | comparison | revisions
src/prox_penalty.rs file | annotate | diff | comparison | revisions
src/run.rs file | annotate | diff | comparison | revisions
src/sliding_fb.rs file | annotate | diff | comparison | revisions
src/sliding_pdps.rs file | annotate | diff | comparison | revisions
--- a/src/fb.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/fb.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -188,7 +188,7 @@
 
     // Run the algorithm
     for state in iterator.iter_init(|| full_stats(&μ, ε, stats.clone())) {
-        let maybe_μ_base = config.merge_now(&state).then(|| μ.clone());
+        let maybe_μ_base = config.if_merge_now(&state, |_| μ.clone());
         let μ_base_len = μ.len();
 
         // Calculate smooth part of surrogate model.
@@ -201,16 +201,18 @@
 
         // Prune and possibly merge spikes
         if let Some(μ_base) = maybe_μ_base {
-            stats.merged += prox_penalty.merge_spikes(
-                &mut μ,
-                &mut τv,
-                &μ_base,
-                τ,
-                ε,
-                config,
-                &reg,
-                Some(|μ̃: &RNDM<N, F>| f.apply(μ̃)),
-            );
+            config.if_merge_now(&state, |ins| {
+                stats.merged += prox_penalty.merge_spikes(
+                    &mut μ,
+                    &mut τv,
+                    &μ_base,
+                    τ,
+                    ε,
+                    ins,
+                    &reg,
+                    Some(|μ̃: &RNDM<N, F>| f.apply(μ̃)),
+                )
+            });
         }
 
         stats.pruned += prune_with_stats(&mut μ);
@@ -309,10 +311,12 @@
         stats.inserted += μ.len() - μ_base_len;
 
         // (Do not) merge spikes.
-        if config.merge_now(&state) && !warned_merging {
-            let err = format!("Merging not supported for μFISTA");
-            println!("{}", err.red());
-            warned_merging = true;
+        if !warned_merging {
+            config.if_merge_now(&state, |_| {
+                let err = format!("Merging not supported for μFISTA");
+                println!("{}", err.red());
+                warned_merging = true;
+            });
         }
 
         // Do extra weight optimisation step heuristic
--- a/src/forward_pdps.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/forward_pdps.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -240,8 +240,7 @@
         // Merge spikes.
         // This crucially expects the merge routine to be stable with respect to spike locations,
         // and not to performing any pruning. That is be to done below simultaneously for γ.
-        let ins = &config.insertion;
-        if ins.merge_now(&state) {
+        config.insertion.if_merge_now(&state, |ins| {
             stats.merged += prox_penalty.merge_spikes(
                 &mut μ,
                 &mut τv,
@@ -252,7 +251,7 @@
                 &reg,
                 Some(|μ̃: &RNDM<N, F>| f.apply(Pair(μ̃, &z))),
             );
-        }
+        });
 
         // Prune spikes with zero weight.
         stats.pruned += prune_with_stats(&mut μ);
--- a/src/lib.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/lib.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -235,10 +235,14 @@
     pub fitness_merging: Option<bool>,
 
     #[arg(long)]
-    /// Scaling heuristic factor
+    /// Scaling heuristic factor. Enables the heuristic if set.
     pub scaling_heuristic: Option<F>,
 
     #[arg(long)]
+    /// Maximum scaling iteration for scaling heuristic.
+    pub max_scaling_iter: Option<usize>,
+
+    #[arg(long)]
     /// Extra finite dimensional steps to take
     pub extra_weight_optimisation_steps: Option<usize>,
 
--- a/src/pdps.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/pdps.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -209,10 +209,10 @@
         prox_penalty.insert_and_reweigh(&mut μ, &mut τv, τ, ε, config, &reg, &state, &mut stats)?;
 
         // Prune and possibly merge spikes
-        if config.merge_now(&state) {
+        config.if_merge_now(&state, |ins| {
             stats.merged +=
-                prox_penalty.merge_spikes_no_fitness(&mut μ, &mut τv, &μ_base, τ, ε, config, &reg);
-        }
+                prox_penalty.merge_spikes_no_fitness(&mut μ, &mut τv, &μ_base, τ, ε, ins, &reg);
+        });
         stats.inserted += μ.len() - μ_base.len();
         stats.pruned += prune_with_stats(&mut μ);
 
--- a/src/prox_penalty.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/prox_penalty.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -68,6 +68,9 @@
 
     /// Additional weight optimisation steps
     pub extra_weight_optimisation_steps: usize,
+
+    /// Maximum iteration for scaling hack, zero if no maximum.
+    pub max_scaling_iter: usize,
 }
 
 #[replace_float_literals(F::cast_from(literal))]
@@ -87,19 +90,41 @@
             merge_every: 10,
             merge_tolerance_mult: 2.0,
             extra_weight_optimisation_steps: 0,
+            max_scaling_iter: 0,
         }
     }
 }
 
 impl<F: Float> InsertionConfig<F> {
     /// Check if merging should be attempted this iteration
-    pub fn merge_now<I: AlgIterator>(&self, state: &AlgIteratorIteration<I>) -> bool {
-        self.merging.enabled && state.iteration() % self.merge_every == 0
+    pub fn if_merge_now<I: AlgIterator, A>(
+        &self,
+        state: &AlgIteratorIteration<I>,
+        mut f: impl FnMut(&Self) -> A,
+    ) -> Option<A> {
+        if (self.merging.enabled || self.merging.scaling.is_some())
+            && state.iteration() % self.merge_every == 0
+        {
+            if self.max_scaling_iter != 0 && state.iteration() > self.max_scaling_iter {
+                if !self.merging.enabled {
+                    None
+                } else {
+                    Some(f(&InsertionConfig {
+                        merging: SpikeMergingMethod { scaling: None, ..self.merging },
+                        ..*self
+                    }))
+                }
+            } else {
+                Some(f(self))
+            }
+        } else {
+            None
+        }
     }
 
     /// Returns the final merging method
     pub fn final_merging_method(&self) -> SpikeMergingMethod<F> {
-        SpikeMergingMethod { enabled: self.final_merging, ..self.merging }
+        SpikeMergingMethod { enabled: self.final_merging, scaling: None, ..self.merging }
     }
 }
 
--- a/src/run.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/run.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -120,6 +120,7 @@
                 .map_or(g.bootstrap_insertions, |n| Some((n[0], n[1]))),
             merge_every: cli.merge_every.unwrap_or(g.merge_every),
             merging: override_merging(g.merging),
+            max_scaling_iter: cli.max_scaling_iter.unwrap_or(g.max_scaling_iter),
             final_merging: cli.final_merging.unwrap_or(g.final_merging),
             fitness_merging: cli.fitness_merging.unwrap_or(g.fitness_merging),
             inner: override_inner(g.inner),
--- a/src/sliding_fb.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/sliding_fb.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -1092,14 +1092,14 @@
         // Merge spikes.
         // This crucially expects the merge routine to be stable with respect to spike locations,
         // and not to performing any pruning. That is be to done below simultaneously for γ.
-        if config.insertion.merge_now(&state) {
+        config.insertion.if_merge_now(&state, |ins| {
             let m = prox_penalty.merge_spikes(
                 &mut μ,
                 &mut τv̆,
                 &μ̆,
                 τ,
                 ε,
-                &config.insertion,
+                ins,
                 &reg,
                 Some(|μ̃: &RNDM<N, F>| f.apply(μ̃)),
             );
@@ -1107,7 +1107,7 @@
             stats.merged += m;
             //v = f.differential(&μ);
             //}
-        }
+        });
 
         γ.prune_compat(&mut μ, &mut stats);
 
--- a/src/sliding_pdps.rs	Wed Jul 22 16:48:22 2026 -0500
+++ b/src/sliding_pdps.rs	Sun Jul 26 17:30:51 2026 -0500
@@ -303,21 +303,21 @@
         // Merge spikes.
         // This crucially expects the merge routine to be stable with respect to spike locations,
         // and not to performing any pruning. That is be to done below simultaneously for γ.
-        if config.insertion.merge_now(&state) {
+        config.insertion.if_merge_now(&state, |ins| {
             let m = prox_penalty.merge_spikes(
                 &mut μ,
                 &mut τv̆,
                 &μ̆,
                 τ,
                 ε,
-                &config.insertion,
+                ins,
                 &reg,
                 Some(|μ̃: &RNDM<N, F>| f.apply(Pair(μ̃, &z))),
             );
             if m > 0 {
                 stats.merged += m;
             }
-        }
+        });
 
         γ.prune_compat(&mut μ, &mut stats);
 

mercurial