The Tao of Tau

Kurt Cagle 0

One of the things that most young mathematicians (think eight years old or so) learn is that pie are square, a fact which is immediately refuted with the confused observation "No they're not! Pies are round!" Later, of course, they discover algebra and geometry, where in fact it is proven demonstrably that, well, the circumference of a circle and the area of the same circle are related by two simple equations:

C = 2\pi r

and

A = \pi r^2

The first law was at least known at the time of Euclid, though it would be a few centuries later that Archimedes actually calculated the ratio of the diameter (twice the radius) to the circumference to be roughly 3 1/7 (though in fact, he was able to get it right to within eight digits eventually, in Greek numeric notation). However, it would be early in the eighteen century AD that Welsh mathematician William Jones would use the Greek letter π (pi) for this particular ratio.

The great mathematician Leonhard Euler would eventually pull together the use of π, the base of the natural logarithms (e, approximately 2.71828) and the square root of negative one (called i) into one of the more fundamental equations of mathematics:

e^{\pi i} = -1

For the next three hundred years, π would reign supreme in mathematics as perhaps the single most well known constant ever. However, for several years, Euler debated whether to use π to indicate the circumference of a circle divided by the diameter (a ratio of about 3.1415927...) or to indicate the circumference divided by the ratio (a ratio of 6.2831854...). His decision, ultimately, to go with the former may have complicated mathematics far more than he'd planned.

In 2010, mathematician Michael Hartl wrote a paper that would eventually become known as the Tau Manifesto, in which he proposed using the letter tau (τ) for the ratio of the circumference divided by radius, the aforementioned 6.28 value. His arguments were actually quite compelling:

  • One τ is precisely one turn around a circle, or one revolution (360°). τ/2 is half a circle or 180°, τ/4 is one fourth of a circle, or 90° and so forth. Compare this with 2π, π, and π/2 respectively. In general, this becomes the same as C= τr, as opposed to C= 2πr.
  • Euler's equation (which laid the foundation for complex numbers), can be expressed with τ as:
e^{\tau i} = 1
  • Similarly, the area of a circle takes on the same characteristics as other power laws in mathematics and physics:
A = \dfrac{1}{2} \tau r^2; E= \dfrac{1}{2} m v^2; 

Where it really shines, however, is in areas such as trigonometry and complex matrix algebra. For instance, if you had trigonometric functions built around τ, you can state that

e^{i \theta} = cos(\theta) + i sin(\theta),\\ where \space 0 \le \theta \le \tau

This equation makes it clear that a quarter of the way through a revolution, the equation has the value +i, at halfway it's -1, at 3/4 of the revolution, the equation is -i, and at one full revolution you're right back to where you started. It also highlights the close association between exponential functions and rotations.

This becomes especially useful in gaming and 3D applications, in which the square root of -1 (the i part) is treated as an axis of rotation. In three dimensions, you end up with three such axes: i, j, and k. These are known as quaternions. If each of these are used to describe a complex number (such as 0.5 + 0.866i) on the unit sphere (a great circle), then by performing a rotation on that circle (from 0 to 1 τ ), you can position that object precisely along that rotation. Camera operators (and pilots) understand these three dimensions as pitch, yaw and roll.

These quaternions become especially important when dealing with rigging in three-dimensional work, involving vectors connecting end to end. These are also known as gimbals, again from the camera mounts that allow for orienting in any dimension. While in data science this can be done using matrix transformations, quaternion calculations are often faster and less time consuming to set up.

The skeleton of a 3D figure is made up of vectors, with quaternions acting as gimbals at the joints to help move the figure around.

