<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rat Wolf (ratwolf)</title>
    <description>The latest articles on DEV Community by Rat Wolf (ratwolf) (@ratwolf).</description>
    <link>https://dev.to/ratwolf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2523543%2F4529e41c-c1dd-45fa-ad6b-ace947a2884c.jpg</url>
      <title>DEV Community: Rat Wolf (ratwolf)</title>
      <link>https://dev.to/ratwolf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ratwolf"/>
    <language>en</language>
    <item>
      <title>One-pass Hopalong orbit density plot</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Sun, 25 Oct 2026 07:05:26 +0000</pubDate>
      <link>https://dev.to/ratwolf/one-pass-hopalong-orbit-density-plot-1eik</link>
      <guid>https://dev.to/ratwolf/one-pass-hopalong-orbit-density-plot-1eik</guid>
      <description>&lt;p&gt;This script renders the classic Hopalong map as a visit-density image. It&lt;br&gt;
streams the orbit into a fixed-size histogram and adapts the represented&lt;br&gt;
coordinate range when necessary, rather than storing every orbit point or&lt;br&gt;
making a full preliminary pass to discover the bounds.&lt;/p&gt;
&lt;h2&gt;
  
  
  Target
&lt;/h2&gt;

&lt;p&gt;The goal is to plot a long orbit with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory bounded by the histogram dimensions, rather than the number of
iterations.&lt;/li&gt;
&lt;li&gt;Exact integer visit counts at the histogram's &lt;strong&gt;final&lt;/strong&gt; cell resolution.&lt;/li&gt;
&lt;li&gt;Automatic coverage when the orbit moves beyond the current grid, at the
finest cell size that still fits the whole orbit.&lt;/li&gt;
&lt;li&gt;A useful density visualization, optionally Gaussian-smoothed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation uses NumPy arrays, Matplotlib, and Numba. Save the script&lt;br&gt;
as &lt;code&gt;hopalong_orbit_onepass.py&lt;/code&gt; or substitute its actual filename in the&lt;br&gt;
commands below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation details
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Orbit recurrence
&lt;/h3&gt;

&lt;p&gt;Starting at &lt;code&gt;(x, y) = (0, 0)&lt;/code&gt;, each step uses the previous &lt;code&gt;x&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;old_x = x
x = y - sign(old_x) * sqrt(abs(b * old_x - c))
y = a - old_x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default parameters are &lt;code&gt;a=0.7&lt;/code&gt;, &lt;code&gt;b=0.4&lt;/code&gt;, and &lt;code&gt;c=0&lt;/code&gt;. The first &lt;code&gt;--skip&lt;/code&gt;&lt;br&gt;
steps are treated as a transient and are not added to the histogram.&lt;/p&gt;
&lt;h3&gt;
  
  
  Starting grid
&lt;/h3&gt;

&lt;p&gt;The first 4,096 plotted points are held in a small buffer (64 KB). Their&lt;br&gt;
bounding box selects the starting cell size: the smallest power of two times&lt;br&gt;
&lt;code&gt;H_MIN = 2**-32&lt;/code&gt; at which the box fits the grid. The grid is centred on the&lt;br&gt;
box, the buffered points are binned, and the orbit simply continues from where&lt;br&gt;
it was. No recurrence step is computed twice, and there is no separate pilot&lt;br&gt;
orbit.&lt;/p&gt;

&lt;p&gt;This is a sizing heuristic only. Later points are still checked, and the grid&lt;br&gt;
is reframed whenever a point falls outside it. The attractor's extent usually&lt;br&gt;
keeps growing with the number of points, so the buffer cannot predict the final&lt;br&gt;
size; it only avoids a burst of cheap-but-pointless regridding at the start.&lt;/p&gt;
&lt;h3&gt;
  
  
  Streaming histogram and dyadic growth
&lt;/h3&gt;

&lt;p&gt;The histogram is a square &lt;code&gt;size x size&lt;/code&gt; array of signed 64-bit integer&lt;br&gt;
counts. A point inside the current bounds is mapped to a cell by scaling its&lt;br&gt;
coordinates by the inverse cell width and converting to integer indices. The&lt;br&gt;
hot loop does nothing else: it carries no bookkeeping of its own.&lt;/p&gt;

&lt;p&gt;When a plotted point falls outside the grid, the grid is reframed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The occupied block of cells is located with one pass over the array.&lt;/li&gt;
&lt;li&gt;If the occupied block &lt;strong&gt;and&lt;/strong&gt; the new point still fit at the current cell
size, the counts are only &lt;strong&gt;translated&lt;/strong&gt;, so the block is centred. Nothing
is merged and no resolution is lost.&lt;/li&gt;
&lt;li&gt;Otherwise the cell width is doubled: each 2x2 block of counts is merged into
one cell by integer addition, and the merged block is centred in the grid.
This repeats until everything fits.&lt;/li&gt;
&lt;li&gt;The triggering point is binned and orbit processing resumes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Centring leaves room on both sides, so a reframe is rarely needed again soon.&lt;br&gt;
Only the occupied block is read, written and cleared, using two preallocated&lt;br&gt;
arrays that swap roles, so a reframe costs a few milliseconds at 2048 x 2048.&lt;/p&gt;

&lt;p&gt;All cell widths are &lt;code&gt;H_MIN * 2**k&lt;/code&gt;, so old and new cell boundaries stay&lt;br&gt;
aligned: translating and 2x2 merging are exact aggregations onto the new&lt;br&gt;
lattice. Earlier points are not lost or approximated in count. However, their&lt;br&gt;
original fine-cell locations cannot be recovered after merging: all points are&lt;br&gt;
ultimately represented at the final, coarser resolution.&lt;/p&gt;

&lt;p&gt;Because the grid doubles only when the occupied region no longer fits, the&lt;br&gt;
final occupied region covers between about 50% and 100% of the grid side.&lt;/p&gt;
&lt;h3&gt;
  
  
  Output image
&lt;/h3&gt;

&lt;p&gt;Empty outer rows and columns are cropped before display. By default the&lt;br&gt;
script displays &lt;code&gt;log1p(count)&lt;/code&gt; with a &lt;code&gt;magma&lt;/code&gt; color map. With &lt;code&gt;--smooth&lt;/code&gt;, it&lt;br&gt;
applies a separable Gaussian filter with sigma 1.5 cells before taking the&lt;br&gt;
logarithm. &lt;code&gt;--save&lt;/code&gt; writes &lt;code&gt;hopalong_orbit.png&lt;/code&gt; to the selected output&lt;br&gt;
directory instead of opening a window.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tuning resolution with &lt;code&gt;--bins&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;--bins&lt;/code&gt; to set the side length of the square count grid. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python hopalong_orbit_onepass.py &lt;span class="nt"&gt;--bins&lt;/span&gt; 4096 &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Larger values generally produce a smaller final &lt;code&gt;cell&lt;/code&gt; width in the status&lt;br&gt;
output, meaning finer spatial resolution. The relationship is not perfectly&lt;br&gt;
linear: the grid only doubles its cell width when the orbit outgrows it, so&lt;br&gt;
the final cell width also depends on the orbit's coordinate range. Compare&lt;br&gt;
&lt;code&gt;cell&lt;/code&gt; values for the same parameters and iteration count; a smaller value&lt;br&gt;
means finer bins.&lt;/p&gt;

