Ridgerunner: Knot tightening software

Ridgerunner is a program for tightening knots created by Jason Cantarella, Eric Rawdon, and Michael Piatek. We used the software to create the tables of tight knots in Knot Tightening By Constrained Gradient Descent and The Shapes of Tight Composite Knots (see Papers) and the movies at Ridgerunner Movies, so it’s fairly battle-tested by now. Actual crashes (segfaults) are quite uncommon, though the software can get to a point where the linear algebra becomes so degenerate that it can go no further. A helpful hint when starting out is to avoid configurations which have a lot of self contact in their starting position, or a lot of symmetry. For instance, a Hopf link formed from two round circles will kill ridgerunner instantly because there are too many initial self-contacts.

The latest data files produced by ridgerunner for tight knots and links are freely available in the subversion repository at http://www.jasoncantarella/TightKnot2. The current best files for knots and links are in the “best” directory.


The best way to install ridgerunner is to use Homebrew. You need to install our tap first, so the commands are:

brew tap designbynumbers/cantarellalab
brew install ridgerunner

This is tested on Fedora 34 and Mac OS Sonoma. Homebrew works with MacOS and most flavors of Linux. The best solution for Windows users is probably to install a Linux virtual machine, rent a Linux system from Amazon EC2, or use your university computing center. The homebrew formula will install all needed dependencies for ridgerunner.

Ridgerunner is also available from GitHub as an open-source project.

Using Ridgerunner

The full list of command-line arguments for ridgerunner is somewhat intimidating. If you just type ridgerunner (after a successful install), you’ll get a lot of possible arguments. Luckily, very few are needed to start tightening some knots. A simple session with ridgerunner looks like this.

>d448.math.uga.edu:tmp> ridgerunner $(brew --prefix)/share/ridgerunner/3.1.vect --StopSteps=100 --EqOn --MaxVectDirSize=1M 
Ridgerunner 2.1.1
Built Sep 30 2022, 15:35:47.
plCurve Version: 10.1.0
Octrope Version: 10.1.0
tsnnls Version: 2.4.5
Seeded from dev/random with seed 1515069954.
Loaded 1 component, 47 vertex plCurve from /usr/local/share/ridgerunner/3.1.vect.
Saved copy of /usr/local/share/ridgerunner/3.1.vect to ./3.1.rr/3.1.vect.
Rerez'd, autoscaled, eq'd file written to ./3.1.rr/3.1.atstart.vect.
Overstep tolerance: 0.000100 of thickness 0.500000 (0.499950) minminrad: 0.49997500
ridgerunner: Curses screen display disabled, using stdout.
             Define CURSES_DISPLAY to enable better screen display.
ridgerunner: Starting run. Will stop if 
               step number >= 100 
               residual < 0 
               ropelength decrease over last 20 steps < -1000.

Resolution = 1.28181 < 1.5. After splining, resolution = 2.03766.
Rescaled to thickness 0.51.
   1 Rop: 36.57021297757 Str:   1 MRstruts:   0 Thi: 0.4999500 
   2 Rop: 36.46753103403 Str:   2 MRstruts:   0 Thi: 0.4999763 
   3 Rop: 36.28892471903 Str:   9 MRstruts:   0 Thi: 0.4945568 
   4 Rop: 36.15314951543 Str:   4 MRstruts:   0 Thi: 0.4996298 
   5 Rop: 36.15264205469 Str:   1 MRstruts:   0 Thi: 0.5000000 
   6 Rop: 36.15264632684 Str:   2 MRstruts:   0 Thi: 0.4999999 
   7 Rop: 36.13635798955 Str:   3 MRstruts:   0 Thi: 0.4999972 
   8 Rop: 36.10530413160 Str:   4 MRstruts:   0 Thi: 0.4999956 
   9 Rop: 36.00997329062 Str:   5 MRstruts:   0 Thi: 0.4999855 
  10 Rop: 35.79402988860 Str:  30 MRstruts:   0 Thi: 0.4927720 
  11 Rop: 35.74911258608 Str:   4 MRstruts:   0 Thi: 0.4998335 
  12 Rop: 35.74894955301 Str:   1 MRstruts:   0 Thi: 0.5000000 
  13 Rop: 35.74895173811 Str:   2 MRstruts:   0 Thi: 0.4999999 
  14 Rop: 35.74825229937 Str:   3 MRstruts:   0 Thi: 0.4999999 
  94 Rop: 34.96259886278 Str:   2 MRstruts:   0 Thi: 0.5000000 
  95 Rop: 34.95994711702 Str:   3 MRstruts:   0 Thi: 0.4999999 
  96 Rop: 34.95988921071 Str:   4 MRstruts:   0 Thi: 0.4999999 
  97 Rop: 34.95978024039 Str:   5 MRstruts:   0 Thi: 0.4999999 
  98 Rop: 34.95973255963 Str:   6 MRstruts:   0 Thi: 0.4999999 
  99 Rop: 34.95945553222 Str:   7 MRstruts:   0 Thi: 0.4999999 
 100 Rop: 34.95934049672 Str:   8 MRstruts:   0 Thi: 0.4999999 
