Skip to content

addictedcoder0/math-as-code

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

math-as-code

This is a reference to ease developers into mathematical notation by showing comparisons with JavaScript code.

Motivation: Academic papers can be intimidating for self-taught game and graphics programmers. :)

This guide is not yet finished. If you see errors or want to contribute, please open a ticket or send a PR.

Note: For brevity, some code examples make use of npm packages. You can refer to their GitHub repos for implementation details.

foreword

Mathematical symbols can mean different things depending on context and the field of study (linear algebra, set theory, etc). This guide may not cover all uses of a symbol, but PRs are welcome.

For a more complete list, refer to Wikipedia - List of Mathematical Symbols.

For simplicity, many of the code examples here operate on floating point values and are not numerically robust. For more details on why this may be a problem, see Robust Arithmetic Notes by Mikola Lysenko.

contents

variable naming conventions

There are a variety of naming conventions depending on the context and field of study, and they are not always consistent. However, in some of the literature you may find variable names to follow a pattern like so:

  • s - italic lowercase letters for scalars (e.g. a number)
  • x - bold lowercase letters for vectors (e.g. a 2D point)
  • A - bold uppercase letters for matrices (e.g. a 3D transformation)
  • θ - italic lowercase Greek letters for constants and special variables (e.g. polar angle θ, theta)

This will also be the format of this guide.

equals symbols

There are a number of symbols resembling the equals sign =. Here is a few common ones and an example of their use:

  • = is for equality (values are the same)
  • is for inequality (value are not the same)
  • is for approximately equal to (π ≈ 3.14159)

In JavaScript:

// equality
2 === 3

// inequality
2 !== 3

// approximately equal
almostEqual(Math.PI, 3.14159, 1e-5)

function almostEqual(a, b, epsilon) {
  return Math.abs(a - b) <= epsilon
}

In mathematics, the := =: and = symbols are used for definition. The following defines x to be another name for 2kj.

equals1

In code, we can use = to define our variables and provide aliases. The above defines the following:

var x = 2 * k * j
var k = x / (2 * j)
var j = x / (2 * k)

dot & cross

The dot · and cross × symbols have different uses depending on context.

They might seem obvious, but it's important to understand the subtle differences before we continue into other sections.

scalar multiplication

Both symbols can represent simple multiplication of scalars. The following are equivalent:

dotcross1

In programming languages we tend to use asterisk for multiplication:

var result = 3 * 4

With adjacent letter variables, the multiplication sign is typically omitted.

dotcross2

If these variables represent scalars, the code would be:

var result = 3 * k * j

vector multiplication

To denote multiplication of one vector by another, or multiplication of a vector with a scalar, we do not use the dot · or cross × symbols. These have different meanings in linear algebra, discussed shortly.

Let's take our earlier example but apply it to vectors:

dotcross3

Here is how it would look in code, using arrays [x, y] to represent the 2D vectors.

var s = 3
var k = [ 1, 2 ]
var j = [ 2, 3 ]

var tmp = multiply(k, j)
var result = multiplyScalar(tmp, s)
//=> [ 6, 18 ]

Our multiply and multiplyScalar functions look like this:

function multiply(a, b) {
  return [ a[0] * b[0], a[1] * b[1] ]
}

function multiplyScalar(a, scalar) {
  return [ a[0] * scalar, a[1] * scalar ]
}

Similarly, matrix multiplication typically does not use a dot or cross symbol. Matrix multiplication will be covered in a later section.

dot product

The dot symbol · can be used to denote the dot product of two vectors. Sometimes this is called the scalar product since it evaluates to a scalar.

dotcross4

It is a very common feature of linear algebra, and with a 3D vector it might look like this:

var k = [ 0, 1, 0 ]
var j = [ 1, 0, 0 ]

var d = dot(k, j)
//=> 0

The result 0 tells us our vectors are perpendicular. Our dot function:

function dot(a, b) {
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}

cross product

The cross symbol × can be used to denote the cross product of two vectors.

dotcross5

In code, it would look like this:

var k = [ 0, 1, 0 ]
var j = [ 1, 0, 0 ]

var result = cross(k, j)
//=> [ 0, 0, -1 ]

Here, we get [ 0, 0, -1 ], which is perpendicular to both k and j.