&lt;p&gt;The memory cost grows quadratically with &lt;code&gt;--bins&lt;/code&gt;. The two 64-bit count grids&lt;br&gt;
alone use about &lt;code&gt;16 * bins * bins&lt;/code&gt; bytes: approximately 64 MiB at 2048 and&lt;br&gt;
256 MiB at 4096, before temporary arrays used for plotting or smoothing. The&lt;br&gt;
option is rounded up to an even grid side internally because the grid growth&lt;br&gt;
merges cells in 2x2 blocks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The result is a two-dimensional image of how often the orbit visits each&lt;br&gt;
cell. Dense regions appear brighter, making the orbit's spatial structure&lt;br&gt;
visible without keeping the full point sequence in memory.&lt;/p&gt;

&lt;p&gt;On normal completion, the script reports the number of binned points, grid&lt;br&gt;
size, final cell width, number of grid doublings, elapsed time, and occupied&lt;br&gt;
image dimensions and coordinate extent. If the orbit reaches the escape&lt;br&gt;
threshold, processing stops and the report indicates that it escaped; fewer&lt;br&gt;
than the requested number of points may then be plotted. If it escapes before&lt;br&gt;
any point is plotted, the script exits with a message.&lt;/p&gt;
&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resolution decreases on growth.&lt;/strong&gt; Each doubling of the coordinate span
also doubles the cell width. This preserves a fixed-size grid, but an orbit
whose extent keeps growing with the number of points ends up with a coarser
final image the longer it runs. The cell width is the smallest power of two
at which the whole occupied region fits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counts are exact only for the final bins.&lt;/strong&gt; Integer merges preserve visit
totals, but do not preserve sub-cell positions or allow a later
higher-resolution reconstruction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Floating-point bin edges.&lt;/strong&gt; Orbit coordinates and grid-to-cell mapping
use floating-point arithmetic. Points extremely close to a cell boundary
can be assigned to either neighboring cell due to rounding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reframing has a small fixed cost.&lt;/strong&gt; Each reframe scans the grid once and
moves only the occupied block. Typical runs need zero to a few dozen
reframes, and heavy-tailed orbits somewhat more; the total is on the order of
0.1 s at 2048 x 2048 and does not scale with the number of iterations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finer images touch more memory.&lt;/strong&gt; A sharper final grid has more occupied
cells, so the scattered histogram updates miss the cache a little more often.
Measured on one machine, this was a few percent, and it is the price of the
better resolution rather than of the reframing code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory is bounded by grid size, not necessarily small.&lt;/strong&gt; Two count arrays
are kept (about &lt;code&gt;16 * bins * bins&lt;/code&gt; bytes in total), plus temporary memory for
plotting/smoothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finest cell size.&lt;/strong&gt; Cells never get finer than &lt;code&gt;2**-32&lt;/code&gt;. This only matters
for orbits whose entire extent is below about &lt;code&gt;5e-7&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escape guard.&lt;/strong&gt; The orbit stops if either coordinate's absolute value
exceeds &lt;code&gt;1e6&lt;/code&gt;. This is a safety cutoff, not a general mathematical test
that every parameter choice produces a bounded orbit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smoothing changes the displayed density.&lt;/strong&gt; Gaussian smoothing is a
visualization step, not part of the visit counts. Its sigma is measured in
cells, so the apparent blur depends on the final cell size. Zero padding can
reduce smoothed values near the image boundary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Exploring the spatial density and structure of Hopalong orbits.&lt;/li&gt;
&lt;li&gt;Quickly comparing plots produced with different &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, and &lt;code&gt;c&lt;/code&gt;
parameters.&lt;/li&gt;
&lt;li&gt;Producing a bounded-memory visualization for very long runs when a
fixed-resolution image is acceptable.&lt;/li&gt;
&lt;li&gt;Creating a smoothed, presentation-friendly density image with &lt;code&gt;--smooth&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method is intended for visualization, not for retaining a high&lt;br&gt;
precision point cloud or supporting later zooming and re-binning. For those&lt;br&gt;
needs, store orbit points or use a sparse/finer-resolution representation,&lt;br&gt;
accepting the associated memory or processing cost.&lt;/p&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Install the dependencies in the Python environment used to run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;numpy matplotlib numba
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python hopalong_orbit_onepass.py
python hopalong_orbit_onepass.py &lt;span class="nt"&gt;--smooth&lt;/span&gt;
python hopalong_orbit_onepass.py &lt;span class="nt"&gt;--save&lt;/span&gt;
python hopalong_orbit_onepass.py &lt;span class="nt"&gt;--a&lt;/span&gt; 1.1 &lt;span class="nt"&gt;--b&lt;/span&gt; 0.5 &lt;span class="nt"&gt;--c&lt;/span&gt; 0.3 &lt;span class="nt"&gt;--iters&lt;/span&gt; 2000000 &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--bins&lt;/code&gt; sets the square grid side length. If omitted, the script derives it&lt;br&gt;
from &lt;code&gt;--iters&lt;/code&gt; and caps it at 2,048; the resulting side length is made even&lt;br&gt;
for the 2x2 merge operation. Increase it for finer resolution at greater&lt;br&gt;
memory cost; check the reported &lt;code&gt;cell&lt;/code&gt; value to compare the actual final&lt;br&gt;
resolution. &lt;code&gt;--outdir&lt;/code&gt; selects where the PNG is saved.&lt;/p&gt;

&lt;p&gt;Code and README: &lt;a href="https://github.com/ratwolfzero/hopalong_phase_space/tree/main/Development" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>performance</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>The Hopalong "Attractor" That Isn't One</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Sun, 25 Oct 2026 06:53:31 +0000</pubDate>
      <link>https://dev.to/ratwolf/the-hopalong-attractor-that-isnt-one-2mne</link>
      <guid>https://dev.to/ratwolf/the-hopalong-attractor-that-isnt-one-2mne</guid>
      <description>&lt;h2&gt;
  
  
  A classic computer-art map has no attractor. Here is how to see its regular and chaotic regions with a few lines of Python and numba
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hhsgglq7b7twhak2sp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hhsgglq7b7twhak2sp8.png" alt="Header" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Orbits of the Hopalong map. Cool colours are regular orbits circling islands, warm colours are chaotic orbits.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Search for "Hopalong attractor" and you get glowing, symmetric pictures made from nothing but two lines of arithmetic. They are fun to produce and easy to share. But the word "attractor" hides what the picture actually is.&lt;/p&gt;

&lt;p&gt;This article walks through what the Hopalong map does, why the name is misleading, and how to see all of its phase space at once instead of one orbit at a time. None of this is new to dynamical-systems people. The point is that a short numba script makes it easy to see for yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The map
&lt;/h2&gt;

&lt;p&gt;Barry Martin's Hopalong map [2] turns each point of an orbit into the next one, using three parameters a, b and c:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbaer9rzqg97djon9rafv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbaer9rzqg97djon9rafv.png" alt="Map" width="798" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start at (0,0), repeat millions of times, plot every point. With a = 0.7, b = 0.4 and c = 0 you get a picture like the one below. (In German the map appeared as the program HÜPFER in A.K. Dewdney's column in &lt;em&gt;Spektrum der Wissenschaft&lt;/em&gt; [3].)&lt;/p&gt;

&lt;h2&gt;
  
  
  One orbit, one picture
&lt;/h2&gt;

&lt;p&gt;The core loop is short. With numba it compiles to machine code and runs ten million iterations in about a second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;numba&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;njit&lt;/span&gt;