ridgerunner: run complete. Terminated because 
ridgerunner: reached maximum number of steps (100).
ridgerunner: Run complete.

We used three options here: --EqOn, which keeps the edges of the polygon approximately the same size as the knot shrinks, --StopSteps=100, which sets the number of steps to 100, and --MaxVectDirSize=1M, which tells ridgerunner that we don’t want to store the geometry from every minimization step if the total size of the stored geometry will be larger than 1 mb. (Ridgerunner will intelligently thin the stored data to give you a set of geometry snapshots spread approximately evenly across the run.)

$(brew --prefix)/share/ridgerunner/3.1.vect

was the name of the input curve. (There’s a complete knot table in that directory for you to play with). The results appear in a subdirectory of wherever you ran ridgerunner from. If you ran knot.vect, the results will be in knot.rr.

>cd 3.1.rr/
> ls
3.1.atstart.vect	3.1.final.vect		3.1.vect		snapshots/
3.1.final.struts	3.1.log			logfiles/		vectfiles/

The results are not particularly dramatic. On the left, we see 3.1.atstart.vect (with a tube around it) and on the right we see 3.1.final.vect (with a tube around it), which is the final output of ridgerunner (assuming that the run completed normally).


Still, if you can do this, you can (in principle) do anything in knot tightening.

You can use multiple stopping criteria together. For instance, in production work, we usually used

ridgerunner -a myfile.vect –StopRes=0.01 –StopTime=2880 –StopSteps=500000

which stops when the residual of the file is 0.01 (that is, the elastic force on the knot is 99% balanced by tube contacts), or when two days (2880 minutes) have passed, or when 500,000 steps have been taken, whichever comes first.

Advanced Options

Many of the advanced options are mostly useful for debugging or historical reasons, but several are worth discussing.

-l, –Lambda=<double> minimum radius of curvature for unit rope

If you are studying stiff rope, with radius of curvature bounded below, you can set the minimum radius of curvature of the rope with this option. Note that the rope has diameter 1 in ridgerunner, so the default setting for lambda is 0.5 (fully flexible rope).

–MangleMode instead of trying to reduce length, try to change configuration
–MangleSteps=<#steps> switch mangling algorithm after this many steps

This is still experimental, but is currently useful enough to document. MangleMode will not try to shrink the knot, but instead turns it inside out by flowing around a randomly selected torus whose center is near the center of mass of the polygon. This is often very effective in finding different local minima for a given knot type.

–EqOn automatically equilateralizes when max/min edge > 3
–EqMultiplier=<scalar> increase to make ‘equilateralization force’ stronger
–EqForceOn turn on equilateralization force during the run (can interfere with stepper)

One of these options should always be chosen during a long run. The difference between them is that EqForceOn will attempt to maintain a perfectly equilateral polygon during the entire run by adding a penalty function which makes non-equilateral edges undesirable. This can interfere with the stepper, and slows down convergence of the run. On the other hand, EqOn monitors the run and essentially rediscretizes the polygon and restarts from the new configuration whenever the longest edge is more than 3 times the length of the shortest edge. This does not look good in a movie, but is more effective during minimization.

–SnapshotInterval=<n> save a complete snapshot of computation every n steps

This is basically a debugging option. In myfile.rr/snapshots, ridgerunner will store everything about your run every n steps. This can be very useful for debugging, but can also take up a lot of disk space. Setting SnapshotInterval to 100000 or so will essentially turn off snapshots.

–Symmetry=Z/pZ,D2 rotation/reflection symmetry group (around z-axis) to enforce on curve

Ridgerunner will enforce a rotational symmetry (around the z axis) or a reflection symmetry (over the x-y plane) on curves during a run. The curves must be carefully generated so that as polygons, vertex-by-vertex they have the desired symmetry in the input configuration. Ridgerunner will then maintain this symmetry during the run.

–Display=ropelength/strutcount/thickness name of logfile to display on screen during run

In the directory myfile.rr/logfiles, ridgerunner will store logs of various quantities that might be of interest during a run. Everything from the variance of edgelength (edgelengthvariance) to the memory used by ridgerunner (memused) is logged. You can display these logs as the program runs by adding –Display=<logfilename> to the command line. The most commonly used of these options is –Display=residual, since the residual is usually a stopping criterion.

Development Roadmap

Ridgerunner is a mature piece of software which is essentially complete. Sylwek Przybyl has (private) software which operates at much higher resolutions than ridgerunner, and is the preferred path forward for very detailed images of specific tight knots and links. For most scientific purposes, the ridgerunner tightenings should be sufficient. Developing ridgerunner is currently focused on making the tool more easily available to new users by integrating it into package managers (brew), and other pieces of software (knotplot) and, of course, bugfixes. If you’re interested in building interfaces (to Sage or Mathematica), virtual machines and appliances, or a web interface, we’re very open to collaboration!