steppyngstounes / ˈstɛp ɪŋˌstoʊnz /
1. pl. n. [Middle English] Stones used as steps of a stairway; also, stones in a stream used for crossing. [1]
…while at Calais in 1474 we find 40 ‘steppyngstounes’ bought for the stairways of the town. [2]
2. n. [chiefly Pythonic] A package that provides iterators for advancing from start to stop, subject to algorithms that depend on user-defined value or error.
Computations that evolve in time or sweep a variable often boil down to a control loop like
for step in range(steps):
do_something(step)
or
t = 0
while t < totaltime:
t += dt
do_something(dt)
which works well enough, until the size of the steps needs to change. This can be to save or plot results at some fixed points, or because the computation becomes either harder or easier to perform. The control loop then starts to dominate the script, obscuring the interesting parts of the computation, particularly as different edge cases are accounted for.
Packages like odeint address many of these issues, but do so through callback functions, which effectively turn the computation of interest inside out, again obscuring the interesting bits. Further, because they are often tailored for applications like solving ordinary differential equations, applying them to other stepping problems, even solving partial differential equations, can be rather opaque.
The steppyngstounes package is designed to retain the simplicity of the original control loop, while allowing great flexibility in how steps are taken and automating all of the aspects of increasing and decreasing the step size.
A steppyngstounes control loop can be as simple as
from steppyngstounes import FixedStepper
for step in FixedStepper(start=0., stop=totaltime, size=dt):
do_something(step.size)
_ = step.succeeded()
which replicates the while
construct above, but further ensures
that totaltime
is not overshot if it isn’t evenly divisible by dt
.
Attention
The call to succeeded()
informs the
Stepper
to advance, otherwise it will
iterate on the same step indefinitely.
Rather than manually incrementing the control variable (e.g., t
), the
values of the control variable before and after the step are available as
the Step
attributes
begin
and
end
. The attribute
size
is a shorthand for
step.end - step.begin
.
If the size of the steps should be adjusted by some characteristic of the
calculation, such as the change in the value since the last solution, the
error (normalized to 1) can be passed to
succeeded()
, causing the
Stepper
to advance (possibly adjusting
the next step size) or to retry the step with a smaller step size.
from steppyngstounes import SomeStepper
old = initial_condition
for step in SomeStepper(start=0., stop=totaltime, size=dt):
new = do_something_else(step.begin, step.end, step.size)
err = (new - old) / scale
if step.succeeded(error=err):
old = new
# do happy things
else:
# do sad things
A hierarchy of Stepper
iterations enables
saving or plotting results at fixed, possibly irregular, points, while
allowing an adaptive Stepper
to find the
most efficient path between those checkpoints.
from steppyngstounes import CheckpointStepper, SomeStepper
old = initial_condition
for checkpoint in CheckpointStepper(start=0.,
stops=[1e-3, 1, 1e3, 1e6]):
for step in SomeStepper(start=checkpoint.begin,
stop=checkpoint.end,
size=checkpoint.size):
new = do_something_else(step.begin, step.end, step.size)
err = (new - old) / scale
if step.succeeded(error=err):
old = new
# do happy things
else:
# do sad things
save_or_plot()
_ = checkpoint.succeeded()
A variety of stepping algorithms are described and demonstrated in the
documentation of the individual steppyngstounes
classes.