&lt;span class="nd"&gt;@njit&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;orbit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
        &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(This is a simplified version of the script's loop, which also discards a short transient and stops if an orbit escapes to infinity.)&lt;/p&gt;

&lt;p&gt;A scatter plot of ten million points is slow and saturates into a solid blob. A log-scaled 2D histogram is faster and shows structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9vfk7mygetdgv926kvwd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9vfk7mygetdgv926kvwd.png" alt="Orbit" width="800" height="815"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the black regions. The orbit never enters them, and they are the first hint that something is going on.&lt;/p&gt;

&lt;p&gt;The picture also depends on how long you run the orbit. In one run it stayed inside the same box from 2 million up to 100 million points, then leaked into a larger region somewhere between 100 and 300 million points, and the box grew from about x −0.76 to 1.02 to about x −1.38 to 1.92. The exact point at which this happens depends on floating-point details, so treat it as an observation, not a constant. The script measures the extent over the full run so the image is framed correctly either way.&lt;/p&gt;

&lt;p&gt;For long runs the script fills the 2D histogram while the orbit runs instead of storing the points, which keeps memory constant (in one test, 300 million points took about 11 seconds and about 200 MB) and gave counts identical to NumPy's &lt;code&gt;histogram2d&lt;/code&gt; in the checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it is not an attractor
&lt;/h2&gt;

&lt;p&gt;An attractor is a set that nearby starting points are pulled toward. That requires the dynamics to shrink areas in phase space. The Hopalong map does not. Wherever it is differentiable, its Jacobian is&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkvj7fc7hbrl1ig5shz05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkvj7fc7hbrl1ig5shz05.png" alt="Jacobian" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and its determinant is&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7y9dp81gb4sjkgxf2drx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7y9dp81gb4sjkgxf2drx.png" alt="Determinant" width="796" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The map is also invertible: equation (2) gives the old x from the new y, and then equation (1) gives the old y. A map that is invertible and has determinant 1 preserves area. Nothing contracts, so there is nothing to attract.&lt;/p&gt;

&lt;p&gt;What you see in the classic picture is the footprint of one orbit, and a different starting point can give a different picture. The name is a loose label, and it is common to call any shape drawn by an iterated map an attractor. It is fine to keep using it, as long as you know what the image is.&lt;/p&gt;

&lt;p&gt;Two more facts about the map will matter later. It keeps its form under the scaling&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftxi8t0yclz6pu8q4fae8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftxi8t0yclz6pu8q4fae8.png" alt="Scaling" width="796" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;for any scale factor s, so a and b behave like lengths and c like a length squared. And its fixed points satisfy&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjyjzyyi94m8abnhrerf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjyjzyyi94m8abnhrerf6.png" alt="Fixed Point" width="797" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;with y = a − x.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeing all of phase space
&lt;/h2&gt;

&lt;p&gt;If one orbit only tells part of the story, we want a way to classify every starting point. The standard tool is the Lyapunov exponent: how fast two nearby orbits separate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;regular&lt;/strong&gt; orbit stays on a closed curve or a chain of islands. Nearby orbits separate slowly and the exponent is about zero.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;chaotic&lt;/strong&gt; orbit fills an area. Nearby orbits separate exponentially and the exponent is positive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To estimate it, a tangent vector is pushed forward by the Jacobian (3) and renormalised at every step; the hat marks a vector rescaled to length 1:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfpdsloq64m0ttdd926x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfpdsloq64m0ttdd926x.png" alt="Tangent" width="800" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs9by97l2mkwlwsx4xouj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs9by97l2mkwlwsx4xouj.png" alt="Renormalised" width="800" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The largest finite-time exponent over N steps is the mean logarithmic stretching:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9m5q1tflo42u835tcmrk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9m5q1tflo42u835tcmrk.png" alt="Lyapunov" width="799" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In code, it is the same loop with a few extra lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@njit&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lyapunov&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="mf"&gt;1e-12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# guard the sqrt singularity
&lt;/span&gt;        &lt;span class="n"&gt;dfdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
        &lt;span class="n"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dfdx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;vx&lt;/span&gt;
        &lt;span class="n"&gt;norm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Also simplified: the script additionally discards the first 500 steps so the tangent vector can align, and stops if the orbit escapes.)&lt;/p&gt;

