Browse by type
Based on math-as-code
This is a reference to ease developers into mathematical notation by showing comparisons with Rust code.
Motivation: Academic papers can be intimidating for self-taught graphics programmers and data wranglers :)
This guide is not yet finished. If you see errors or want to contribute, please open a ticket or send a PR.
Mathematical symbols can mean different things depending on the author, context and the field of study (linear algebra, set theory, etc). This guide may not cover all uses of a symbol. In some cases, real-world references (blog posts, publications, etc) will be cited to demonstrate how a symbol might appear in the wild.
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.
= ≈ ≠ :=√ i· × ∘Σ - summationΠ - products of sequences||â - unit vector∈ ∉ℝ ℤ ℚ ℕƒ↦ →′⌊ ⌉⇒ →< ≥ ≫∧ ∨¬ ~ !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:
This will also be the format of this guide.
There are a number of symbols resembling the equals sign =. Here are a few common examples:
= is for equality (values are the same)≠ is for inequality (value are not the same)≈ is for approximately equal to (π ≈ 3.14159):= is for definition (A is defined as B)In Rust:
// equality
2 == 3
// inequality
2 != 3
// approximately equal
#[macro_use]
extern crate is_close;
// is_close! doesn't have a third argument for tolerance, so this is false
is_close!(std::f64::consts::PI, 3.14159), true)
fn is_almost_equal(x: f64, y: f64, epsilon: f64) -> bool {
(x - y).abs() < (10f64.powf(-epsilon))
}
is_almost_equal(std::f64::consts::PI, 3.14159, 1e-5) // true
Read more: programmers got this idea from the [epsilon-delta definition of limit][1]
In mathematical notation, you might see the :=, =: and = symbols being used for definition.[2]
For example, the following defines x to be another name for 2kj.
In rust, we define our variables with =.
let x = 2 * k * j
Assignment in rust variables are immutable by default.
Note: Some languages have pre-processor
#definestatements, which are closer to a mathematical define.
Notice that fn is a form of := as well.
fn plus(x: f64, y: f64) -> f64 {
x + y
}
The following, on the other hand, represents equality:
Important: the difference between = and == can be more obvious in code than it is in math literature! In rust, a = is an instruction. You're telling the machine to interact with the namespace, add something to it or change something in it. In rust, when you write == you're asking the machine "may I have a bool?". In math, the former case is either covered by := or =, while the latter case is usually =, and you might have to do some disambiguating in your reading.
In math, when I write 1 + 1 = 2 I'm making a judgment. It's not that i'm asking the world (or the chalkboard) for a bool, it's that I'm keeping track of my beliefs. This distinction is the foundation of unit tests or assertions.
// assert in takes an expression that lands in bool and a string to be printed if it turns out false.
assert!(plus(1, 1) == 2, "DANGER: PHYSICS IS BROKEN. PLEASE STAY INSIDE.");
It's important to know when a falsehood ought to crash a program vs. when you just want a boolean value. To understand this better, read this.
A square root operation is of the form:
In programming we use a sqrt function, like so:
println!("{}", 2f64.sqrt());
// Out: 1.4142135623730951
Complex numbers are expressions of the form
is defined as:
.
println!("{}", 2f64.sqrt());
// Out: 1+1i
use num::Complex;
let complex_integer = num::Complex::new(1, 1);
println!("{}", complex_integer.sqrt());
// Out: 1.0986841134678098+0.45508986056222733i
// we can represent the basic meaning of the imaginary unit like so
let cn1 = num::complex::Complex::new(-1, 0);
let cn2 = num::complex::Complex::new(0, 1);
assert!(cn1 == cn2); // Should fail
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.
Both symbols can represent simple multiplication of scalars. The following are equivalent:
In programming languages we tend to use asterisk for multiplication:
let result = 5 * 4
Often, the multiplication sign is only used to avoid ambiguity (e.g. between two numbers). Here, we can omit it entirely:
If these variables represent scalars, the code would be:
let result = 3 * k * j
To denote multiplication of one vector with a scalar, or element-wise multiplication of a vector with another vector, we typically 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. For element-wise vector multiplication, you might see an open dot ∘ to represent the Hadamard product.[2]
In other instances, the author might explicitly define a different notation, such as a circled dot ⊙ or a filled circle ●.[3]
Here is how it would look in code, using arrays [x, y] to represent the 2D vectors.
let s = 3
let k = vec![1, 2]
let j = vec![2, 3]
let tmp = multiply(k, j)
let result = multiply_scalar(tmp, s)
// Out: [6, 18]
Our multiply and multiply_scalar functions look like this:
fn multiply(a: Vec<i64>, b: Vec<i64>) -> Vec<i64> {
let it = a.iter().zip(b.iter());
it.map(|(x, y)| x * y).collect()
}
fn multiply_scalar(a: Vec<i64>, scalar: i64) -> Vec<i64> {
a.iter().map(|v| v * scalar).collect()
}
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.
It is a very common feature of linear algebra, and with a 3D vector it might look like this:
let k = [0, 1, 0];
let j = [1, 0, 0];
let d = dot(k, j);
// Out: 0
The result 0 tells us our vectors are perpendicular. Here is a dot function for 3-component vectors:
fn dot(a, b):
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
The cross symbol × can be used to denote the cross product of two vectors.
In code, it would look like this:
let k = vec![0, 1, 0];
let j = vec![1, 0, 0];
let result = cross(k, j);
// Out: [ 0, 0, -1 ]
Here, we get [0, 0, -1], which is perpendicular to both k and j.
Our cross function:
fn cross(a: Vec<i64>, b: Vec<i64>) -> Vec<i64> {
let rx = a[1] * b[2] - a[2] * b[1];
let ry = a[2] * b[0] - a[0] * b[2];
let rz = a[0] * b[1] - a[1] * b[0];
vec![rx, ry, rz]
}
The big Greek Σ (Sigma) is for Summation. In other words: summing up some numbers.
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:
Hence, the big sigma is the std::iter::Sum module.
std::iter::Sum::sum((0..=100).into_iter())
// Out: 5050
Tip: With whole numbers, this particular pattern can be optimized to the following (and try to grok the proof. The legend of how Gauss discovered I can only describe as "typical programmer antics"):
fn sum_to_n(n: f64) -> f64 {
(n * (n + 1.)) / 2.
}
Here is another example where the i, or the "what to sum," is different:
In code:
std::iter::Sum::sum((0..n).map(|k| 2 * k + 1).into_iter())
// Out: 10000
important: range in Rust has an inclusive lower bound and exclusive
upper bound, meaning that ... (0..100) is equivalent to the sum of
... for k=0 to k=n.
If you're still not absolutely fluent in indexing for these applications, spend some time with Trev Tutor on youtub
$ claude mcp add math-as-rust \
-- python -m otcore.mcp_server <graph>