Our cross function:

function cross(a, b) {
  var ax = a[0], ay = a[1], az = a[2],
    bx = b[0], by = b[1], bz = b[2]

  var rx = ay * bz - az * by
  var ry = az * bx - ax * bz
  var rz = ax * by - ay * bx
  return [ rx, ry, rz ]
}

For other implementations of vector multiplication, cross product, and dot product:

sigma

The big Greek "E" (Sigma) is for Summation. In other words: summing up some numbers.

sigma

Here, i=1 says to start at 1 and end at the number above the Sigma, 100. These are the lower and upper bounds, respectively. The i to the right of the "E" tells us what we are summing. In code:

var n = 100
var sum = 0
for (var i = 1; i <= n; i++) {
  sum += i
}

The result of sum is 5050.

Tip: With whole numbers, this particular pattern can be optimized to the following:

var n = 100
var sum = (n * (n + 1)) / 2

Here is another example where the i, or the "what to sum," is different:

sum2

In code:

var n = 100
var sum = 0
for (var i = 1; i <= n; i++) {
  sum += (2 * i + 1)
}

The result of sum is 10200.

The notation can be nested, which is much like nesting a for loop. You should evaluate the right-most sigma first, unless the author has enclosed them in parentheses to alter the order.

sigma3

In code:

var sum = 0
for (var i = 1; i <= 2; i++) {
  for (var j = 4; j <= 6; j++) {
    sum += (3 * i * j)
  }
}

Here, sum will be 135.

capital Pi

The capital Pi or "Big Pi" is very similar to Sigma, except we are using multiplication to find the "products of sequences."

Take the following:

capitalPi

In code, it might look like this:

var n = 6
var value = 1
for (var i = 1; i <= n; i++) {
  value *= i
}

Where value will evaluate to 720.

pipes

Pipe symbols, known as bars, can mean different things depending on the context. Below are three common uses: absolute, Euclidean norm, and determinant.

These three features all describe the length of an object.

absolute

pipes1

For a number x, | x | means the absolute of x. In code:

var x = -5
var result = Math.abs(x)
// => 5

Euclidean norm

pipes4

For a vector v, ‖v‖ is the Euclidean norm of v. It is also referred to as the "magnitude" or "length" of a vector.

Often this is represented by double-bars to avoid ambiguity with the absolute notation, but sometimes you may see it with single bars:

pipes2

Here is an example using an array [x, y, z] to represent a 3D vector.

var v = [ 0, 4, -3 ]
length(v)
//=> 5

The length function:

function length (vec) {
  var x = vec[0]
  var y = vec[1]
  var z = vec[2]
  return Math.sqrt(x * x + y * y + z * z)
}

Other implementations:

determinant

pipes3

For a matrix A, |A| means the determinant of matrix A.

Here is an example computing the determinant of a 2x2 matrix, represented by a flat array in column-major format.

var determinant = require('gl-mat2/determinant')

var matrix = [ 1, 0, 0, 1 ]
var det = determinant(matrix)
//=> 1

Implementations:

hat

In geometry, the "hat" symbol above a character is used to represent a unit vector. For example, here is the unit vector of a:

hat

In cartesian space, a unit vector is typically length 1. That means each part of the vector will be in the range of -1.0 to 1.0. Here we normalize a 3D vector into a unit vector:

var a = [ 0, 4, -3 ]
normalize(a)
//=> [ 0, 0.8, -0.6 ]

Here is the normalize function, operating on 3D vectors:

function normalize(vec) {
  var x = vec[0]
  var y = vec[1]
  var z = vec[2]
  var squaredLength = x * x + y * y + z * z

  if (squaredLength > 0) {
    var length = Math.sqrt(squaredLength)
    vec[0] = vec[0] / length
    vec[1] = vec[1] / length
    vec[2] = vec[2] / length
  }
  return vec
}

Other implementations:

element

In set theory, the "element of" symbol and can be used to describe whether something is an element of a set. For example:

element1

Here we have a set of numbers A [ 3, 9, 14 ] and we are saying 3 is an "element of" that set. In code:

var A = [ 3, 9, 14 ]

A.indexOf(3) >= 0
//=> true

The backwards is the same, but the order changes:

element2