&lt;p&gt;Run it over a 400 × 400 grid of starting points (numba's &lt;code&gt;prange&lt;/code&gt; spreads the grid over your cores) and colour each point by its exponent:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmj2or9fohz5xdbbs5fr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmj2or9fohz5xdbbs5fr.png" alt="IExponent" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the plotted window (x from −2.5 to 2.9, y from −2.2 to 3.2), about 70% of the starting points are regular and about 30% are chaotic, with 8,000 iterations per point. The dark blobs are islands of regular motion, and the bright bands between them are the chaotic region. The cross marks (0,0). Its orbit has an exponent of about 0.018 and lies in the chaotic region, which is why the classic picture has those empty holes: it is one chaotic orbit, and most of the empty holes are regular islands it does not enter (in a pixel-by-pixel check, about 81% of the empty pixels start regular orbits).&lt;/p&gt;

&lt;p&gt;Starting points far from the centre, such as (50,50) or (1000,−1000), turned out to be regular, but a coarse survey still found chaotic orbits at every distance we probed (up to 64 units from the origin), so the structure has no sharp outer edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  A prettier version
&lt;/h2&gt;

&lt;p&gt;The classification map is informative but pixelated. For a more attractive picture, start a few hundred orbits along a line through phase space and draw each one as its own set of points. Regular orbits become nested rings around the islands, and chaotic ones fill the bands in between:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvp2pr0d7g6dn3uz0ygap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvp2pr0d7g6dn3uz0ygap.png" alt="Portrait" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The warm colours (yellow to red) are chaotic orbits, brighter for a larger exponent. The cool colours (cyan to magenta) are regular orbits, but there they only encode the position of the starting point along the line, so they are decoration, not data.&lt;/p&gt;

&lt;p&gt;The picture shows a ring of seven large islands around the centre and an inner ring of about ten smaller ones. The line of starting points only grazes the rims of the large islands, so their interiors stay empty. We did not analyse these island chains further.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is, and what it isn't
&lt;/h2&gt;

&lt;p&gt;Mixed regular and chaotic motion is the standard behaviour of area-preserving maps, and this article reproduces it for one parameter set. The references at the end give the sources of the map; the results here have not been compared with them or with other literature on its dynamics. A few limits are worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The percentages depend on the window.&lt;/strong&gt; The chaotic share is about 31% in a window twice as wide as the one above, but about 20% at four times as wide. The numbers describe the plotted region, not the whole plane.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The threshold matters a little.&lt;/strong&gt; The chaotic share stays at 30–31% for thresholds from 0.002 to 0.004 and for 4,000 to 20,000 iterations. At a threshold of 0.008 it drops to about 25–27%, because weakly chaotic orbits are no longer counted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The exponent is finite-time.&lt;/strong&gt; Orbits that stick near an island can look regular over a short run, so the chaotic share can be under-counted near island boundaries. Even the exponent of the orbit from (0,0) varies by about 10% between runs of different length.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The square-root term is singular at b·x = c.&lt;/strong&gt; The derivative is unbounded there, so classical results for smooth maps are an analogy, not a guarantee.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One parameter set.&lt;/strong&gt; Nothing here says how the picture changes as a, b and c vary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;The full script produces all three views and the header image, with a, b, c and the iteration counts as command-line options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install numpy matplotlib numba
python hopalong_phase_space.py --save
python hopalong_phase_space.py --a 1.1 --b 0.5 --c 0.3 --iters 2000000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script runs in two passes. A quick first pass finds the length scale of the map from a, b and c, the centre of its structure (the map's fixed point) and the exact extent of the orbit from (0,0). The second pass draws the three views with that framing, so you can change a, b, c and the number of points and still get a framed picture. How much of the structure fits the window depends on the parameters, though: the window is 3.8 length scales around the fixed point (or around the middle of the orbit if there is none), which is a zoom choice, and for maps with much larger orbits you will want &lt;code&gt;--window-scale&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Code and README: &lt;a href="https://github.com/ratwolfzero/hopalong_phase_space" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pretty picture is a good way into a real question. Here the question is how a map that preserves area still produces such rich structure, and the answer is that its phase space is not one thing but a patchwork of regular and chaotic regions. The natural next step is to ask how that patchwork changes as the parameters move.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] &lt;strong&gt;J. Lansdown and R. A. Earnshaw (eds.)&lt;/strong&gt;, &lt;em&gt;Computers in Art, Design and Animation&lt;/em&gt;. New York: Springer-Verlag, 1989.&lt;/p&gt;

&lt;p&gt;[2] &lt;strong&gt;Barry Martin&lt;/strong&gt;, "Graphic Potential of Recursive Functions," in &lt;em&gt;Computers in Art, Design and Animation&lt;/em&gt; [1], pp. 109–129.&lt;/p&gt;

&lt;p&gt;[3] &lt;strong&gt;A.K. Dewdney&lt;/strong&gt;, &lt;em&gt;Psychotapeten&lt;/em&gt;, algorithm &lt;em&gt;HÜPFER&lt;/em&gt;, in &lt;em&gt;Spektrum der Wissenschaft: Computer Kurzweil&lt;/em&gt;. Heidelberg: Spektrum der Wissenschaft Verlagsgesellschaft mbH &amp;amp; Co., 1988. (German edition of &lt;em&gt;Scientific American&lt;/em&gt;.)&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>python</category>
      <category>performance</category>
      <category>programming</category>
    </item>
    <item>
      <title>Discreteness as the Late-Time Limit of a Smooth Flow</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Fri, 09 Oct 2026 03:55:32 +0000</pubDate>
      <link>https://dev.to/ratwolf/discreteness-as-the-late-time-limit-of-a-smooth-flow-3ia4</link>
      <guid>https://dev.to/ratwolf/discreteness-as-the-late-time-limit-of-a-smooth-flow-3ia4</guid>
      <description>&lt;p&gt;&lt;em&gt;An exactly solvable gradient flow whose time-evolution maps are smooth bijections of the real line at every finite time, and whose infinite-time limit is rounding to the nearest integer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxytu1d5trima4lf5apo4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxytu1d5trima4lf5apo4.png" alt="Dashboard" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Abstract
&lt;/h3&gt;

&lt;p&gt;We study the gradient flow of the periodic potential&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgufwkj09mcaox2piqaa8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgufwkj09mcaox2piqaa8.png" alt="Potential" width="269" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Integers are its stable fixed points and half-integers its unstable ones. The flow can be solved in closed form. At every finite time it is a smooth, strictly increasing bijection of the real line; the family of maps forms a one-parameter group; and as time tends to infinity every open cell between consecutive half-integers collapses onto the integer it contains. "Integer-valued" therefore appears as the endpoint of a continuous process with a single, exactly composable resolution parameter, the time. The integers are not generated by the flow: they enter through the period of the potential. What the flow provides is an exactly solvable, exactly composable route to that prescribed discrete target. We state the closed form, derive the main properties, give a geometric interpretation as a hyperbolic Möbius dilation of the circle, and give the exact time it takes to resolve a point to a given accuracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The exact solution
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frokh8ik3j922u1g48c2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frokh8ik3j922u1g48c2p.png" alt="Exact" width="416" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full derivations and proofs, the verification script and the visualization code are in the GitHub repo: &lt;a href="https://github.com/ratwolfzero/continuous-discreteness" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>science</category>
    </item>
    <item>
      <title>I Built a Finite-Element Solver With Nothing to Hide</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Thu, 16 Jul 2026 01:13:07 +0000</pubDate>
      <link>https://dev.to/ratwolf/i-built-a-finite-element-solver-with-nothing-to-hide-4bm4</link>
      <guid>https://dev.to/ratwolf/i-built-a-finite-element-solver-with-nothing-to-hide-4bm4</guid>
      <description>&lt;p&gt;&lt;em&gt;A transparent, dependency-free capacitor simulator — and an honest account of exactly where a simple mesh lets it down.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4scfm7eo2bauklaydajc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4scfm7eo2bauklaydajc.png" alt="Plate Capacitor" width="800" height="677"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every physics course teaches the parallel-plate capacitor formula in the first week:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc160cqt92lscijj0gl9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc160cqt92lscijj0gl9c.png" alt="Plate Capacitor" width="332" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clean. Memorable. &lt;strong&gt;Wrong&lt;/strong&gt; the moment your capacitor isn't two infinite plates in a vacuum. Real capacitors have edges. Real dielectrics are layered. Real geometries are curved. The formula survives exactly as long as the idealization does.&lt;/p&gt;

&lt;p&gt;So you reach for simulation instead — and run into two unsatisfying options.&lt;/p&gt;

&lt;p&gt;Option one: a commercial FEM package. Powerful, polished, and a complete black box. You get a number. You don't get to see the mesh, the matrix, or the fifty implicit decisions that produced it.&lt;/p&gt;

&lt;p&gt;Option two: a serious open-source stack — FEniCS, deal.II, scikit-fem with gmsh underneath. Genuinely open, genuinely capable, and also genuinely heavy: compiled mesh generators, several abstraction layers, a real learning curve before you can even see where the physics happens.&lt;/p&gt;

&lt;p&gt;I wanted a third thing. So I set myself three rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every formula in the code has to be traceable to something you could write on a whiteboard.&lt;/li&gt;
&lt;li&gt;Every accuracy claim has to be measured, not assumed.&lt;/li&gt;
&lt;li&gt;Every known limitation has to be printed by the program itself, not buried in a comment nobody reads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what building to those rules actually looked like.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Physics, Compressed
&lt;/h2&gt;

&lt;p&gt;A capacitor problem is one equation. Given some conductors at fixed voltages and a dielectric — possibly several, possibly varying in space — filling the gaps between them, the electric potential $V$ satisfies:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzavnqwro2tuouefqrwdo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzavnqwro2tuouefqrwdo.png" alt="Potential" width="350" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the whole physics. Everything else — field, displacement, stored energy, capacitance — falls out of &lt;em&gt;&lt;strong&gt;V&lt;/strong&gt;&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh9a48glr3h0dil32dovj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh9a48glr3h0dil32dovj.png" alt="Physics Details" width="775" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That last formula is worth a pause. The obvious way to get capacitance out of a simulation is to integrate surface charge on a conductor. This solver doesn't do that — it computes the &lt;em&gt;total energy stored in the field&lt;/em&gt; instead and rearranges that same relationship to solve for C. Charge integration means differentiating a numerically noisy field exactly at a boundary, the single worst place to differentiate anything. Energy integration only needs a field the solver has already computed everywhere. Small choice, but the kind that separates code that runs from code that's numerically honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "No Black Box" Actually Means
&lt;/h2&gt;

&lt;p&gt;Every finite element method runs on the same trick: chop the domain into small pieces, assume the solution is simple on each piece, turn a differential equation into a large sparse system of linear equations.&lt;/p&gt;

&lt;p&gt;For a triangular piece, the entire physics collapses into one formula for how strongly two corners of that triangle interact:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftk6m5iksw3qpkwsdadnn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftk6m5iksw3qpkwsdadnn.png" alt="Grid" width="268" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;𝐴 is the triangle’s area. 𝑏𝑖 and 𝑐𝑖 aren’t mysterious — they’re just numbers describing the edge directly across from corner 𝑖: bigger for a longer opposite edge, and they flip sign depending on which way that edge points. Feed in three corner coordinates and a material, and out comes a small table of these numbers, one for every pair of corners. That’s the whole trick. There’s no hidden layer between this formula and the code that computes it — about twenty lines of NumPy, every one of them readable.&lt;/p&gt;

&lt;p&gt;Assemble that over every triangle, apply boundary conditions — a conductor is just a region held at a fixed voltage — solve the sparse system, and you have 𝑉 everywhere. The same twenty lines handle a flat plate, a circular cable cross-section, or any shape you can build from simple ones combined with union, intersection, and difference. The geometry never touches the physics.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deliberate Trade-Off
&lt;/h2&gt;

&lt;p&gt;Here's the part most "look what I built" posts skip: an honest accounting of what it costs.&lt;/p&gt;

&lt;p&gt;Real finite element tools mesh the &lt;em&gt;actual&lt;/em&gt; geometry — triangles that hug a curved boundary exactly, dense where the field is doing something interesting, sparse where it isn't. Building that kind of mesh is genuinely hard, and it's exactly what pulls in the heavy dependencies — a geometry kernel, mesh-quality algorithms, compiled binaries — that make FEM tooling opaque in the first place.&lt;/p&gt;

&lt;p&gt;I made a different choice: a plain Cartesian grid, split into triangles. No mesh generator. No native dependencies — just &lt;code&gt;numpy.linspace&lt;/code&gt; and index arithmetic. A circle on a grid like this isn't a circle. It's a staircase of grid cells that looks roughly circular from far enough away.&lt;/p&gt;

&lt;p&gt;That's not an oversight. It's the trade, made on purpose, to keep the whole tool inside three lines of &lt;code&gt;pip install&lt;/code&gt;. The question is whether the trade is &lt;em&gt;honest&lt;/em&gt; about its cost — and that's where most projects stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving It's Not a Toy
&lt;/h2&gt;

&lt;p&gt;Before trusting any result, I checked the solver against a case with a known &lt;em&gt;exact&lt;/em&gt; answer: plates spanning the entire simulation width, so no fringing is even geometrically possible. Just,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvqhqcnljpdomo1agoybo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvqhqcnljpdomo1agoybo.png" alt="Capacity" width="165" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;exactly, with nothing to approximate on the physics side.&lt;/p&gt;

&lt;p&gt;The solver matched it to &lt;strong&gt;0.0000% error, at every mesh resolution tested.&lt;/strong&gt; That's the baseline: the finite element machinery has no bugs hiding in it. Whatever error shows up elsewhere comes from the mesh, not the math.&lt;/p&gt;

&lt;p&gt;That check used a deliberately artificial setup — plates spanning the whole domain so fringing couldn’t happen — specifically so the formula would be exact and the test would be clean.&lt;br&gt;
Real capacitors don’t look like that. So next I ran the two real examples, with real, finite edges — a coax cable filled with polyethylene, and a parallel plate with a glass slab across half the gap — through an actual convergence study instead of reporting one number and hoping.&lt;br&gt;
The two reference values below aren’t the same kind of thing, on purpose: the coax formula is exact, so its gap is genuine mesh error. The parallel-plate formula, by contrast, is deliberately fringing-free — the classic no-fringing approximation, now extended to a glass-and-air sandwich instead of a single material — so its gap here isn’t a solver problem, it’s the real physics that formula was never built to capture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyblfb4ka7lw6jxr6ikn3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyblfb4ka7lw6jxr6ikn3.png" alt="Convergence" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The coax case increases at every step, though not at a perfectly steady rate — there’s a plateau partway through, still climbing but less steeply.&lt;/p&gt;

&lt;p&gt;The parallel-plate case looks different again — across the four resolutions actually tested, it runs 102.0 → 101.9 → 98.8 → 101.8 pF/m, dipping in the middle before recovering, rather than settling smoothly toward the table’s two endpoints.&lt;/p&gt;

&lt;p&gt;Two things are tangled together in that dip.&lt;br&gt;
&lt;strong&gt;One is mundane&lt;/strong&gt;: a fixed grid can only land exactly on some target sizes, and this particular resolution happens to round the capacitor’s own gap and dielectric thickness to slightly different values than its neighbors do — a small, real change in what’s actually being simulated, not an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The other is more interesting&lt;/strong&gt;: a conductor’s sharp corner carries a genuine field singularity ( (𝐸∼𝑟^−1/3, for anyone curious), and independent meshes built at different resolutions aren’t strict refinements of each other, so the usual “finer mesh, better answer” guarantee doesn’t hold between them.&lt;/p&gt;

&lt;p&gt;I could have shown just the two endpoints and let it look like ordinary convergence. Instead the program prints the whole sweep, and flags which of the two effects is at play at each point, rather than leaving the reader to guess.&lt;/p&gt;

&lt;p&gt;A tool that only shows you its clean case isn’t more trustworthy than one that shows you both. It’s just quieter about its limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Is — and What It Isn't
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;This is&lt;/th&gt;
&lt;th&gt;This is not&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A from-scratch, mathematically correct FEM electrostatics solver&lt;/td&gt;
&lt;td&gt;A replacement for COMSOL, Ansys Maxwell, or a production EDA tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small enough to read in one sitting — about 1,000 lines, three dependencies&lt;/td&gt;
&lt;td&gt;An adaptive, conforming-mesh solver — curved boundaries are approximated, and quantifiably so&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validated against exact solutions, not just "it runs and the number looks plausible"&lt;/td&gt;
&lt;td&gt;A 3D solver, or one that ingests arbitrary CAD geometry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Honest, in its own printed output, about exactly where and why it's approximate&lt;/td&gt;
&lt;td&gt;Trying to be any of the above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you need production-grade accuracy on complex geometry, this isn't your tool. If you want to actually &lt;strong&gt;&lt;em&gt;see&lt;/em&gt; what a finite element solver is doing&lt;/strong&gt; — every matrix entry, every approximation, every place it's wrong and by how much — &lt;strong&gt;this was built for exactly that.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Goes From Here
&lt;/h2&gt;

&lt;p&gt;The single biggest lever left on the table is the mesh itself. A few concrete next steps, in roughly the order they'd pay off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Swap the Cartesian grid for a real unstructured, conforming mesh, fixing the staircased boundaries and under-resolved corners in one move — at the cost of the dependency-free simplicity that's the whole point of this version.&lt;/li&gt;
&lt;li&gt;Short of that: grade the existing grid finer near sharp corners while staying dependency-free, which would tighten the parallel-plate numbers specifically without touching the coax case.&lt;/li&gt;
&lt;li&gt;Add the classic extras once the mesh is solid: floating conductors, nonlinear and anisotropic dielectrics, 3D.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that happens silently. That's kind of the point.&lt;/p&gt;

&lt;p&gt;The full code, the complete derivation, and both worked examples with figures are on: &lt;a href="https://github.com/ratwolfzero/Capacitor_FEM/tree/main" rel="noopener noreferrer"&gt;&lt;em&gt;GitHub&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>fem</category>
      <category>capacitor</category>
      <category>electrostatics</category>
      <category>python</category>
    </item>
    <item>
      <title>A Measured Formulation of the Shadow View of Collatz</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Fri, 03 Jul 2026 06:53:02 +0000</pubDate>
      <link>https://dev.to/ratwolf/a-measured-formulation-of-the-shadow-view-of-collatz-2l5</link>
      <guid>https://dev.to/ratwolf/a-measured-formulation-of-the-shadow-view-of-collatz-2l5</guid>
      <description>&lt;h2&gt;
  
  
  A Measured Formulation of the Shadow View of Collatz
&lt;/h2&gt;

&lt;h2&gt;
  
  
  A more careful description
&lt;/h2&gt;

&lt;p&gt;A useful way to think about the Collatz map is to separate the orbit into two layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the parity pattern of the terms;&lt;/li&gt;
&lt;li&gt;the arithmetic values that carry that pattern.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If one records the orbit as a sequence of even/odd decisions, the resulting binary word can be viewed as a symbolic encoding of the dynamics. In the accelerated version of the map, where one removes all factors of 2 immediately after each odd step, the evolution of the values becomes closely tied to this parity word. At that symbolic level, the dynamics look simple: the orbit is encoded by a binary sequence, and the next state is determined by the next bit of that sequence.&lt;/p&gt;

&lt;p&gt;This is a genuine simplification of the picture, but it is a simplification of the representation, not a solution of the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Accelerated Collatz Formulation
&lt;/h2&gt;

&lt;p&gt;This perspective uses the &lt;em&gt;accelerated&lt;/em&gt; (or shortcut) Collatz map rather than the classical 3n+1 rule. In the accelerated version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;strong&gt;n is even&lt;/strong&gt;: next = n / 2&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;n is odd&lt;/strong&gt;:  next = (3n + 1) / 2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By folding the immediate division by 2 after each 3n+1 step into a single operation, the accelerated map produces a shorter sequence in which every step corresponds directly to one bit in the parity word (0 = divide by 2, 1 = apply (3n+1)/2). For example, starting from 27 the classical orbit requires 111 steps, while the accelerated orbit reaches 1 in 71 steps. This formulation makes the parity shadow cleaner and more tightly coupled to the symbolic dynamics, while preserving the essential behavior of the original conjecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6msbsfh7b66j01twrz0f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6msbsfh7b66j01twrz0f.png" alt="Block Recurrence Metric" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Collatz Shadow Explorer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk943cuzuyk8imnylbozp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk943cuzuyk8imnylbozp.png" alt="Shadow Explorer" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To actively investigate these dynamics, this repository includes a Python-based interactive desktop application (&lt;code&gt;shadow_explorer.py&lt;/code&gt;). &lt;a href="https://github.com/ratwolfzero/Collatz/blob/main/shadow_approach/shadow_explorer.py" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rather than viewing static plots, the application allows you to input starting values, adjust sequence parameters, and dynamically generate up to four concurrent analytical views of the parity shadow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analytical Views Included
&lt;/h3&gt;

&lt;p&gt;The application provides several lenses through which to study the sequence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Orbit + Shadow Track:&lt;/strong&gt; The fundamental view. It overlays the numeric orbit (the arithmetic layer) with the binary parity sequence (the symbolic layer) on the same step axis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling Density Analysis:&lt;/strong&gt; Displays the ratio of odd to even steps over a sliding window, highlighting localized phases of growth or decay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;:&lt;/strong&gt; A histogram showing the frequency of $k$-length binary blocks (e.g., how often &lt;code&gt;011&lt;/code&gt; or &lt;code&gt;101&lt;/code&gt; occurs), revealing combinatorial constraints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2D Turtle Walk (Fractal):&lt;/strong&gt; Treats the parity word as directional instructions (0 = Left, 1 = Right) on a 2D plane. This maps the sequence to spatial geometry, often revealing structural symmetries hidden in the linear data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shannon Block Entropy:&lt;/strong&gt; Measures the informational unpredictability of the sequence over time. It highlights chaotic phases versus highly ordered phases as the orbit collapses toward 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast Fourier Transform (FFT):&lt;/strong&gt; Calculates the power spectrum of the $0-1$ signal, allowing you to check for dominant frequencies or periodic harmonics in the shadow word.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;De Bruijn Transition Network:&lt;/strong&gt; A directed network graph of parity block transitions. It visualizes the chronological "flow" of the sequence, essentially mapping the pathways that drain into the standard 4-2-1 attractor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;more see development folder on GitHub:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this perspective is
&lt;/h2&gt;

&lt;p&gt;This viewpoint is best understood as a reformulation. It highlights that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Collatz map can be studied through its parity pattern;&lt;/li&gt;
&lt;li&gt;the parity pattern gives a symbolic description of the orbit;&lt;/li&gt;
&lt;li&gt;the arithmetic part of the map is pushed into the encoding of the symbolic sequence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this sense, the problem can be seen as a question about which binary sequences arise from ordinary integers under this encoding, and how those sequences are constrained by arithmetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this perspective is not
&lt;/h2&gt;

&lt;p&gt;It is important not to overstate what this does. It does not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;solve the Collatz conjecture;&lt;/li&gt;
&lt;li&gt;prove convergence for all starting values;&lt;/li&gt;
&lt;li&gt;replace the classical numerical and probabilistic analysis;&lt;/li&gt;
&lt;li&gt;introduce a fundamentally new dynamical system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main point is not that the problem becomes easy in this language. The point is that the structure becomes more legible: the symbolic part is simple, while the arithmetic constraints are the part that remains difficult.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it can still be useful
&lt;/h2&gt;

&lt;p&gt;Even if it does not solve the conjecture, this perspective can be valuable for at least three reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it gives a compact symbolic representation of the orbit;&lt;/li&gt;
&lt;li&gt;it separates the simple combinatorial aspect from the arithmetic constraint;&lt;/li&gt;
&lt;li&gt;it connects the problem with broader ideas from symbolic dynamics, signal processing, and 2-adic analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the idea is not that one has found a new mechanism for proving convergence. It is rather that one has found a different coordinate system—aided by computational visualization—in which the problem looks more structured and more interpretable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation and Usage
&lt;/h2&gt;

&lt;p&gt;To run the interactive explorer, you will need Python 3 installed along with a few standard scientific and GUI libraries.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash

# Install required dependencies
pip install PyQt6 matplotlib numpy networkx

# Launch the explorer
python shadow_explorer.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>mathematics</category>
      <category>collatz</category>
    </item>
    <item>
      <title>A Triplet-Based Parameterization for the Local Asymptotic Characterization of Polynomial Roots</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:24:01 +0000</pubDate>
      <link>https://dev.to/ratwolf/a-triplet-based-parameterization-for-the-local-asymptotic-characterization-of-polynomial-roots-33oa</link>
      <guid>https://dev.to/ratwolf/a-triplet-based-parameterization-for-the-local-asymptotic-characterization-of-polynomial-roots-33oa</guid>
      <description>&lt;p&gt;Abstract​&lt;/p&gt;

&lt;p&gt;This paper introduces a compact three-parameter framework for characterizing the local geometry of polynomial roots. For each root, the framework records its position, its algebraic multiplicity, and a newly defined quantity called the characteristic deflection distance &lt;strong&gt;𝛿&lt;/strong&gt;. This third parameter acts as a natural geometric scale: it measures how sharply or gradually the polynomial departs from zero in the immediate vicinity of the root, and it encodes the collective influence of all other roots through their distances from the one being analyzed.&lt;/p&gt;

&lt;p&gt;The characteristic deflection distance generalizes the classical condition number of a simple root to roots of arbitrary multiplicity, and it allows direct geometric comparison across roots of different degrees. A key finding is that multiplicity alone does not determine geometric dominance — a lower-multiplicity root can have a larger spatial footprint than a higher-multiplicity one, depending on the global root configuration.&lt;/p&gt;

&lt;p&gt;Symbolic and numerical Python implementations are provided, along with a worked example. The framework extends naturally to polynomials over the complex numbers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doi.org/10.5281/zenodo.19303726" rel="noopener noreferrer"&gt;DOI (Zenodo)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To visualize the spatial competition between these triplets, we map the &lt;strong&gt;𝛿-Normalized Distance Field&lt;/strong&gt; and the &lt;strong&gt;Newton Flow&lt;/strong&gt;. This provides a "topographical" view of the polynomial's geometry where every root is evaluated against its own intrinsic scale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ratwolfzero/Param_Poly_Root" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx4hlrwxa8n4ky0khsuf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx4hlrwxa8n4ky0khsuf.png" alt=" " width="800" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>science</category>
    </item>
    <item>
      <title>High-Precision Companion Matrix Root Finder</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Fri, 13 Mar 2026 06:53:50 +0000</pubDate>
      <link>https://dev.to/ratwolf/high-precision-companion-matrix-root-finder-1ph7</link>
      <guid>https://dev.to/ratwolf/high-precision-companion-matrix-root-finder-1ph7</guid>
      <description>&lt;h2&gt;
  
  
  High-Precision Companion Matrix Root Finder
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ratwolfzero/Poly_Root" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;br&gt;
&lt;a href="https://medium.com/@ratwolf/the-floating-point-catastrophe-9e795d46cfb1" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;High-Precision Companion Matrix Root Finder&lt;/p&gt;

&lt;p&gt;A robust polynomial solver leveraging arbitrary-precision floating-point arithmetic to compute roots of ill-conditioned polynomials. This tool primarily addresses rounding and finite-precision issues present in standard 64-bit floating-point implementations by constructing and solving a companion matrix within the mpmath environment.&lt;/p&gt;

&lt;p&gt;The solver cannot eliminate the intrinsic mathematical ill-conditioning of certain polynomials, but it can significantly reduce the numerical errors caused by rounding, making the computed roots far more reliable.&lt;/p&gt;

&lt;p&gt;It features modular code structure, residual checks, and robust input validation to improve usability and reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Idea
&lt;/h2&gt;

&lt;p&gt;The solver does not attempt to solve the fundamental conditioning problem of polynomial root finding.&lt;/p&gt;

&lt;p&gt;Instead, it focuses on reducing numerical rounding errors using high‑precision arithmetic, which in practice leads to far more reliable root approximations for difficult polynomials.&lt;/p&gt;

</description>
      <category>python</category>
      <category>performance</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Fast Chladni Pattern Simulation with a Simplified Membrane Model Using Vectorization and Stochastic Particles View</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Thu, 05 Mar 2026 06:25:48 +0000</pubDate>
      <link>https://dev.to/ratwolf/fast-chladni-pattern-simulation-with-a-simplified-membrane-model-using-vectorization-and-stochastic-30gf</link>
      <guid>https://dev.to/ratwolf/fast-chladni-pattern-simulation-with-a-simplified-membrane-model-using-vectorization-and-stochastic-30gf</guid>
      <description>&lt;h2&gt;
  
  
  Chladni Figures Simulation
&lt;/h2&gt;

&lt;p&gt;This Python code simulates the nodal line patterns of Chladni figures, approximating Ernst Chladni’s 1787 experiments with vibrating plates by modeling the resonant modes of a square membrane. It captures the visual essence of these wave patterns without simulating the complex physics of elastic bending and the dynamics of particle motion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python Code, detailed simulation description, physics and mathematics see:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ratwolfzero/Chladni_Figures/tree/main" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61z432duledtl75i2mju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61z432duledtl75i2mju.png" alt="Magnitude View" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Magnitude View:&lt;/strong&gt; A 2D static plot showing the time-independent displacement magnitude ∣Z(x,y,f)∣ of the steady-state response at a driving frequency f matching the degenerate modes (3,5) and (5,3). A small damping factor (γ = 0.01) ensures these modes dominate. The magnitude highlights nodal lines and makes positive and negative antinodes indistinguishable, mimicking experimental Chladni patterns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9vrk3dlfh8lo9ok0qem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9vrk3dlfh8lo9ok0qem.png" alt="Phase View" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase View:&lt;/strong&gt; This view displays the signed spatial amplitude of the steady-state response at a given frequency. The sign indicates whether a region oscillates in phase or π out of phase with a reference. Nodal lines appear where the amplitude vanishes. The membrane motion remains oscillatory in time; this view represents the spatial structure and phase relationships of that motion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuue6narguh138gvvtr1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuue6narguh138gvvtr1.png" alt="Sand View" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sand View&lt;/strong&gt;: The Sand View acts as a visually intuitive “nodal detector” derived from the same resonance-weighted field used in the magnitude and phase views.&lt;/p&gt;

&lt;p&gt;Relation to Physical Experiments&lt;/p&gt;

&lt;p&gt;In real Chladni experiments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grains bounce dynamically due to plate acceleration.
Migration occurs through complex frictional and collisional processes.&lt;/li&gt;
&lt;li&gt;Accumulation is influenced by time evolution and nonlinear interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Sand View abstracts these dynamics into a stationary probability model. It captures the final accumulation pattern without modeling transient motion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Field-based simulation; does not model particle dynamics.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This simulation visualizes the nodal patterns of a unit square membrane L_x = L_y with fixed edges, governed by the wave equation, capturing the essence of Chladni figures. In real experiments, elastic plates follow complex bending physics, but the membrane model approximates their nodal patterns using simpler wave mechanics. Focusing on nodal patterns avoids the unresolved complexity of grain motion, highlighting the universal physics of resonance, independent of material properties.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Only finite max_mode included.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Damping gamma is uniform; real plates have non-uniform damping.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Geometry is idealized unit square membrane.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Uses a membrane model for computational efficiency, which simplifies the physics of a true plate with bending stiffness.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Simulation–Experiment Match Fidelity
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode Category&lt;/th&gt;
&lt;th&gt;Typical Example(s)&lt;/th&gt;
&lt;th&gt;Match Fidelity&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exact Symmetric Modes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(2,2), (3,3), (4,4)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Perfect match: Clean axial nodal lines align exactly with experiments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Low-order Degenerate Pairs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(1,2)+(2,1), (1,3)+(3,1)&lt;/td&gt;
&lt;td&gt;~50–60%&lt;/td&gt;
&lt;td&gt;Partial match: Shows tilted/blended nodals instead of sharp crosses seen in some low-order plate patterns.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;High-order Degenerate Pairs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(3,5)+(5,3), (1,6)+(6,1)&lt;/td&gt;
&lt;td&gt;~90–95%&lt;/td&gt;
&lt;td&gt;Excellent match: Complex, interior nodals closely mimic historical figures; diagonals may flip orientation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Symmetric modes m,m are exact (100%) and visually identical to experiments.
&lt;/li&gt;
&lt;li&gt;Asymmetric modes m ≠ n always form degenerate pairs in this square model, blending into symmetric patterns—both contribute fully e.g., (1,2) and (2,1) dominate together. No isolated "single" asymmetric modes appear due to symmetry.
&lt;/li&gt;
&lt;li&gt;Low-order pairs can look less crisp; high-order excel at reproducing rich, experimental-like topology, including accidental degeneracies where symmetric modes blend with pairs (e.g., (10,10) + (2,14)/(14,2)). Larger gamma adds asymmetry from nearby modes, enhancing realism.
&lt;/li&gt;
&lt;li&gt;Ratings prioritize &lt;strong&gt;nodal patterns&lt;/strong&gt; (positions and shapes); the sim's blending captures the visual spirit of Chladni figures effectively, even if idealized.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Historical Context
&lt;/h2&gt;

&lt;p&gt;Ernst Chladni (1756–1827), known as the father of acoustics, pioneered the study of vibrational modes by observing how elastic plates, sprinkled with sand or powder, formed Chladni figures—geometric patterns along nodal lines where the surface remains stationary. These patterns reveal standing waves and remain a subject of research today.&lt;/p&gt;

&lt;p&gt;In his 1787 experiments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An elastic plate (thin brass plate) was fixed at its center or edges.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It was vibrated with a violin bow at various frequencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Particles migrated from high-vibration areas to nodal lines, creating striking patterns.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chladni figures laid foundations for acoustics, wave physics, and applications like mechanical engineering, with their nodal patterns inspiring both historical and modern scientific inquiry.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Chladni, Ernst Florens Friedrich. &lt;em&gt;Entdeckungen über die Theorie des Klanges&lt;/em&gt;. Leipzig: Weidmanns Erben und Reich, 1787. ETH-Bibliothek Zürich,&lt;br&gt;
Rar 5284. &lt;a href="https://doi.org/10.3931/e-rara-4235" rel="noopener noreferrer"&gt;https://doi.org/10.3931/e-rara-4235&lt;/a&gt;. Public Domain Mark.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Herman, Russell. Vibrations of Rectangular Membranes. (2024, September 4) University of North Carolina Wilmington. CC BY‑NC‑SA. &lt;a href="https://math.libretexts.org/@go/page/90264" rel="noopener noreferrer"&gt;https://math.libretexts.org/@go/page/90264&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wikipedia contributors. “Ernst Chladni.” Wikipedia. &lt;a href="https://en.wikipedia.org/wiki/Ernst_Chladni" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Ernst_Chladni&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Further Reading&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Abramian, A., Protière, S., Lazarus, A., &amp;amp; Devauchelle, O.&lt;br&gt;
Chladni patterns explained by the space-dependent diffusion of bouncing grains.&lt;br&gt;
Phys. Rev. Research 7, L032001 — Published 1 July,2025&lt;br&gt;
&lt;a href="https://doi.org/10.1103/PhysRevResearch.7.L032001" rel="noopener noreferrer"&gt;https://doi.org/10.1103/PhysRevResearch.7.L032001&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tuan, P.H., Lai, Y.H., Wen, C.P. et al. Point-driven modern Chladni figures with symmetry breaking. Sci Rep 8, 10844 (2018).&lt;br&gt;
&lt;a href="https://doi.org/10.1038/s41598-018-29244-6" rel="noopener noreferrer"&gt;https://doi.org/10.1038/s41598-018-29244-6&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tseng, Yu-Chen, Yu-Hsin Hsu, Yu-Hsiang Lai, Yan-Ting Yu, Hsing-Chih Liang, Kai-Feng Huang, and Yung-Fu Chen. 2021. “Exploiting Modern Chladni Plates to Analogously Manifest the Point Interaction” Applied Sciences 11, no. 21: 10094. &lt;a href="https://doi.org/10.3390/app112110094" rel="noopener noreferrer"&gt;https://doi.org/10.3390/app112110094&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Becker, R. (2025, September 12). Unveiling the hidden geometry of sound. Medium. &lt;a href="https://medium.com/@ratwolf/unveiling-the-hidden-geometry-of-sound-c3f0cf09dba5" rel="noopener noreferrer"&gt;https://medium.com/@ratwolf/unveiling-the-hidden-geometry-of-sound-c3f0cf09dba5&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>softwaredevelopment</category>
      <category>computerscience</category>
      <category>science</category>
    </item>
    <item>
      <title>An old Classic with new Perspective in 2D and 3D</title>
      <dc:creator>Rat Wolf (ratwolf)</dc:creator>
      <pubDate>Wed, 04 Dec 2024 14:31:34 +0000</pubDate>
      <link>https://dev.to/ratwolf/an-old-classic-with-new-perspective-in-3d-48af</link>
      <guid>https://dev.to/ratwolf/an-old-classic-with-new-perspective-in-3d-48af</guid>
      <description>&lt;h2&gt;
  
  
  Historical Context
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Hopalong&lt;/strong&gt;* attractor, authored by Barry Martin of Aston University in Birmingham, England [2], was popularized by A.K. Dewdney in the September 1986 issue of &lt;em&gt;Scientific American&lt;/em&gt;. In Germany, the &lt;em&gt;Hopalong&lt;/em&gt; attractor gained further popularity through an algorithm called &lt;em&gt;HÜPFER&lt;/em&gt; in the article &lt;em&gt;Psychotapeten&lt;/em&gt; published in &lt;em&gt;Spektrum der Wissenschaft&lt;/em&gt;. [3].&lt;br&gt;&lt;br&gt;
*Nicknamed by A.K. Dewdney.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python Code, detailed description and mathematics see:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ratwolfzero/hopalong_python" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hopalong Attractor Functions
&lt;/h2&gt;

&lt;p&gt;The mathematical definition of the Hopalong attractor is given by the following system of recursive functions, defined in [2]:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hfkhthh8u4o1euej8m2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hfkhthh8u4o1euej8m2.png" alt="Hopalong" width="301" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The sequence starts from the initial point (x0 , y0) = (0 , 0).&lt;/li&gt;
&lt;li&gt;xn and yn represent the coordinates at the n-th iteration of the attractor.&lt;/li&gt;
&lt;li&gt;a, b, and c are parameters influencing the attractor's dynamics.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;sgn&lt;/em&gt; is the &lt;em&gt;signum&lt;/em&gt; function. However, the programs use &lt;code&gt;math.copysign()&lt;/code&gt;, which is defined as follows:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqqk551mxeho18dgnydy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqqk551mxeho18dgnydy.png" alt="Copysign" width="606" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Computational Approach
&lt;/h2&gt;

&lt;p&gt;The available Python programs calculate and visualize the attractor by iterating the described function system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Visual Representation&lt;/strong&gt;:&lt;br&gt;
The attractor is rendered as a density heatmap, with color intensity reflecting areas of high or low point concentration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large numbers of iterations are computed with low memory usage and high processing speed.&lt;/li&gt;
&lt;li&gt;Just-in-time (JIT) compilation is applied and supported by a low-complexity code structure.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Core Algorithm:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The algorithm consists of two main passes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;First Pass&lt;/strong&gt;: Determines the spatial extent of the attractor trajectory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Second Pass&lt;/strong&gt;: Maps the sequentially generated trajectory points in continuous space directly to a discrete pixel grid, keeping track of the number of pixel hits used to generate the density heatmap matrix. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqg2pyibs93ftqrd6qjw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqg2pyibs93ftqrd6qjw.png" alt="Hopalong Attractor 2D" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvlb3jg35p8vo9tuypj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvlb3jg35p8vo9tuypj8.png" alt="Hopalong Attractor 3D" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;[1]&lt;/strong&gt; J. Lansdown and R. A. Earnshaw (eds.), &lt;em&gt;Computers in Art, Design and Animation&lt;/em&gt;. New York: Springer-Verlag, 1989.&lt;br&gt;
e-ISBN-13: 978-1-4612-4538-4.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[2]&lt;/strong&gt; Barry Martin, "Graphic Potential of Recursive Functions," in &lt;em&gt;Computers in Art, Design and Animation&lt;/em&gt; [1], pp. 109–129.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[3]&lt;/strong&gt; A.K. Dewdney, &lt;em&gt;Psychotapeten&lt;/em&gt;, algorithm &lt;em&gt;HÜPFER&lt;/em&gt;, in &lt;em&gt;Spektrum der Wissenschaft: Computer Kurzweil&lt;/em&gt;.&lt;br&gt;
Spektrum der Wissenschaft Verlagsgesellschaft mbH &amp;amp; Co., Heidelberg, 1988.&lt;br&gt;
(German version of &lt;em&gt;Scientific American&lt;/em&gt;).&lt;br&gt;
ISBN-10: 3922508502, ISBN-13: 978-3922508502.&lt;/p&gt;

</description>
      <category>python</category>
      <category>attractors</category>
      <category>hopalong</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
