ITADN

Documentation: Clarify AttractorsViaRecurrences `(sparse=false)` state modification by `basins_of_attraction` and impact on subsequent `mapper(ic)` calls

#177Closedk1m9l 创建于 2025-05-21
bugrecurrences
K
k1m9lcommented
When using `AttractorsViaRecurrences` with `sparse = false`, there's a potentially confusing interaction between `basins_of_attraction(mapper; ...)` (the specialized method for `AttractorsViaRecurrences`) and subsequent calls to `mapper(ic)` (which are used by `basins_fractions(mapper, ...)`). This can lead to unexpected label categorization in the output of basins_fractions. Specifically, after `basins_of_attraction` correctly identifies and stores attractors (e.g., with labels 1, 2, 3), a subsequent call to `basins_fractions` using the same populated mapper instance may return a dictionary of fractions with different, re-categorized labels (e.g., 0, 1) instead of the original attractor labels (1, 2, 3). However, `extract_attractors(mapper)` will still correctly report the initially found attractors with labels 1, 2, 3. This discrepancy can be confusing as it appears the mapper's interpretation of its own attractors changes based on its history. **Example Code** ``` using Attractors, StaticArrays, Printf # Define Newton map function newton_map(z, p, n) z1 = z[1] + im*z[2] # Safer Newton step: check for zero derivative df_val = p[1]*z1^(p[1]-1) if abs(df_val) < 1e-12 # Avoid division by zero or very small numbers return SVector(real(z1), imag(z1)) end dz1 = (z1^p[1] - 1) / df_val z1_new = z1 - dz1 return SVector(real(z1_new), imag(z1_new)) end ds = DiscreteDynamicalSystem(newton_map, [0.1, 0.2], [3.0]) # p=3 (3 roots) xg = yg = range(-1.5, 1.5; length = 100) # Coarser grid for MWE grid = (xg, yg) # 1. Initialize mapper mapper_newton = AttractorsViaRecurrences(ds, grid; sparse = false, # Crucial for this behavior consecutive_lost_steps = 1000 ) # 2. Populate mapper with basins_of_attraction # This version of basins_of_attraction modifies mapper_newton.bsn_nfo.basins println("Running basins_of_attraction...") basins_grid_output, attractors_from_boa = basins_of_attraction(mapper_newton; show_progress = false) println("Attractors from basins_of_attraction (should be e.g., 1, 2, 3):") display(attractors_from_boa) # 3. Attempt to get fractions using the *same, now populated* mapper sampler, = statespace_sampler(grid) # sampler for initial conditions println("\nRunning basins_fractions on the populated mapper...") fractions_after_boa = basins_fractions(mapper_newton, sampler; N=5000, show_progress=false) println("Fractions after basins_of_attraction (shows re-categorized labels, e.g., 0, 1):") display(fractions_after_boa) # For comparison: # 4. Fractions with a fresh mapper (expected behavior) mapper_fresh = AttractorsViaRecurrences(ds, grid; sparse = false, consecutive_lost_steps = 1000 ) println("\nRunning basins_fractions on a fresh mapper...") fractions_fresh = basins_fractions(mapper_fresh, sampler; N=5000, show_progress=false) println("Fractions with a fresh mapper (expected labels e.g., 1,2,3,-1):") display(fractions_fresh) attractors_from_fresh = extract_attractors(mapper_fresh) println("Attractors from fresh mapper run:") display(attractors_from_fresh) # 5. Fractions after resetting the original populated mapper println("\nResetting the original populated mapper and running basins_fractions...") reset_mapper!(mapper_newton) fractions_after_reset = basins_fractions(mapper_newton, sampler; N=5000, show_progress=false) println("Fractions after resetting original mapper (expected labels e.g., 1,2,3,-1):") display(fractions_after_reset) attractors_from_reset = extract_attractors(mapper_newton) println("Attractors from reset mapper run:") display(attractors_from_reset) ``` **Cause of the Behavior:** The specialized method `basins_of_attraction(mapper::AttractorsViaRecurrences; ...)` (used when `sparse=false` and the mapper is the primary argument) modifies the internal `mapper.bsn_nfo.basins` grid. After initially filling this grid with raw Finite State Machine (FSM) labels (where attractors and their basins have distinct internal numerical representations), it applies a transformation. This transformation converts these raw FSM labels into the final, user-facing attractor IDs (e.g., 1, 2, 3, -1) and stores these final IDs back into `mapper.bsn_nfo.basins`. When `mapper(ic)` (and thus `basins_fractions`) is called subsequently: The `mapper(ic)` method calls `recurrences_map_to_label!`. This function calls `reset_basins_counters!`, which resets the FSM's immediate search state but leaves the already modified `bsn_nfo.basins` (now containing final labels 1, 2, 3, -1) intact. The FSM then reads these "final" labels from `bsn_nfo.basins` as if they were "raw" FSM input labels from a grid cell. These labels (1, 2, 3, -1) are processed by the FSM logic (e.g., label 1 is odd and treated like a basin hit; label 2 is even and treated like an attractor hit). The FSM then returns a new set of raw-like labels based on this input (e.g., for an input cell labeled 1 in `bsn_nfo.basins`, the FSM returns 1; for an input cell labeled 2, the FSM returns 3; for an input cell labeled 3, the FSM returns 3). The final transformation step within the `(mapper::AttractorsViaRecurrences)(u0) method (iseven(lab) ? (lab ÷ 2) : (lab - 1) ÷ 2)` converts these new raw-like labels from the FSM into the observed re-categorized labels (e.g., 0 and 1). Impact: This behavior is non-obvious and can lead to incorrect interpretation of `basins_fractions` output if the user is unaware that `basins_of_attraction` (the specific version for `AttractorsViaRecurrences`) has modified the mapper's internal state so significantly. The fact that `extract_attractors(mapper)` still shows the original labels {1,2,3} (as this dictionary is populated before this re-processing of labels occurs for subsequent mapper(ic) calls) adds to the potential confusion. **Suggested Documentation Enhancements:** The docstring for `basins_of_attraction(mapper::AttractorsViaRecurrences; ...) `should explicitly state that this method (when `sparse=false`) modifies the internal state of `mapper.bsn_nfo.basins` to store final, user-facing labels. The documentation should recommend calling `reset_mapper!(mapper) `before subsequent uses of `mapper(ic)` or `basins_fractions(mapper, ...)` if the user expects the mapper to behave "freshly" or to produce labels consistent with the attractor IDs as initially found and stored in `extract_attractors(mapper)`. Clarifying this behavior would greatly aid users in understanding the stateful nature of the AttractorsViaRecurrences mapper and help them achieve consistent and interpretable results across different function calls. **Especially** because this is the first example in the Examples on the website.
关闭于 2025-05-22 10 条评论