You can also use the "not an element of" symbols and like so:

element3

function

Functions are fundamental features of mathematics, and fairly easy to translate into code.

A function relates an input to an output value. For example, the following is a function:

function1

We can give this function a name. Commonly, we use ƒ to describe a function, but it could be named A(x) or anything else.

function2

In code, we might name it square and write it like this:

function square (x) {
  return Math.pow(x, 2)
}

Sometimes a function is not named, and instead the output is written.

function3

In the above example, x is the input, the relationship is squaring, and y is the output.

Functions can also have multiple parameters, like in a programming language. These are known as arguments in mathematics, and the number of arguments a function takes is known as the arity of the function.

function4

In code:

function length (x, y) {
  return Math.sqrt(x * x + y * y)
}

limit

Sometimes we can't figure out a function directly, but instead, we can see what it should be as we get closer to a certain value.

For example:

limit1

Translating this to code might look like this:

function f (x) {
  return (Math.pow(x, 2) - 1) / (x - 1)
}

Running it, we can see a pattern emerge:

f(2)     //=> 3
f(1.75)  //=> 2.75
f(1.5)   //=> 2.5
f(1.25)  //=> 2.25
f(1.1)   //=> 2.1

As we get closer to x=1, the output is getting closer to 2. But then:

f(1) //=> NaN

This is because the expression evaluates to (0 / 0), which is intederminate (in JavaScript, represented as "Not a Number").

Although we cannot express the result of x=1, we can say that, as we approach 1, the limit is 2. This is where the lim symbol comes in:

limit2

prime

The prime symbol () is often used in variable names to describe things which are similar, without giving it a different name altogether. It often describes the "next value" after some transformation.

For example, if we take a 2D point (x, y) and rotate it, you might name the result (x′, y′). Or, the transpose of matrix M might be named M′.

In code, we typically just assign the variable a more descriptive name, like transformedPosition.

For a mathematical function, the prime symbol describes the derivative of that function. This will be explained shortly. Let's take our earlier function:

function2

The derivative would be written as:

prime1

Multiple prime symbols can be used to describe the second derivative f′′(x), third derivative f′′′(x) and so forth.

derivatives

Derivatives and differentiation is difficult to explain in code, but let's try. For a more detailed beginner's introduction, check out Calculus: Building Intuition for the Derivative.

First, let's imagine what number comes after zero. Maybe something like this?

0.000000001

Nope! We can go smaller still. For any small number, we can find something smaller than it. This can go on infinitely - we will never know the smallest number.

Let's call this mysterious and infinitely small gap between numbers dx. Or, the "jump" to the next number in the continuum.

Sometimes you will see this dx written as ∆x (pronounced "delta-x").

deriv1

Derivatives can tell us how a function like ƒ(x) = x2 can change as the input moves through this continuum. Let's dissect this a bit:

deriv2

Again, here we see the limit symbol, which is telling us that the expression holds true as our dx interval approaches 0.

In code, determining the rate of motion through the continuum might look like this:

var difference = f(x + dx) - f(x)
var m = difference / dx

The goal of differentiation is to remove dx from the equation, so that we can determine the rate of motion without error.

Say we input whole numbers into the equation, and use a dx interval of 1 (a very large interval).

f(1)  //=> 1
f(2)  //=> 4
f(3)  //=> 9
f(4)  //=> 16

function f (x) {
  return Math.pow(x, 2)
}

Using the above, let's determine the rate of motion with x=2 and dx=1, and see how it might look in code.

var x = 2
var dx = 1
var difference = f(x + dx) - f(x)
var m = difference / dx
//=> 5

But an interval of 1 is pretty huge, and gives us a very large margin of error. Using dx=0.001 will give us a smaller margin of error, resulting in:

4.00099999999

As dx approaches 0, we can see that the rate of motion for x=2 is 4.

From this, we can deduce that as dx approaches zero margin of error, we get 2x. Here is how it might look if we were to solve the equation and remove the dx margin of error:

deriv3

Thus, the derivative of our x2 function can be succinctly described as below. Note the prime in the name.

prime1

more...

Like this guide? Suggest some more features or send us a Pull Request!

License

MIT, see LICENSE.md for details.

About

a cheat-sheet for mathematical notation in code form

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published