MCPcopy Create free account
hub / github.com/eduardonunesp/math-as-rust

github.com/eduardonunesp/math-as-rust @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
51 symbols 63 edges 1 files 0 documented · 0% updated 2y ago★ 37

Browse by type

Functions 51 Types & classes 0
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

math-as-rust 🦀

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.

foreword

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.

contents

variable name 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.

Rust Math Lib?

equals symbols

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 #define statements, 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.

square root and complex numbers

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

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:

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

vector multiplication

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()
}

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.

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]

cross product

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]
}

sigma

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

Core symbols most depended-on inside this repo

Shape

Function 51

Languages

Rust100%

Modules by API surface

src/lib.rs51 symbols

For agents

$ claude mcp add math-as-rust \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page