The value of Tau (and the relationship between Tau revolutions and complex numbers, inspired me to write a small Javascript library that both defined some Tau helper functions (the Tau class, made up exclusively of static) and an immutable TauComplex class that was built specifically with Tau revolutions in mind. These scripts are available at https://github.com/kurtcagle/tau.

class Tau {     
    static TAU = 2 * Math.PI
    static TAU_2 = this.TAU/2
    static TAU_4 = this.TAU/4
    static TAU_6 = this.TAU/6
    static TAU_8 = this.TAU/8
    static TAU_12 = this.TAU/12
    constructor(){
        // This consists solely of static methods and constants
    }
    // converts a revolution to degrees
    static toDegrees(rev){return rev * 360}
    // converts a revolution to radians
    static toRadians(rev){return rev * this.TAU}
    // converts from degrees to revolutions
    static fromDegrees(deg){return deg / 360}
    // converts from radians to revolutions
    static fromRadians(rad){return rad / this.TAU}
    // returns the sine value of the given revolution
    static sin(rev){
        return Math.sin(rev * this.TAU)
    }
    // returns the cosine value of the given revolution
    static cos(rev){
        return Math.cos(rev * this.TAU)
    }
    // returns the tangent value of the given revolution
    static tan(rev){
        return Math.tan(rev * this.TAU)
    }
    // returns the arcsine value of the given revolution
    static asin(rev){
        return this.fromRadians(Math.asin(rev))
    }
    // returns the arccosine value of the given revolution
    static acos(rev){
        return this.fromRadians(Math.acos(rev))
    }
    // For a given x,y value, returns the corresponding revolution from -0.5 to 0.5.
    static atan(x,y){
        return this.fromRadians(Math.atan2(y,x))    }

}

class TauComplex{
    // Indicates the number of significant digits complex numbers are displayed using.
    static SIGDIGITS = 5;
    constructor(x,y){
        this.x = x
        this.y = y
        return this
    }
    // toString() generates a complex number of the form "a+bi" for string output
    toString(){
        let minX = Math.abs(this.x)<1e-5?0:TauComplex.trim(this.x);
        let minY = Math.abs(this.y)<1e-5?0:TauComplex.trim(this.y);
        return `${minX} ${Math.sign(this.y)>=0?'+':'-'} ${Math.abs(minY)}i`
    }
    // generates the length of the complex number vector
    get modulus(){
        return Math.sqrt(this.x*this.x + this.y*this.y);
    }
    // generates the square of the length of the complex number vector. This avoids the need to take the square root
    get modsquare(){
        return this.x*this.x + this.y*this.y;
    }
    // retrieves the angle relative to the positive x axis of the complex number, in revolutions
    get theta(){
        let angle = Tau.atan(this.x,this.y);
        let ySgn = Math.sign(this.y);
        let adjAngle = ySgn<0?1+angle:angle;
        return adjAngle;
    }
    // retrieves the complex conjugate (a-bi) of the complex number (a+bi)
    get conjugate(){
        return new TauComplex(this.x,-this.y)
    }
    // retrieves the complex inverse of the number (a+bi).
    get inverse(){
        return (this.conjugate).scale(1/this.modsquare)
    }
    // rotates the complex number through the angle, expressed in revolutions.
    rotate(angle){
        let newX = this.x * Tau.cos(angle) - this.y * Tau.sin(angle);
        let newY = this.x * Tau.sin(angle) + this.y * Tau.cos(angle)
        return new TauComplex(newX,newY)
    }
    // Multiplies the complex number by a scalar value (or values if two arguments are supplied)
    scale(x,y=x){
        let newX = this.x * x;
        let newY = this.y * y;
        return new TauComplex(newX,newY)
    }
    // translates the complex number by the given amount. Equivalent to adding two complex numbers
    translate(x,y=x){
        let newX = this.x + x;
        let newY = this.y + y;
        return new TauComplex(newX,newY)
    }
    // Adds two or more complex numbers together.
    static sum(...c){
        let reducer = (acc, cur) => new TauComplex(acc.x+cur.x,acc.y+cur.y)
        return c.reduce(reducer)
    }
    // Multiples two or more complex numbers together.
    static mult(...c){
        let reducer = (acc, cur) => new TauComplex(acc.x*cur.x-acc.y*cur.y,acc.x*cur.y+acc.y*cur.x)
        return c.reduce(reducer)
    }
    // Divides the first complex number by the second
    static div(c1,c2){
        return TauComplex.mult(c1,c2.inverse)
    }
    // Takes the complex number to the given power. Power MUST be a non-negative integer.
    pow(power){
        let arr = [];
        for (var index=0;index!=power;index++){
            arr.push(this);
        }
        if (arr.length>0) {
            return TauComplex.mult(...arr)
        }
        else {
            return new TauComplex(1,0);
        }
    }
    // Returns the real portion of a complex number
    get re(){
        return this.x
    }
    // Returns the imaginary portion of a complex number
    get im(){
        return this.y
    }
    // Returns the complex number associated with a unit vector rotated by the revolution amount
    static tau(rev){
        return new TauComplex(Tau.cos(rev),Tau.sin(rev));
    }
    // Returns the complex exponent of the given complex number
    get exp(){
        return TauComplex.tau(this.y).scale(Math.exp(this.x))
    }
    // Creates a string representation of a number to the given significant digits, default being 5.
    static trim(value,sigDigits=this.SIGDIGITS){
        return value.toLocaleString("en-us",{maximumSignificantDigits:sigDigits})
    }
    static array(...arr){
        return arr.map((subArr,index)=>new TauComplex(...subArr))
    }
}
const _TauComplex = TauComplex;
exports.TauComplex = _TauComplex;
const _Tau = Tau;
exports.Tau = _Tau;

The complex class is reasonably complete for complex number manipulation, including handling addition and multiplication of complex numbers, creating complex conjugates, moduli, and equations for rotating, scaling and translating such numbers in the complex plane. A test script (TauTest.js) illustrates how these functions are invoked:

const { Tau, TauComplex } = require('./tau');
TauComplex.SIGDIGITS = 3
const c1 = new TauComplex(1,2);
const c2 =  c1.rotate(0.25);
const c3 = TauComplex.sum(c1,c2);
const c4 = new TauComplex(0,-1);
const c5 = new TauComplex(1,3/8);
const cArr = TauComplex.array([1,0],[0,1],[-1,0],[0,-1])

console.log(`c1: ${c1}`);
console.log(`c2: ${c2}`);
console.log(`c3: ${c3}`);
console.log(`c4: ${c4}`);
console.log(`modulus of c3: ${c3.modulus}`)
console.log(`modulus squared of c3: ${c3.modsquare}`)
console.log(`theta of c3: ${c3.theta}`)
console.log(`conjugate of c3: ${c3.conjugate}`)
console.log('c1 + c2: '+TauComplex.sum(c1,c2))
console.log('c1 * c2: '+TauComplex.mult(c1,c2))
console.log('c1 ^ 2: '+c1.pow(2))
console.log('c1 ^ 3: '+c1.pow(3))
console.log('1 / c1: '+c1.inverse)
console.log(`c1 / c3: ${TauComplex.div(c1,c3)}`)
console.log(`exp(c5): ${c5.exp}`)
console.log('c1 scale 2: '+c1.scale(2));
console.log('c1 translate 2,3: '+c1.translate(2,3));
for (index=0;index<=12;index++){
    console.log(`c4 rotate ${index}/12 or ${Tau.toDegrees(index/12)}: ${c4.rotate(index/12)}`)
}
console.log(`Complex Array: ${cArr}`);

with this test script generating the following output :

 c1: 1 + 2i
 c2: -2 + 1i
 c3: -1 + 3i
 c4: 0 - 1i
 modulus of c3: 3.1622776601683795
 modulus squared of c3: 10
 theta of c3: 0.30120819117478337
 conjugate of c3: -1 - 3i
 c1 + c2: -1 + 3i
 c1 * c2: -4 - 3i
 c1 ^ 2: -3 + 4i
 c1 ^ 3: -11 - 2i
 1 / c1: 0.2 - 0.4i
 c1 / c3: 0.5 - 0.5i
 exp(c5): -1.92 + 1.92i
 c1 scale 2: 2 + 4i
 c1 translate 2,3: 3 + 5i
 c4 rotate 0/12 or 0: 0 - 1i
 c4 rotate 1/12 or 30: 0.5 - 0.866i
 c4 rotate 2/12 or 60: 0.866 - 0.5i
 c4 rotate 3/12 or 90: 1 - 0i
 c4 rotate 4/12 or 120: 0.866 + 0.5i
 c4 rotate 5/12 or 150: 0.5 + 0.866i
 c4 rotate 6/12 or 180: 0 + 1i
 c4 rotate 7/12 or 210: -0.5 + 0.866i
 c4 rotate 8/12 or 240: -0.866 + 0.5i
 c4 rotate 9/12 or 270: -1 + 0i
 c4 rotate 10/12 or 300: -0.866 - 0.5i
 c4 rotate 11/12 or 330: -0.5 - 0.866i
 c4 rotate 12/12 or 360: 0 - 1i
 Complex Array: 1 + 0i,0 + 1i,-1 + 0i,0 - 1i

One of the critical points about this library is that the complex numbers are meant to be treated as immutable, with any operation on a complex number generating a new complex number. For instance, if you wanted to add three complex numbers, you'd create an expression such as:

const c6 = TauComplex.sum(c1,c2,c3)

=> c6 = Sum of (c1,c2,c3): -2 + 6i

Complex numbers factor heavily in both pure and applied mathematics, and can be proved to be the largest complete set of numbers - you cannot construct another space of numbers that cannot be decomposed into complex numbers. Additionally, the equations used to create approximations of surfaces, known as the Taylor Series, makes extensive use of complex numbers.

There is one final point to be made in this particular article. There is a tendency to look at either Python or R when dealing with mathematics, but it's worth noting that it is perfectly possible to create mathematics libraries and classes in JavaScript as well. Indeed, Javascript contains its own equivalents of both Pandas (Danfo.js) and NumPy (nympy.js), two of the foundational classes, and Google's TensorFlow library has been ported over to Javascript as well (primarily around the node.js version). In some cases, Javascript is even faster than Python in the analytics space, especially given recent optimizations for handling binary data types.