.

I missed Tau day, which was the 28th of June. However, I've been thinking about the intersection of Tau, complex numbers, code libraries and Javascript in analytics for a while now, and this seemed like a good chance to explore one of my favorite topics: Tauism. -- Kurt Cagle

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πr

and

A=π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^(π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= xn--
  • Euler's equation (which laid the foundation for complex numbers), can be expressed with τ as:

e^(τ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;

A=(1/2)τr^2 ; E = (1/2) mv^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θ) = cos(θ) + i sin(θ), where 0 < θ < τ

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.

No alt text provided for this image

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: ijand 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.

No alt text provided for this image

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.

No alt text provided for this image

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.

No alt text provided for this image

A Library For Tau Based Complex Number Operations

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)/span>1e-5?0:TauComplex.trim(this.x);
            let minY = Math.abs(this.y)/span>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/span>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 = (acccur=> 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 = (acccur=> 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 { TauTauComplex } = 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.

Views: 445

Tags: dsc_javascript, dsc_mathematics

Comment

You need to be a member of Data Science Central to add comments!

Join Data Science Central

© 2021   TechTarget, Inc.   Powered by

Badges  |  Report an Issue  |  Privacy Policy  |  Terms of Service