Note
Go to the end to download the full example code.
Duffing Oscillator#
The forced, damped Duffing oscillator models a particle in a double-well potential driven by a periodic force,
or, as the first-order system in \((x, v)\) actually integrated,
\(\dot{x} = v\), \(\dot{v} = -\delta v - \alpha x - \beta x^3 +
\gamma \cos(\omega t)\). Here \(\delta\) is the damping coefficient,
\(\alpha\) and \(\beta\) set the (linear and cubic) restoring force
– with \(\alpha<0\) and \(\beta>0\), as used below, the potential
\(V(x)=\tfrac12\alpha x^2+\tfrac14\beta x^4\) has two wells – and
\(\gamma\), \(\omega\) are the forcing amplitude and frequency. For
the classic chaotic parameter set below, its long-time trajectory settles
onto a strange attractor in the (x, v) phase plane.
Integrate#
Discard an initial transient so the plotted trajectory has already settled onto the attractor.
Phase portrait#

Quantifying it: a fractal attractor#
The dense-but-structured phase portrait above is a strange attractor: its box-counting (capacity) dimension \(D_0\) and correlation dimension \(D_2\) (see Fractal Dimension: Box-Counting and Correlation Dimension for both estimators applied to the Henon map) are non-integers, between the curve (\(D=1\)) a single trajectory traces out locally and the 2D plane it never quite fills. Only every 4th stroboscopic-ish sample is kept for the correlation-dimension estimate, since it computes all \(O(n^2)\) pairwise distances.
d0, eps0, counts0 = box_counting_dimension(states)
d2, eps2, csum2 = correlation_dimension(states[::4])
print(f"Duffing attractor: box-counting D0 = {d0:.3f}, correlation D2 = {d2:.3f}")
fig2, axes2 = plt.subplots(1, 2, figsize=(12, 5))
axes2[0].loglog(1.0 / eps0, counts0, "o-", color="steelblue")
axes2[0].set_xlabel(r"$1/\epsilon$")
axes2[0].set_ylabel(r"$N(\epsilon)$")
axes2[0].set_title(f"Box counting: D0 = {d0:.3f}")
axes2[1].loglog(eps2, csum2, "o-", color="darkorange")
axes2[1].set_xlabel(r"$\epsilon$")
axes2[1].set_ylabel(r"$C(\epsilon)$")
axes2[1].set_title(f"Correlation sum: D2 = {d2:.3f}")
fig2.suptitle("Duffing attractor: fractal dimension between 1 (curve) and 2 (plane)")
fig2.tight_layout()
plt.show()

Duffing attractor: box-counting D0 = 1.180, correlation D2 = 1.301
Total running time of the script: (0 minutes 1.064 seconds)