From b5929b4ee901ad2c939766436dd431e1861b3325 Mon Sep 17 00:00:00 2001 From: chocolateboy Date: Thu, 22 Oct 2015 19:04:30 +0100 Subject: [PATCH 01/17] fix complex-number typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16648ce..2b977cf 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ var a = math.complex(3, -1) //=> { re: 3, im: -1 } var b = math.sqrt(-1) -//=> { re: 0, im: -1 } +//=> { re: 0, im: 1 } console.log(math.multiply(a, b).toString()) //=> '1 + 3i' From a4a40d417b3138597ebc831d37dc4ecb4ead7d84 Mon Sep 17 00:00:00 2001 From: Erik Mulder Date: Thu, 15 Dec 2016 15:35:49 +0100 Subject: [PATCH 02/17] Fixed variable name typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a6cad0..a40feed 100644 --- a/README.md +++ b/README.md @@ -1034,9 +1034,9 @@ In code: ```js var Interval = require('interval-arithmetic') -var nexafter = require('nextafter') +var nextafter = require('nextafter') -var a = Interval(3, nexafter(5, -Infinity)) +var a = Interval(3, nextafter(5, -Infinity)) var b = Interval(4, 6) Interval.intersection(a, b) From 635afdbf1f75af2f2947a1ff899381f6078e65a4 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Sun, 12 May 2019 11:49:18 -0400 Subject: [PATCH 03/17] through about a fifth of it, finished complexes --- PYTHON-README.md | 1125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1125 insertions(+) create mode 100644 PYTHON-README.md diff --git a/PYTHON-README.md b/PYTHON-README.md new file mode 100644 index 0000000..db8f7d6 --- /dev/null +++ b/PYTHON-README.md @@ -0,0 +1,1125 @@ +# math-as-code + +>[Chinese translation (中文版)](./README-zh.md) + +This is a reference to ease developers into mathematical notation by showing comparisons with Python 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](https://github.com/Jam3/math-as-code/issues) or send a PR. + +> **Note**: There will be some discussion of vectorization with [numpy](https://www.numpy.org/). + +> **Note**: We're only looking at `python3`, if there are significant differences with `python2` please bring them up in issues. + + +# 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](https://en.wikipedia.org/wiki/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](https://github.com/mikolalysenko/robust-arithmetic-notes) by Mikola Lysenko. + +# contents + +- [variable name conventions](#variable-name-conventions) +- [equals `=` `≈` `≠` `:=`](#equals-symbols) +- [square root and complex numbers `√` *`i`*](#square-root-and-complex-numbers) +- [dot & cross `·` `×` `∘`](#dot--cross) + - [scalar multiplication](#scalar-multiplication) + - [vector multiplication](#vector-multiplication) + - [dot product](#dot-product) + - [cross product](#cross-product) +- [sigma `Σ`](#sigma) - *summation* +- [capital Pi `Π`](#capital-pi) - *products of sequences* +- [pipes `||`](#pipes) + - [absolute value](#absolute-value) + - [Euclidean norm](#euclidean-norm) + - [determinant](#determinant) +- [hat **`â`**](#hat) - *unit vector* +- ["element of" `∈` `∉`](#element) +- [common number sets `ℝ` `ℤ` `ℚ` `ℕ`](#common-number-sets) +- [function `ƒ`](#function) + - [piecewise function](#piecewise-function) + - [common functions](#common-functions) + - [function notation `↦` `→`](#function-notation) +- [prime `′`](#prime) +- [floor & ceiling `⌊` `⌉`](#floor--ceiling) +- [arrows](#arrows) + - [material implication `⇒` `→`](#material-implication) + - [equality `<` `≥` `≫`](#equality) + - [conjunction & disjunction `∧` `∨`](#conjunction--disjunction) +- [logical negation `¬` `~` `!`](#logical-negation) +- [intervals](#intervals) +- [more...](#more) + +## 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*](https://en.wikipedia.org/wiki/Spherical_coordinate_system)) + +This will also be the format of this guide. + +### numpy +numpy is a powerful **array programming** library, which in python can be interpreted as a **domain specific language** (DSL). Sometimes it's helpful to think of math in python as two languages sharing a namespace, with special [syntax sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) to access one or the other. This will be important in our vectors and matrices section, because *slightly different python syntax means different speeds at large input*. The convention is `import numpy as np`. + +## 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 Python: + +```python +## equality +2 == 3 + +## inequality +2 != 3 + +## approximately equal +import math +math.isclose(math.pi, 3.14159) # math.isclose doesn't have a third argument for tolerance, so this is false + +from numpy.testing import assert_almost_equal +assert_almost_equal(math.pi, 3.14159, 1e-5) # we gave it a the tolerance we want, 5 decimal places. +# This is actually a unit test, equivalent to "assert isclose(x,y)", read on for more. + +def almost_equal(x, y, epsilon=7): + ''' you can make your own! + in numpy, 1e-7 is the default epsilon + ''' + return abs(x - y) < 10 ** -epsilon + +``` + +**Read more**: programmers got this idea from the [epsilon-delta definition of limit][1] + +**Note**: subclasses of [`unittest.TestCase`](https://docs.python.org/3/library/unittest.html) come with their own `assertAlmostEqual`. + +**Warning**: *please* don't use exact `==` equality on floats! + +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 2*kj*. + +![equals1](http://latex.codecogs.com/svg.latex?x%20%3A%3D%202kj) + + + +In python, we *define* our variables and provide aliases with `=`. + +```python +x = 2 * k * j +``` +*assignment* in python is generally *mutable* besides special cases like `Tuple`. + +However, this is mutable, and only takes a snapshot of the values at that time. Some languages have pre-processor `#define` statements, which are closer to a mathematical *define*. + +Notice that `def` is a form of `:=` as well. +```python +def plus(x, y): + return x + y +``` + +The following, on the other hand, represents equality: + +![equals2](http://latex.codecogs.com/svg.latex?x%20%3D%202kj) + + + +**Important**: the difference between `=` and `==` can be more obvious in code than it is in math literature! In python, a `=` is an *instruction*. You're telling the machine to interact with the namespace, add something to it or change something in it. In python, 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*. + +```python + +# assert in python 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][3]. + + + + +## square root and complex numbers + +A square root operation is of the form: + +![squareroot](http://latex.codecogs.com/svg.latex?%5Cleft%28%5Csqrt%7Bx%7D%5Cright%29%5E2%20%3D%20x) + + + +In programming we use a `sqrt` function, like so: + +```python +import math +print(math.sqrt(2)) +# Out: 1.4142135623730951 + +import numpy as np +print(np.sqrt(2)) +# Out: 1.4142135623730951 + +``` + +Complex numbers are expressions of the form ![complex](http://latex.codecogs.com/svg.latex?a&space;+&space;ib), where ![a](http://latex.codecogs.com/svg.latex?a) is the real part and ![b](http://latex.codecogs.com/svg.latex?b) is the imaginary part. The imaginary number ![i](http://latex.codecogs.com/svg.latex?i) is defined as: + +![imaginary](http://latex.codecogs.com/svg.latex?i%3D%5Csqrt%7B-1%7D). + + +Vanilla python has a `complex` constructor, and a standard module `cmath` for working with them. + +```python +complex(1,1) +# Out: (1+1j)s + +math.sqrt(complex(1,1)) +# TypeError: can't convert complex to float + +import cmath +cmath.sqrt(complex(1,1)) +# Out: (1.09868411346781+0.45508986056222733j) + +# you need numpy to get conjugate +np.conj(complex(0.5,0.5)) +# Out: (0.5-0.5j) + +# we can represent the basic meaning of the imaginary unit like so +assert cmath.sqrt(complex(-1, 0)) == complex(0,1) +``` + +As you can see, it uses `j` to denote the imaginary unit, instead of `i`. + +Just as complex numbers can be interpreted as a sort of wrapper around tuples of +reals, a complex number data type wraps two floats. Numpy uses this to implement +complex numbers of different sizes/precisions. + +The syntax is close enough to `cmath`, but it comes with the power and +convenience of numpy. Importantly, other numpy methods are better at *casting* +to and from complex. + + +observe the following [cube roots of unity](https://www.math-only-math.com/the-cube-roots-of-unity.html) +```python +z1 = 0.5 * np.complex(-1, math.sqrt(3)) # Numpy's constructor is basically the same. +z2 = np.conj(z1) + +math.isclose(z1**3, z2**3) +# TypeError: can't convert complex to float + +np.testing.assert_almost_equal(z1**3, z2**3) +``` +[Read on about numpy's complex numbers](https://docs.scipy.org/doc/numpy/user/basics.types.html) + +## 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](http://latex.codecogs.com/svg.latex?5%20%5Ccdot%204%20%3D%205%20%5Ctimes%204) + + + +In programming languages we tend to use asterisk for multiplication: + +```js +var result = 5 * 4 +``` + +Often, the multiplication sign is only used to avoid ambiguity (e.g. between two numbers). Here, we can omit it entirely: + +![dotcross2](http://latex.codecogs.com/svg.latex?3kj) + + + +If these variables represent scalars, the code would be: + +```js +var 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](https://en.wikipedia.org/wiki/Hadamard_product_%28matrices%29).[2] + +![dotcross3](http://latex.codecogs.com/svg.latex?3%5Cmathbf%7Bk%7D%5Ccirc%5Cmathbf%7Bj%7D) + + + +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. + +```js +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: + +```js +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 the 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*](https://en.wikipedia.org/wiki/Dot_product) of two vectors. Sometimes this is called the *scalar product* since it evaluates to a scalar. + +![dotcross4](http://latex.codecogs.com/svg.latex?%5Cmathbf%7Bk%7D%5Ccdot%20%5Cmathbf%7Bj%7D) + + + +It is a very common feature of linear algebra, and with a 3D vector it might look like this: + +```js +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. Here is a `dot` function for 3-component vectors: + +```js +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*](https://en.wikipedia.org/wiki/Cross_product) of two vectors. + +![dotcross5](http://latex.codecogs.com/svg.latex?%5Cmathbf%7Bk%7D%5Ctimes%20%5Cmathbf%7Bj%7D) + + + +In code, it would look like this: + +```js +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: + +```js +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: + +- [gl-vec3](https://github.com/stackgl/gl-vec3) +- [gl-vec2](https://github.com/stackgl/gl-vec2) +- [vectors](https://github.com/hughsk/vectors) - includes n-dimensional + +## sigma + +The big Greek `Σ` (Sigma) is for [Summation](https://en.wikipedia.org/wiki/Summation). In other words: summing up some numbers. + +![sigma](http://latex.codecogs.com/svg.latex?%5Csum_%7Bi%3D1%7D%5E%7B100%7Di) + + + +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: + +```js +var sum = 0 +for (var i = 1; i <= 100; i++) { + sum += i +} +``` + +The result of `sum` is `5050`. + +**Tip:** With whole numbers, this particular pattern can be optimized to the following: + +```js +var n = 100 // upper bound +var sum = (n * (n + 1)) / 2 +``` + +Here is another example where the *i*, or the "what to sum," is different: + +![sum2](http://latex.codecogs.com/svg.latex?%5Csum_%7Bi%3D1%7D%5E%7B100%7D%282i+1%29) + + + +In code: + +```js +var sum = 0 +for (var i = 1; i <= 100; 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. However, in the following case, since we are dealing with finite sums, the order does not matter. + +![sigma3](http://latex.codecogs.com/svg.latex?%5Csum_%7Bi%3D1%7D%5E%7B2%7D%5Csum_%7Bj%3D4%7D%5E%7B6%7D%283ij%29) + + + +In code: + +```js +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](#sigma), except we are using multiplication to find the product of a sequence of values. + +Take the following: + +![capitalPi](http://latex.codecogs.com/svg.latex?%5Cprod_%7Bi%3D1%7D%5E%7B6%7Di) + + + +In code, it might look like this: + +```js +var value = 1 +for (var i = 1; i <= 6; 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 value](#absolute-value), [Euclidean norm](#euclidean-norm), and [determinant](#determinant). + +These three features all describe the *length* of an object. + +#### absolute value + +![pipes1](http://latex.codecogs.com/svg.latex?%5Cleft%20%7C%20x%20%5Cright%20%7C) + + + +For a number *x*, `|x|` means the absolute value of *x*. In code: + +```js +var x = -5 +var result = Math.abs(x) +// => 5 +``` + +#### Euclidean norm + +![pipes4](http://latex.codecogs.com/svg.latex?%5Cleft%20%5C%7C%20%5Cmathbf%7Bv%7D%20%5Cright%20%5C%7C) + + + +For a vector **v**, `‖v‖` is the [Euclidean norm](https://en.wikipedia.org/wiki/Norm_%28mathematics%29#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 value* notation, but sometimes you may see it with single bars: + +![pipes2](http://latex.codecogs.com/svg.latex?%5Cleft%20%7C%20%5Cmathbf%7Bv%7D%20%5Cright%20%7C) + + + +Here is an example using an array `[x, y, z]` to represent a 3D vector. + +```js +var v = [ 0, 4, -3 ] +length(v) +//=> 5 +``` + +The `length` function: + +```js +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: + +- [magnitude](https://github.com/mattdesl/magnitude/blob/864ff5a7eb763d34bf154ac5f5332d7601192b70/index.js) - n-dimensional +- [gl-vec2/length](https://github.com/stackgl/gl-vec2/blob/21f460a371540258521fd2f720d80f14e87bd400/length.js) - 2D vector +- [gl-vec3/length](https://github.com/stackgl/gl-vec3/blob/507480fa57ba7c5fb70679cf531175a52c48cf53/length.js) - 3D vector + +#### determinant + +![pipes3](http://latex.codecogs.com/svg.latex?%5Cleft%20%7C%5Cmathbf%7BA%7D%20%5Cright%20%7C) + + + +For a matrix **A**, `|A|` means the [determinant](https://en.wikipedia.org/wiki/Determinant) of matrix **A**. + +Here is an example computing the determinant of a 2x2 matrix, represented by a flat array in column-major format. + +```js +var determinant = require('gl-mat2/determinant') + +var matrix = [ 1, 0, 0, 1 ] +var det = determinant(matrix) +//=> 1 +``` + +Implementations: + +- [gl-mat4/determinant](https://github.com/stackgl/gl-mat4/blob/c2e2de728fe7eba592f74cd02266100cc21ec89a/determinant.js) - also see [gl-mat3](https://github.com/stackgl/gl-mat3) and [gl-mat2](https://github.com/stackgl/gl-mat2) +- [ndarray-determinant](https://www.npmjs.com/package/ndarray-determinant) +- [glsl-determinant](https://www.npmjs.com/package/glsl-determinant) +- [robust-determinant](https://www.npmjs.com/package/robust-determinant) +- [robust-determinant-2](https://www.npmjs.com/package/robust-determinant-2) and [robust-determinant-3](https://www.npmjs.com/package/robust-determinant-3), specifically for 2x2 and 3x3 matrices, respectively + +## hat + +In geometry, the "hat" symbol above a character is used to represent a [unit vector](https://en.wikipedia.org/wiki/Unit_vector). For example, here is the unit vector of **a**: + +![hat](http://latex.codecogs.com/svg.latex?%5Chat%7B%5Cmathbf%7Ba%7D%7D) + + + +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: + +```js +var a = [ 0, 4, -3 ] +normalize(a) +//=> [ 0, 0.8, -0.6 ] +``` + +Here is the `normalize` function, operating on 3D vectors: + +```js +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] = x / length + vec[1] = y / length + vec[2] = z / length + } + return vec +} +``` + +Other implementations: + +- [gl-vec3/normalize](https://github.com/stackgl/gl-vec3/blob/507480fa57ba7c5fb70679cf531175a52c48cf53/normalize.js) and [gl-vec2/normalize](https://github.com/stackgl/gl-vec2/blob/21f460a371540258521fd2f720d80f14e87bd400/normalize.js) +- [vectors/normalize-nd](https://github.com/hughsk/vectors/blob/master/normalize-nd.js) (n-dimensional) + +## 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](http://latex.codecogs.com/svg.latex?A%3D%5Cleft%20%5C%7B3%2C9%2C14%7D%7B%20%5Cright%20%5C%7D%2C%203%20%5Cin%20A) + + + +Here we have a set of numbers *A* `{ 3, 9, 14 }` and we are saying `3` is an "element of" that set. + +A simple implementation in ES5 might look like this: + +```js +var A = [ 3, 9, 14 ] + +A.indexOf(3) >= 0 +//=> true +``` + +However, it would be more accurate to use a `Set` which only holds unique values. This is a feature of ES6. + +```js +var A = new Set([ 3, 9, 14 ]) + +A.has(3) +//=> true +``` + +The backwards `∋` is the same, but the order changes: + +![element2](http://latex.codecogs.com/svg.latex?A%3D%5Cleft%20%5C%7B3%2C9%2C14%7D%7B%20%5Cright%20%5C%7D%2C%20A%20%5Cni%203) + + + +You can also use the "not an element of" symbols `∉` and `∌` like so: + +![element3](http://latex.codecogs.com/svg.latex?A%3D%5Cleft%20%5C%7B3%2C9%2C14%7D%7B%20%5Cright%20%5C%7D%2C%206%20%5Cnotin%20A) + + + +## common number sets + +You may see some some large [Blackboard](https://en.wikipedia.org/wiki/Blackboard_bold) letters among equations. Often, these are used to describe sets. + +For example, we might describe *k* to be an [element of](#element) the set `ℝ`. + +![real](http://latex.codecogs.com/svg.latex?k%20%5Cin%20%5Cmathbb%7BR%7D) + + + +Listed below are a few common sets and their symbols. + +#### `ℝ` real numbers + +The large `ℝ` describes the set of *real numbers*. These include integers, as well as rational and irrational numbers. + +JavaScript treats floats and integers as the same type, so the following would be a simple test of our *k* ∈ ℝ example: + +```js +function isReal (k) { + return typeof k === 'number' && isFinite(k); +} +``` + +*Note:* Real numbers are also *finite*, as in, *not infinite.* + +#### `ℚ` rational numbers + +Rational numbers are real numbers that can be expressed as a fraction, or *ratio* (like `⅗`). Rational numbers cannot have zero as a denominator. + +This also means that all integers are rational numbers, since the denominator can be expressed as 1. + +An irrational number, on the other hand, is one that cannot be expressed as a ratio, like π (PI). + +#### `ℤ` integers + +An integer, i.e. a real number that has no fractional part. These can be positive or negative. + +A simple test in JavaScript might look like this: + +```js +function isInteger (n) { + return typeof n === 'number' && n % 1 === 0 +} +``` + +#### `ℕ` natural numbers + +A natural number, a positive and non-negative integer. Depending on the context and field of study, the set may or may not include zero, so it could look like either of these: + +```js +{ 0, 1, 2, 3, ... } +{ 1, 2, 3, 4, ... } +``` + +The former is more common in computer science, for example: + +```js +function isNaturalNumber (n) { + return isInteger(n) && n >= 0 +} +``` + +#### `ℂ` complex numbers + +A complex number is a combination of a real and imaginary number, viewed as a co-ordinate in the 2D plane. For more info, see [A Visual, Intuitive Guide to Imaginary Numbers](http://betterexplained.com/articles/a-visual-intuitive-guide-to-imaginary-numbers/). + +## function + +[Functions](https://en.wikipedia.org/wiki/Function_%28mathematics%29) are fundamental features of mathematics, and the concept is fairly easy to translate into code. + +A function relates an input to an output value. For example, the following is a function: + +![function1](http://latex.codecogs.com/svg.latex?x%5E%7B2%7D) + + + +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](http://latex.codecogs.com/svg.latex?f%5Cleft%20%28x%20%5Cright%20%29%20%3D%20x%5E%7B2%7D) + + + +In code, we might name it `square` and write it like this: + +```js +function square (x) { + return Math.pow(x, 2) +} +``` + +Sometimes a function is not named, and instead the output is written. + +![function3](http://latex.codecogs.com/svg.latex?y%20%3D%20x%5E%7B2%7D) + + + +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](http://latex.codecogs.com/svg.latex?f%28x%2Cy%29%20%3D%20%5Csqrt%7Bx%5E2%20+%20y%5E2%7D) + + + +In code: + +```js +function length (x, y) { + return Math.sqrt(x * x + y * y) +} +``` + +### piecewise function + +Some functions will use different relationships depending on the input value, *x*. + +The following function *ƒ* chooses between two "sub functions" depending on the input value. + +![piecewise1](http://latex.codecogs.com/svg.latex?f%28x%29%3D%20%5Cbegin%7Bcases%7D%20%5Cfrac%7Bx%5E2-x%7D%7Bx%7D%2C%26%20%5Ctext%7Bif%20%7D%20x%5Cgeq%201%5C%5C%200%2C%20%26%20%5Ctext%7Botherwise%7D%20%5Cend%7Bcases%7D) + + + +This is very similar to `if` / `else` in code. The right-side conditions are often written as **"for x < 0"** or **"if x = 0"**. If the condition is true, the function to the left is used. + +In piecewise functions, **"otherwise"** and **"elsewhere"** are analogous to the `else` statement in code. + +```js +function f (x) { + if (x >= 1) { + return (Math.pow(x, 2) - x) / x + } else { + return 0 + } +} +``` + +### common functions + +There are some function names that are ubiquitous in mathematics. For a programmer, these might be analogous to functions "built-in" to the language (like `parseInt` in JavaScript). + +One such example is the *sgn* function. This is the *signum* or *sign* function. Let's use [piecewise function](#piecewise-function) notation to describe it: + +![sgn](http://latex.codecogs.com/svg.latex?sgn%28x%29%20%3A%3D%20%5Cbegin%7Bcases%7D%20-1%26%20%5Ctext%7Bif%20%7D%20x%20%3C%200%5C%5C%200%2C%20%26%20%5Ctext%7Bif%20%7D%20%7Bx%20%3D%200%7D%5C%5C%201%2C%20%26%20%5Ctext%7Bif%20%7D%20x%20%3E%200%5C%5C%20%5Cend%7Bcases%7D) + + + +In code, it might look like this: + +```js +function sgn (x) { + if (x < 0) return -1 + if (x > 0) return 1 + return 0 +} +``` + +See [signum](https://github.com/scijs/signum) for this function as a module. + +Other examples of such functions: *sin*, *cos*, *tan*. + +### function notation + +In some literature, functions may be defined with more explicit notation. For example, let's go back to the `square` function we mentioned earlier: + +![function2](http://latex.codecogs.com/svg.latex?f%5Cleft%20%28x%20%5Cright%20%29%20%3D%20x%5E%7B2%7D) + + + +It might also be written in the following form: + +![mapsto](http://latex.codecogs.com/svg.latex?f%20%3A%20x%20%5Cmapsto%20x%5E2) + + + +The arrow here with a tail typically means "maps to," as in *x maps to x2*. + +Sometimes, when it isn't obvious, the notation will also describe the *domain* and *codomain* of the function. A more formal definition of *ƒ* might be written as: + +![funcnot](http://latex.codecogs.com/svg.latex?%5Cbegin%7Balign*%7D%20f%20%3A%26%5Cmathbb%7BR%7D%20%5Crightarrow%20%5Cmathbb%7BR%7D%5C%5C%20%26x%20%5Cmapsto%20x%5E2%20%5Cend%7Balign*%7D) + + + +A function's *domain* and *codomain* is a bit like its *input* and *output* types, respectively. Here's another example, using our earlier *sgn* function, which outputs an integer: + +![domain2](http://latex.codecogs.com/svg.latex?sgn%20%3A%20%5Cmathbb%7BR%7D%20%5Crightarrow%20%5Cmathbb%7BZ%7D) + + + +The arrow here (without a tail) is used to map one *set* to another. + +In JavaScript and other dynamically typed languages, you might use documentation and/or runtime checks to explain and validate a function's input/output. Example: + +```js +/** + * Squares a number. + * @param {Number} a real number + * @return {Number} a real number + */ +function square (a) { + if (typeof a !== 'number') { + throw new TypeError('expected a number') + } + return Math.pow(a, 2) +} +``` + +Some tools like [flowtype](http://flowtype.org/) attempt to bring static typing into JavaScript. + +Other languages, like Java, allow for true method overloading based on the static types of a function's input/output. This is closer to mathematics: two functions are not the same if they use a different *domain*. + +## prime + +The prime symbol (`′`) is often used in variable names to describe things which are similar, without giving it a different name altogether. It can describe 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](#function), the prime symbol often describes the *derivative* of that function. Derivatives will be explained in a future section. Let's take our earlier function: + +![function2](http://latex.codecogs.com/svg.latex?f%5Cleft%20%28x%20%5Cright%20%29%20%3D%20x%5E%7B2%7D) + + + +Its derivative could be written with a prime `′` symbol: + +![prime1](http://latex.codecogs.com/svg.latex?f%27%28x%29%20%3D%202x) + + + +In code: + +```js +function f (x) { + return Math.pow(x, 2) +} + +function fPrime (x) { + return 2 * x +} +``` + +Multiple prime symbols can be used to describe the second derivative *ƒ′′* and third derivative *ƒ′′′*. After this, authors typically express higher orders with roman numerals *ƒ*IV or superscript numbers *ƒ*(n). + +## floor & ceiling + +The special brackets `⌊x⌋` and `⌈x⌉` represent the *floor* and *ceil* functions, respectively. + +![floor](http://latex.codecogs.com/svg.latex?floor%28x%29%20%3D%20%5Clfloor%20x%20%5Crfloor) + + + +![ceil](http://latex.codecogs.com/svg.latex?ceil%28x%29%20%3D%20%5Clceil%20x%20%5Crceil) + + + +In code: + +```js +Math.floor(x) +Math.ceil(x) +``` + +When the two symbols are mixed `⌊x⌉`, it typically represents a function that rounds to the nearest integer: + +![round](http://latex.codecogs.com/svg.latex?round%28x%29%20%3D%20%5Clfloor%20x%20%5Crceil) + + + +In code: + +```js +Math.round(x) +``` + +## arrows + +Arrows are often used in [function notation](#function-notation). Here are a few other areas you might see them. + +#### material implication + +Arrows like `⇒` and `→` are sometimes used in logic for *material implication.* That is, if A is true, then B is also true. + +![material1](http://latex.codecogs.com/svg.latex?A%20%5CRightarrow%20B) + + + +Interpreting this as code might look like this: + +```js +if (A === true) { + console.assert(B === true) +} +``` + +The arrows can go in either direction `⇐` `⇒`, or both `⇔`. When *A ⇒ B* and *B ⇒ A*, they are said to be equivalent: + +![material-equiv](http://latex.codecogs.com/svg.latex?A%20%5CLeftrightarrow%20B) + + + +#### equality + +In math, the `<` `>` `≤` and `≥` are typically used in the same way we use them in code: *less than*, *greater than*, *less than or equal to* and *greater than or equal to*, respectively. + +```js +50 > 2 === true +2 < 10 === true +3 <= 4 === true +4 >= 4 === true +``` + +On rare occasions you might see a slash through these symbols, to describe *not*. As in, *k* is "not greater than" *j*. + +![ngt](http://latex.codecogs.com/svg.latex?k%20%5Cngtr%20j) + + + +The `≪` and `≫` are sometimes used to represent *significant* inequality. That is, *k* is an [order of magnitude](https://en.wikipedia.org/wiki/Order_of_magnitude) larger than *j*. + +![orderofmag](http://latex.codecogs.com/svg.latex?k%20%5Cgg%20j) + + + +In mathematics, *order of magnitude* is rather specific; it is not just a "really big difference." A simple example of the above: + +```js +orderOfMagnitude(k) > orderOfMagnitude(j) +``` + +And below is our `orderOfMagnitude` function, using [Math.trunc](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc) (ES6). + +```js +function log10(n) { + // logarithm in base 10 + return Math.log(n) / Math.LN10 +} + +function orderOfMagnitude (n) { + return Math.trunc(log10(n)) +} +``` + +*Note:* This is not numerically robust. + +See [math-trunc](https://www.npmjs.com/package/math-trunc) for a ponyfill in ES5. + +#### conjunction & disjunction + +Another use of arrows in logic is conjunction `∧` and disjunction `∨`. They are analogous to a programmer's `AND` and `OR` operators, respectively. + +The following shows conjunction `∧`, the logical `AND`. + +![and](http://latex.codecogs.com/svg.latex?k%20%3E%202%20%5Cland%20k%20%3C%204%20%5CLeftrightarrow%20k%20%3D%203) + + + +In JavaScript, we use `&&`. Assuming *k* is a natural number, the logic implies that *k* is 3: + +```js +if (k > 2 && k < 4) { + console.assert(k === 3) +} +``` + +Since both sides are equivalent `⇔`, it also implies the following: + +```js +if (k === 3) { + console.assert(k > 2 && k < 4) +} +``` + +The down arrow `∨` is logical disjunction, like the OR operator. + +![logic-or](http://latex.codecogs.com/svg.latex?A%20%5Clor%20B) + + + +In code: + +```js +A || B +``` + +## logical negation + +Occasionally, the `¬`, `~` and `!` symbols are used to represent logical `NOT`. For example, *¬A* is only true if A is false. + +Here is a simple example using the *not* symbol: + +![negation](http://latex.codecogs.com/svg.latex?x%20%5Cneq%20y%20%5CLeftrightarrow%20%5Clnot%28x%20%3D%20y%29) + + + +An example of how we might interpret this in code: + +```js +if (x !== y) { + console.assert(!(x === y)) +} +``` + +*Note:* The tilde `~` has many different meanings depending on context. For example, *row equivalence* (matrix theory) or *same order of magnitude* (discussed in [equality](#equality)). + +## intervals + +Sometimes a function deals with real numbers restricted to some range of values, such a constraint can be represented using an *interval* + +For example we can represent the numbers between zero and one including/not including zero and/or one as: + +- Not including zero or one: ![interval-opened-left-opened-right](http://latex.codecogs.com/svg.latex?%280%2C%201%29) + + + +- Including zero or but not one: ![interval-closed-left-opened-right](http://latex.codecogs.com/svg.latex?%5B0%2C%201%29) + + + +- Not including zero but including one: ![interval-opened-left-closed-right](http://latex.codecogs.com/svg.latex?%280%2C%201%5D) + + + +- Including zero and one: ![interval-closed-left-closed-right](http://latex.codecogs.com/svg.latex?%5B0%2C%201%5D) + + + +For example we to indicate that a point `x` is in the unit cube in 3D we say: + +![interval-unit-cube](http://latex.codecogs.com/svg.latex?x%20%5Cin%20%5B0%2C%201%5D%5E3) + + + +In code we can represent an interval using a two element 1d array: + +```js +var nextafter = require('nextafter') + +var a = [nextafter(0, Infinity), nextafter(1, -Infinity)] // open interval +var b = [nextafter(0, Infinity), 1] // interval closed on the left +var c = [0, nextafter(1, -Infinity)] // interval closed on the right +var d = [0, 1] // closed interval +``` + +Intervals are used in conjunction with set operations: + +- *intersection* e.g. ![interval-intersection](http://latex.codecogs.com/svg.latex?%5B3%2C%205%29%20%5Ccap%20%5B4%2C%206%5D%20%3D%20%5B4%2C%205%29) + + + +- *union* e.g. ![interval-union](http://latex.codecogs.com/svg.latex?%5B3%2C%205%29%20%5Ccup%20%5B4%2C%206%5D%20%3D%20%5B3%2C%206%5D) + + + +- *difference* e.g. ![interval-difference-1](http://latex.codecogs.com/svg.latex?%5B3%2C%205%29%20-%20%5B4%2C%206%5D%20%3D%20%5B3%2C%204%29) and ![interval-difference-2](http://latex.codecogs.com/svg.latex?%5B4%2C%206%5D%20-%20%5B3%2C%205%29%20%3D%20%5B5%2C%206%5D) + + + + +In code: + +```js +var Interval = require('interval-arithmetic') +var nextafter = require('nextafter') + +var a = Interval(3, nextafter(5, -Infinity)) +var b = Interval(4, 6) + +Interval.intersection(a, b) +// {lo: 4, hi: 4.999999999999999} + +Interval.union(a, b) +// {lo: 3, hi: 6} + +Interval.difference(a, b) +// {lo: 3, hi: 3.9999999999999996} + +Interval.difference(b, a) +// {lo: 5, hi: 6} +``` + +See: + +- [next-after](https://github.com/scijs/nextafter) +- [interval-arithmetic](https://github.com/maurizzzio/interval-arithmetic) + +## more... + +Like this guide? Suggest some [more features](https://github.com/Jam3/math-as-code/issues/1) or send us a Pull Request! + +## Contributing + +For details on how to contribute, see [CONTRIBUTING.md](./CONTRIBUTING.md). + +## License + +MIT, see [LICENSE.md](http://github.com/Jam3/math-as-code/blob/master/LICENSE.md) for details. + +[1]: https://en.wikipedia.org/wiki/(%CE%B5,_%CE%B4)-definition_of_limit +[2]: http://mimosa-pudica.net/improved-oren-nayar.html#images +[3]: https://en.wikipedia.org/wiki/G%C3%B6del,_Escher,_Bach +[4]: http://buzzard.ups.edu/courses/2007spring/projects/million-paper.pdf +[5]: https://www.math.washington.edu/~morrow/464_12/fft.pdf From 40add498b44952b97c646dcc6915b0c49c9618ba Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Sun, 12 May 2019 12:28:32 -0400 Subject: [PATCH 04/17] got through sigma, roughly 1/3 of the way done --- PYTHON-README.md | 160 +++++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 75 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index db8f7d6..7ccf900 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -65,8 +65,15 @@ There are a variety of naming conventions depending on the context and field of This will also be the format of this guide. -### numpy -numpy is a powerful **array programming** library, which in python can be interpreted as a **domain specific language** (DSL). Sometimes it's helpful to think of math in python as two languages sharing a namespace, with special [syntax sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) to access one or the other. This will be important in our vectors and matrices section, because *slightly different python syntax means different speeds at large input*. The convention is `import numpy as np`. +### Numpy +Numpy is a powerful **array programming** library, which in python can be +interpreted as a **domain specific language** (DSL). Sometimes it's helpful to +think of math in python as two languages sharing a namespace, with special +[syntax sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) to access one or +the other. This will be important in our vectors and matrices section, because +*slightly different python syntax means different speeds at large input*. The +convention is `import numpy as np`, so when you see `np.something` you know that +we're working in numpy. ## equals symbols @@ -102,7 +109,7 @@ def almost_equal(x, y, epsilon=7): ``` -**Read more**: programmers got this idea from the [epsilon-delta definition of limit][1] +> **Read more**: programmers got this idea from the [epsilon-delta definition of limit][1] **Note**: subclasses of [`unittest.TestCase`](https://docs.python.org/3/library/unittest.html) come with their own `assertAlmostEqual`. @@ -121,9 +128,9 @@ In python, we *define* our variables and provide aliases with `=`. ```python x = 2 * k * j ``` -*assignment* in python is generally *mutable* besides special cases like `Tuple`. +*Assignment* in python is generally *mutable* besides special cases like `Tuple`. -However, this is mutable, and only takes a snapshot of the values at that time. Some languages have pre-processor `#define` statements, which are closer to a mathematical *define*. +> **Note**: Some languages have pre-processor `#define` statements, which are closer to a mathematical *define*. Notice that `def` is a form of `:=` as well. ```python @@ -201,6 +208,9 @@ assert cmath.sqrt(complex(-1, 0)) == complex(0,1) As you can see, it uses `j` to denote the imaginary unit, instead of `i`. +The **conjugate** of a complex number is flipping the plus in the middle to +minus. + Just as complex numbers can be interpreted as a sort of wrapper around tuples of reals, a complex number data type wraps two floats. Numpy uses this to implement complex numbers of different sizes/precisions. @@ -213,9 +223,9 @@ to and from complex. observe the following [cube roots of unity](https://www.math-only-math.com/the-cube-roots-of-unity.html) ```python z1 = 0.5 * np.complex(-1, math.sqrt(3)) # Numpy's constructor is basically the same. -z2 = np.conj(z1) +z2 = np.conj(z1) # but numpy gives us a conjugation function, while the standard module does not. -math.isclose(z1**3, z2**3) +assert math.isclose(z1**3, z2**3) # TypeError: can't convert complex to float np.testing.assert_almost_equal(z1**3, z2**3) @@ -238,8 +248,8 @@ Both symbols can represent simple multiplication of scalars. The following are e In programming languages we tend to use asterisk for multiplication: -```js -var result = 5 * 4 +```python +result = 5 * 4 ``` Often, the multiplication sign is only used to avoid ambiguity (e.g. between two numbers). Here, we can omit it entirely: @@ -250,8 +260,8 @@ Often, the multiplication sign is only used to avoid ambiguity (e.g. between two If these variables represent scalars, the code would be: -```js -var result = 3 * k * j +```python +result = 3 * k * j ``` #### vector multiplication @@ -268,25 +278,25 @@ In other instances, the author might explicitly define a different notation, suc Here is how it would look in code, using arrays `[x, y]` to represent the 2D vectors. -```js -var s = 3 -var k = [ 1, 2 ] -var j = [ 2, 3 ] +```python +s = 3 +k = [1, 2] +j = [2, 3] -var tmp = multiply(k, j) -var result = multiplyScalar(tmp, s) -//=> [ 6, 18 ] +tmp = multiply(k, j) +result = multiplyScalar(tmp, s) +# Out: [6, 18] ``` Our `multiply` and `multiplyScalar` functions look like this: -```js -function multiply(a, b) { - return [ a[0] * b[0], a[1] * b[1] ] -} +```python +def multiply(a, b) { + return [aa * bb for aa,bb in zip(a,b) + -function multiplyScalar(a, scalar) { - return [ a[0] * scalar, a[1] * scalar ] +def multiplyScalar(a, scalar) { + return [scalar * aa for aa in a] } ``` @@ -302,20 +312,19 @@ The dot symbol `·` can be used to denote the [*dot product*](https://en.wikiped It is a very common feature of linear algebra, and with a 3D vector it might look like this: -```js -var k = [ 0, 1, 0 ] -var j = [ 1, 0, 0 ] +```python +k = [0, 1, 0] +j = [1, 0, 0] -var d = dot(k, j) -//=> 0 +d = np.dot(k, j) +# Out: 0 ``` The result `0` tells us our vectors are perpendicular. Here is a `dot` function for 3-component vectors: -```js -function dot(a, b) { +```python +def dot(a, b): return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] -} ``` #### cross product @@ -328,28 +337,25 @@ The cross symbol `×` can be used to denote the [*cross product*](https://en.wik In code, it would look like this: -```js -var k = [ 0, 1, 0 ] -var j = [ 1, 0, 0 ] +```python +k = [0, 1, 0] +j = [1, 0, 0] -var result = cross(k, j) -//=> [ 0, 0, -1 ] +result = cross(k, j) +# Out: [ 0, 0, -1 ] ``` -Here, we get `[ 0, 0, -1 ]`, which is perpendicular to both **k** and **j**. +Here, we get `[0, 0, -1]`, which is perpendicular to both **k** and **j**. Our `cross` function: -```js -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 ] -} +```python +def cross(a, b): + ''' take two 3D vectors and return their cross product. ''' + rx = a[1] * b[2] - a[2] * b[1] + ry = a[2] * b[0] - a[0] * b[2] + rz = a[0] * b[1] - a[1] * b[0] + return rx, ry, rz ``` For other implementations of vector multiplication, cross product, and dot product: @@ -368,20 +374,21 @@ The big Greek `Σ` (Sigma) is for [Summation](https://en.wikipedia.org/wiki/Summ 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: -```js -var sum = 0 -for (var i = 1; i <= 100; i++) { - sum += i -} +Hence, the big sigma is the `for` keyword. +```python +sum([k for k in range(100)]) +# Out: 5050 ``` -The result of `sum` is `5050`. - -**Tip:** With whole numbers, this particular pattern can be optimized to the following: +**Tip:** With whole numbers, this particular pattern can be optimized to the +following (and try to [grok the +proof](http://mathcentral.uregina.ca/QQ/database/QQ.02.06/jo1.html). The legend +of how Gauss discovered I can only describe as "typical programmer antics"): -```js -var n = 100 // upper bound -var sum = (n * (n + 1)) / 2 +```python +def sum_to_n(n): + ''' return the sum of integers from 0 to n''' + return 0.5 * n * (n + 1) ``` Here is another example where the *i*, or the "what to sum," is different: @@ -392,16 +399,19 @@ Here is another example where the *i*, or the "what to sum," is different: In code: -```js -var sum = 0 -for (var i = 1; i <= 100; i++) { - sum += (2 * i + 1) -} +```python +sum([2*k + 1 for k in range(100)]) +# Out: 10000 ``` -The result of `sum` is `10200`. +**important**: `range` in python has an *inclusive lower bound and exclusive +upper bound*, meaning that `... for k in range(100)` is equivalent to `the sum of +... for k=0 to k=n`. -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. However, in the following case, since we are dealing with finite sums, the order does not matter. +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. However, in the following case, since we are +dealing with finite sums, the order does not matter. ![sigma3](http://latex.codecogs.com/svg.latex?%5Csum_%7Bi%3D1%7D%5E%7B2%7D%5Csum_%7Bj%3D4%7D%5E%7B6%7D%283ij%29) @@ -409,16 +419,16 @@ The notation can be nested, which is much like nesting a `for` loop. You should In code: -```js -var sum = 0 -for (var i = 1; i <= 2; i++) { - for (var j = 4; j <= 6; j++) { - sum += (3 * i * j) - } -} +```python +sum( + [sum([3*i*j + for j + in range(4,7)]) + for i + in range(1,3)]) +# Out: 135 ``` -Here, `sum` will be `135`. ## capital Pi @@ -432,7 +442,7 @@ Take the following: In code, it might look like this: -```js +```python var value = 1 for (var i = 1; i <= 6; i++) { value *= i From 2b9f280921b32ad20da57c6496bc29122d8c29fe Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 01:33:24 -0400 Subject: [PATCH 05/17] almost done, starting to proofread from the top --- PYTHON-README.md | 507 +++++++++++++++++++++++++++-------------------- 1 file changed, 296 insertions(+), 211 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index 7ccf900..f53f5be 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -440,20 +440,47 @@ Take the following: -In code, it might look like this: - +This was removed from vanilla python for python 3, but it's easy to recover with +a generalization of the list accumulator. ```python -var value = 1 -for (var i = 1; i <= 6; i++) { - value *= i -} +def times(x, y): + ''' first, give a name to the multiplication operator ''' + return x * y + +from functools import reduce + +reduce(times, range(1,7)) +# Out: 720 ``` -Where `value` will evaluate to `720`. +With reduce, you can actually repeatedly apply a binary function to items of a +list and accumulate the value _for any binary operator_. Python gives `and` and +`or` out of the box like `sum`, but keep `reduce` in mind if you encounter a +less common binary operator out in the wild. + +Note that in Numpy arrays, the syntax is different (and product is given out of +the box) + +```python +import numpy as np + +xs = np.array([2*k + 1 for k in range(100)]) +ys = np.array(range(1,7)) + +xs.sum() +# Out: 10000 + +ys.prod() +# Out: 720 +``` +which is better on larger input, but you're always welcome to use functions for +ordinary lists as you please. ## pipes -Pipe symbols, known as *bars*, can mean different things depending on the context. Below are three common uses: [absolute value](#absolute-value), [Euclidean norm](#euclidean-norm), and [determinant](#determinant). +Pipe symbols, known as *bars*, can mean different things depending on the +context. Below are three common uses: [absolute value](#absolute-value), +[Euclidean norm](#euclidean-norm), and [determinant](#determinant). These three features all describe the *length* of an object. @@ -465,12 +492,11 @@ These three features all describe the *length* of an object. For a number *x*, `|x|` means the absolute value of *x*. In code: -```js -var x = -5 -var result = Math.abs(x) -// => 5 +```python +x = -5 +abs(x) +# Out: 5 ``` - #### Euclidean norm ![pipes4](http://latex.codecogs.com/svg.latex?%5Cleft%20%5C%7C%20%5Cmathbf%7Bv%7D%20%5Cright%20%5C%7C) @@ -487,28 +513,35 @@ Often this is represented by double-bars to avoid ambiguity with the *absolute v Here is an example using an array `[x, y, z]` to represent a 3D vector. -```js -var v = [ 0, 4, -3 ] +```python +v = [0, 4, -3] length(v) -//=> 5 +# Out: 5.0 + ``` -The `length` function: +The `length** function: -```js -function length (vec) { - var x = vec[0] - var y = vec[1] - var z = vec[2] - return Math.sqrt(x * x + y * y + z * z) -} +```python +def length(vec): + x = vec[0] + y = vec[1] + z = vec[2] + return math.sqrt(x**2 + y**2 + z**2) ``` -Other implementations: +The implementation for arbitrary length'd vectors is left as an exercise for the +reader. + +In practice, you'll probably use the following numpy call -- [magnitude](https://github.com/mattdesl/magnitude/blob/864ff5a7eb763d34bf154ac5f5332d7601192b70/index.js) - n-dimensional -- [gl-vec2/length](https://github.com/stackgl/gl-vec2/blob/21f460a371540258521fd2f720d80f14e87bd400/length.js) - 2D vector -- [gl-vec3/length](https://github.com/stackgl/gl-vec3/blob/507480fa57ba7c5fb70679cf531175a52c48cf53/length.js) - 3D vector +```python +np.linalg.norm([0, 4, -3]) +# Out: 5.0 +``` + +Resources: +- [numpy.linalg docs](get link to numpy.linalg docs) #### determinant @@ -516,25 +549,35 @@ Other implementations: -For a matrix **A**, `|A|` means the [determinant](https://en.wikipedia.org/wiki/Determinant) of matrix **A**. +For a matrix **A**, `|A|` means the +[determinant](https://en.wikipedia.org/wiki/Determinant) of matrix **A**. -Here is an example computing the determinant of a 2x2 matrix, represented by a flat array in column-major format. +Here is an example computing the determinant of a 2x2 identity matrix -```js -var determinant = require('gl-mat2/determinant') +```python +ident_2 = [[1, 0], + [0, 1]] -var matrix = [ 1, 0, 0, 1 ] -var det = determinant(matrix) -//=> 1 +np.linalg.det(ident_2) +# Out: 1 ``` -Implementations: +You should watch [3blue1brown](https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab), but in short if a matrix (list of list of numbers) +is interpreted as hitting a **coordinate system** with a +*squisher-stretcher-rotater*, the determinant of that matrix is the **measure of +how much the unit area/volume of the coordinate system got +squished-stretched-rotated**. + +```python +np.linalg.det(np.identity(100)) # the determinant of the 100 x 100 identity matrix is still one, because the identity matrix doesn't squish, stretch, or rotate at all. +# Out: 1.0 + +np.linalg.det(np.array([[x, y], [z, w]])) # look up the 90 degree rotation. +# Out: 1.0 +# + +``` -- [gl-mat4/determinant](https://github.com/stackgl/gl-mat4/blob/c2e2de728fe7eba592f74cd02266100cc21ec89a/determinant.js) - also see [gl-mat3](https://github.com/stackgl/gl-mat3) and [gl-mat2](https://github.com/stackgl/gl-mat2) -- [ndarray-determinant](https://www.npmjs.com/package/ndarray-determinant) -- [glsl-determinant](https://www.npmjs.com/package/glsl-determinant) -- [robust-determinant](https://www.npmjs.com/package/robust-determinant) -- [robust-determinant-2](https://www.npmjs.com/package/robust-determinant-2) and [robust-determinant-3](https://www.npmjs.com/package/robust-determinant-3), specifically for 2x2 and 3x3 matrices, respectively ## hat @@ -546,35 +589,52 @@ In geometry, the "hat" symbol above a character is used to represent a [unit vec 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: -```js -var a = [ 0, 4, -3 ] +```python +a = [ 0, 4, -3 ] normalize(a) -//=> [ 0, 0.8, -0.6 ] +# Out: [ 0, 0.8, -0.6 ] ``` +If a vector is that which has magnitude and direction, normalization of a vector +is the operation that deletes magnitude and preserves direction. + Here is the `normalize` function, operating on 3D vectors: -```js -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) +```python +def normalize(vec): + x = vec[0] + y = vec[1] + z = vec[2] + squaredLength = x * x + y * y + z * z + + if (squaredLength > 0): + length = math.sqrt(squaredLength) vec[0] = x / length vec[1] = y / length vec[2] = z / length - } + return vec -} ``` -Other implementations: +Which Numpy's **broadcasting** syntax sugar can do in fewer lines + +You should try to generalize this to vectors of arbitrary length yourself, +before reading this... + +Go, I mean it! + +```python +def normalize(vec): + vec = np.array(vec) # ensure that input is casted to numpy + length = np.linalg.norm(vec) + if length > 0: + return vec / length +``` +Notice that **broadcasting** here is just short for `[x / length for x in vec]`. +But it's actually **faster** on large input, because arrays. + +[*read* the Numpy docs. *Be* the Numpy docs](https://docs.scipy.org/doc/numpy/reference/routines.linalg.html) -- [gl-vec3/normalize](https://github.com/stackgl/gl-vec3/blob/507480fa57ba7c5fb70679cf531175a52c48cf53/normalize.js) and [gl-vec2/normalize](https://github.com/stackgl/gl-vec2/blob/21f460a371540258521fd2f720d80f14e87bd400/normalize.js) -- [vectors/normalize-nd](https://github.com/hughsk/vectors/blob/master/normalize-nd.js) (n-dimensional) ## element @@ -584,24 +644,26 @@ In set theory, the "element of" symbol `∈` and `∋` can be used to describe w -Here we have a set of numbers *A* `{ 3, 9, 14 }` and we are saying `3` is an "element of" that set. +Here we have a set of numbers *A* = `{ 3, 9, 14 }` and we are saying `3` is an "element of" that set. -A simple implementation in ES5 might look like this: +The `in` keyword plays the role of the elementhood function, giving a bool. -```js -var A = [ 3, 9, 14 ] +```python +A = [ 3, 9, 14 ] -A.indexOf(3) >= 0 -//=> true +3 in A +# Out: True ``` -However, it would be more accurate to use a `Set` which only holds unique values. This is a feature of ES6. +Python also has set. You can wrap any iterable or generator with the set keyword to delete +repeats. -```js -var A = new Set([ 3, 9, 14 ]) +```python +set([3,3,3,2,4,3,3,3,1,2,4,5,3]) +# Out: {1, 2, 3, 4, 5} -A.has(3) -//=> true +3 in set(range(1, 20, 4)) +# Out: False ``` The backwards `∋` is the same, but the order changes: @@ -616,6 +678,8 @@ You can also use the "not an element of" symbols `∉` and `∌` like so: +Which you know is represented by the convenient `not` keyword in python. + ## common number sets You may see some some large [Blackboard](https://en.wikipedia.org/wiki/Blackboard_bold) letters among equations. Often, these are used to describe sets. @@ -632,57 +696,99 @@ Listed below are a few common sets and their symbols. The large `ℝ` describes the set of *real numbers*. These include integers, as well as rational and irrational numbers. -JavaScript treats floats and integers as the same type, so the following would be a simple test of our *k* ∈ ℝ example: +Computers approximate `ℝ` with `float`. -```js -function isReal (k) { - return typeof k === 'number' && isFinite(k); -} +You can use `isinstance` to check "*k* ∈ ℝ", where float and `ℝ` aren't *really* +the same thing but the intuition is close enough. + +```python +isinstance(np.pi, float) +# Out: True ``` -*Note:* Real numbers are also *finite*, as in, *not infinite.* +Again, you may elevate that bool to an `assertion` that makes-or-breaks the whole program +with the `assert` keyword when you see fit. + +[Excellent resource on floats in python](https://youtu.be/zguLmgYWhM0) #### `ℚ` rational numbers -Rational numbers are real numbers that can be expressed as a fraction, or *ratio* (like `⅗`). Rational numbers cannot have zero as a denominator. +Rational numbers are real numbers that can be expressed as a fraction, or +*ratio*. Rational numbers cannot have zero as a denominator. + +Imagine taking `ℝ` and removing radicals (like `np.sqrt`) and logarithms (in a +family called +[transcendentals](https://en.wikipedia.org/wiki/Transcendental_function)), +that's basically what `ℚ` is. This also means that all integers are rational numbers, since the denominator can be expressed as 1. An irrational number, on the other hand, is one that cannot be expressed as a ratio, like π (PI). +You can work with rationals without dividing them into floatiness with the +[`fractions` standard module](https://docs.python.org/3.7/library/fractions.html) + #### `ℤ` integers -An integer, i.e. a real number that has no fractional part. These can be positive or negative. +An integer is a whole number. Just imagine starting from zero and one and +building out an inventory with addition and subtraction. -A simple test in JavaScript might look like this: +An integer has no division, no decimals. -```js -function isInteger (n) { - return typeof n === 'number' && n % 1 === 0 -} +```python +assert isinstance(8/7, int), "GO DIRECTLY TO JAIL" ``` #### `ℕ` natural numbers -A natural number, a positive and non-negative integer. Depending on the context and field of study, the set may or may not include zero, so it could look like either of these: +A natural number, a positive and non-negative integer. -```js -{ 0, 1, 2, 3, ... } -{ 1, 2, 3, 4, ... } -``` +Depending on the context and field of study, the set may or **start with zero**. -The former is more common in computer science, for example: +...ok but, between you and me, they start with zero. -```js -function isNaturalNumber (n) { - return isInteger(n) && n >= 0 -} -``` +`ℕ` is not a datatype in python, we can't use typechecking to disambiguate `int` +from `non-negative int`, but in a pinch you could easily write up #### `ℂ` complex numbers +As we saw earlier, the complex numbers are a particular wrapper around tuples of +reals. + A complex number is a combination of a real and imaginary number, viewed as a co-ordinate in the 2D plane. For more info, see [A Visual, Intuitive Guide to Imaginary Numbers](http://betterexplained.com/articles/a-visual-intuitive-guide-to-imaginary-numbers/). +We can say `ℂ = {a + b*i | a,b ∈ ℝ}`, which is a notation called + +## Set builder notation + +Pythoners call **Set builder notation* is just comprehension + +- `{ }`: delimiter around iterable (curlybois for `dict` or `set`, `[` for list) +- `a + b * i`: an expression (for instance, for a list of odd numbers this + expression was `2*k + 1`) to be evaluated for each item in source list. +- `|`: `for` +- `a,b ∈ ℝ`: this just shows that `a,b` are drawn from a particular place, in + this case the real numbers. + +So if you've been writing Python listcomps, that definition of the complex +numbers wasn't so bad! Say it with me this time + +`ℂ = {a + b*i | a,b ∈ ℝ}`` + +**inhaaaaaaless** *unison* "C IS THE SET OF a + b*i FOR REAL NUMBERS a AND b" + +If you want, you can draw up a grainy picture of an *interval* of ℂ with `zip` +and `np.linspace`, and of course list comprehension. + +```python +j = np.complex(0,1) + +R = np.linspace(-2, 2, 100) + +{a + b * j for a,b in zip(R, R)} +# too much to print but try it yourself. +``` + ## function [Functions](https://en.wikipedia.org/wiki/Function_%28mathematics%29) are fundamental features of mathematics, and the concept is fairly easy to translate into code. @@ -693,7 +799,7 @@ A function relates an input to an output value. For example, the following is a -We can give this function a *name*. Commonly, we use `ƒ` to describe a function, but it could be named `A(x)` or anything else. +We can give this function a *name*. Commonly, we use `ƒ` to describe a function, but it could be named `A` or anything else. ![function2](http://latex.codecogs.com/svg.latex?f%5Cleft%20%28x%20%5Cright%20%29%20%3D%20x%5E%7B2%7D) @@ -701,9 +807,9 @@ We can give this function a *name*. Commonly, we use `ƒ` to describe a function In code, we might name it `square` and write it like this: -```js -function square (x) { - return Math.pow(x, 2) +```python +def square(x): { + return math.pow(x, 2) } ``` @@ -713,22 +819,31 @@ Sometimes a function is not named, and instead the output is written. -In the above example, *x* is the input, the relationship is *squaring*, and *y* is the output. +In the above example, *x* is the input, the relationship is *squaring*, and *y* +is the output. We can express this as an equation because, conventionally, we +think of *x* as input and *y* as 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. +But we have a stronger idea called **anonymous functions** to generalize this. -![function4](http://latex.codecogs.com/svg.latex?f%28x%2Cy%29%20%3D%20%5Csqrt%7Bx%5E2%20+%20y%5E2%7D) +Just as we can name strings `x = "Alonzo"` then call them with their names *or* +we can just pass string *literals*, we also have **function literals**. - +Math first, then python: -In code: +`x ↦ x^2` is equivalent to the equational description above. -```js -function length (x, y) { - return Math.sqrt(x * x + y * y) -} +Nearly identical, but very different to the untrained eye, is `λx.x^2`, hence +the python keyword +```python +lambda x: x**2 ``` +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](http://latex.codecogs.com/svg.latex?f%28x%2Cy%29%20%3D%20%5Csqrt%7Bx%5E2%20+%20y%5E2%7D) + + + ### piecewise function Some functions will use different relationships depending on the input value, *x*. @@ -747,14 +862,13 @@ This is very similar to `if` / `else` in code. The right-side conditions are oft In piecewise functions, **"otherwise"** and **"elsewhere"** are analogous to the `else` statement in code. -```js -function f (x) { - if (x >= 1) { - return (Math.pow(x, 2) - x) / x - } else { +```python +def f(x): + if (x >= 1): + return (math.pow(x, 2) - x) / x + else: return 0 - } -} + ``` ### common functions @@ -774,12 +888,14 @@ One such example is the *sgn* function. This is the *signum* or *sign* function. In code, it might look like this: -```js -function sgn (x) { - if (x < 0) return -1 - if (x > 0) return 1 - return 0 -} +```python +def signum(x): + if (x < 0): + return -1 + elif (x > 0): + return 1 + else: + return 0 ``` See [signum](https://github.com/scijs/signum) for this function as a module. @@ -820,25 +936,28 @@ A function's *domain* and *codomain* is a bit like its *input* and *output* type The arrow here (without a tail) is used to map one *set* to another. -In JavaScript and other dynamically typed languages, you might use documentation and/or runtime checks to explain and validate a function's input/output. Example: +In Python and other dynamically typed languages, you might use documentation and/or runtime checks to explain and validate a function's input/output. Example: -```js -/** - * Squares a number. - * @param {Number} a real number - * @return {Number} a real number - */ -function square (a) { - if (typeof a !== 'number') { - throw new TypeError('expected a number') - } - return Math.pow(a, 2) -} +```python +def square_ints(k): + ''' FEED ME INTEGER ''' + try: + assert isinstance(k, int), "I HUNGER FOR AN INTEGER! " + return math.pow(k, 2) + except AssertionError as e: + raise e ``` -Some tools like [flowtype](http://flowtype.org/) attempt to bring static typing into JavaScript. +The python of a more glorious future as described in +[pep484](https://www.python.org/dev/peps/pep-0484/) proposes a static type +checker for Python, but no one's proposed anything shrewd enough to *prevent +code with type errors from compiling* for Python yet. -Other languages, like Java, allow for true method overloading based on the static types of a function's input/output. This is closer to mathematics: two functions are not the same if they use a different *domain*. +Other languages, like Java, allow for true method overloading based on the +static types of a function's input/output. This is closer to mathematics: two +functions are not the same if they use a different *domain*. This is also called +*polymorphism* and it explains why `'literally' + 'alonzo'` concats two strings +together but `1 + 1` is addition on numbers. ## prime @@ -862,14 +981,12 @@ Its derivative could be written with a prime `′` symbol: In code: -```js -function f (x) { +```python +def f(x): return Math.pow(x, 2) -} -function fPrime (x) { +def fPrime(x): return 2 * x -} ``` Multiple prime symbols can be used to describe the second derivative *ƒ′′* and third derivative *ƒ′′′*. After this, authors typically express higher orders with roman numerals *ƒ*IV or superscript numbers *ƒ*(n). @@ -888,22 +1005,23 @@ The special brackets `⌊x⌋` and `⌈x⌉` represent the *floor* and *ceil* fu In code: -```js -Math.floor(x) -Math.ceil(x) +```python +math.floor(4.8) +math.ceil(3.1) +np.floor(4.9) +np.ceil(3.001) ``` +> **Note**: the Numpy version returns a float, in the above example `4.0`, +> rather than the int `4` + When the two symbols are mixed `⌊x⌉`, it typically represents a function that rounds to the nearest integer: ![round](http://latex.codecogs.com/svg.latex?round%28x%29%20%3D%20%5Clfloor%20x%20%5Crceil) -In code: - -```js -Math.round(x) -``` +Python automatically gives you a keyword `round` to call on a number. ## arrows @@ -919,10 +1037,11 @@ Arrows like `⇒` and `→` are sometimes used in logic for *material implicatio Interpreting this as code might look like this: -```js -if (A === true) { - console.assert(B === true) -} +```python +def if_A_then_B: + if A: + assert B, "alas, not A!" + return B ``` The arrows can go in either direction `⇐` `⇒`, or both `⇔`. When *A ⇒ B* and *B ⇒ A*, they are said to be equivalent: @@ -931,15 +1050,15 @@ The arrows can go in either direction `⇐` `⇒`, or both `⇔`. When *A ⇒ B* -#### equality +#### inequality In math, the `<` `>` `≤` and `≥` are typically used in the same way we use them in code: *less than*, *greater than*, *less than or equal to* and *greater than or equal to*, respectively. -```js -50 > 2 === true -2 < 10 === true -3 <= 4 === true -4 >= 4 === true +```python +assert 50 > 2 +assert 2 < 10 +assert 3 <= 4 +assert 4 >= 4 ``` On rare occasions you might see a slash through these symbols, to describe *not*. As in, *k* is "not greater than" *j*. @@ -948,35 +1067,17 @@ On rare occasions you might see a slash through these symbols, to describe *not* -The `≪` and `≫` are sometimes used to represent *significant* inequality. That is, *k* is an [order of magnitude](https://en.wikipedia.org/wiki/Order_of_magnitude) larger than *j*. +The `≪` and `≫` are sometimes used to represent *significant* inequality. That +is, *k* is an [order of +magnitude](https://en.wikipedia.org/wiki/Order_of_magnitude) larger than *j*. +Sometimes read "beats", when I say `x^k ≫ log(x)` what I'm really saying is that +"polynomial functions grow an order of magnitude faster than logarithms; in a +word, the polynomial *beats* the logarithm." ![orderofmag](http://latex.codecogs.com/svg.latex?k%20%5Cgg%20j) -In mathematics, *order of magnitude* is rather specific; it is not just a "really big difference." A simple example of the above: - -```js -orderOfMagnitude(k) > orderOfMagnitude(j) -``` - -And below is our `orderOfMagnitude` function, using [Math.trunc](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc) (ES6). - -```js -function log10(n) { - // logarithm in base 10 - return Math.log(n) / Math.LN10 -} - -function orderOfMagnitude (n) { - return Math.trunc(log10(n)) -} -``` - -*Note:* This is not numerically robust. - -See [math-trunc](https://www.npmjs.com/package/math-trunc) for a ponyfill in ES5. - #### conjunction & disjunction Another use of arrows in logic is conjunction `∧` and disjunction `∨`. They are analogous to a programmer's `AND` and `OR` operators, respectively. @@ -987,20 +1088,17 @@ The following shows conjunction `∧`, the logical `AND`. -In JavaScript, we use `&&`. Assuming *k* is a natural number, the logic implies that *k* is 3: +In Python, we just say `and`. Assuming *k* is a natural number, the logic implies that *k* is 3: -```js -if (k > 2 && k < 4) { - console.assert(k === 3) -} +```python +lambda k: if (k > 2 and k < 4): assert k == 3, "Exercise: can this error ever be +raised?" ``` Since both sides are equivalent `⇔`, it also implies the following: -```js -if (k === 3) { - console.assert(k > 2 && k < 4) -} +```python +lambda k: if (k == 3): assert (k > 2 and k < 4), "I mean it, think through this exercise." ``` The down arrow `∨` is logical disjunction, like the OR operator. @@ -1009,11 +1107,8 @@ The down arrow `∨` is logical disjunction, like the OR operator. -In code: - -```js -A || B -``` +In Python, we have the `or` keyword. Like and, it is a function that will trade +you one bool for two bools. ## logical negation @@ -1027,10 +1122,9 @@ Here is a simple example using the *not* symbol: An example of how we might interpret this in code: -```js -if (x !== y) { - console.assert(!(x === y)) -} +```python +lambda x, y: if (x != y): assert not x == y, "arrr, buried treasure lost +forever. " ``` *Note:* The tilde `~` has many different meanings depending on context. For example, *row equivalence* (matrix theory) or *same order of magnitude* (discussed in [equality](#equality)). @@ -1063,16 +1157,11 @@ For example we to indicate that a point `x` is in the unit cube in 3D we say: -In code we can represent an interval using a two element 1d array: +In Python, we have to be sensitive about **inclusive vs. exclusive boundaries** +in generators like `range`, but you already know that. -```js -var nextafter = require('nextafter') +if you want to play with *infinite lists* in Python, learn more about [generators](https://jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/) -var a = [nextafter(0, Infinity), nextafter(1, -Infinity)] // open interval -var b = [nextafter(0, Infinity), 1] // interval closed on the left -var c = [0, nextafter(1, -Infinity)] // interval closed on the right -var d = [0, 1] // closed interval -``` Intervals are used in conjunction with set operations: @@ -1111,10 +1200,6 @@ Interval.difference(b, a) // {lo: 5, hi: 6} ``` -See: - -- [next-after](https://github.com/scijs/nextafter) -- [interval-arithmetic](https://github.com/maurizzzio/interval-arithmetic) ## more... From da2eea2e2d21fd7a14b9bdd012c8e9e28018e08f Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 01:48:51 -0400 Subject: [PATCH 06/17] Time to proofread --- PYTHON-README.md | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index f53f5be..9ec428c 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -1178,28 +1178,45 @@ Intervals are used in conjunction with set operations: -In code: +Integer versions in basic python look like this + +```python +# intersection of two int intervals +[x for x in range(3,5) if x in range(4, 6+1)] +# Out: [4] + +# Union of two int intervals +[x for x in range(20) if x in range(3, 5) or x in range(4, 6+1)] +# Out: [3, 4, 5, 6] + +# Set difference +[x for x in range(3, 5) if x not in range(4, 6+1)] +# Out: [3] -```js -var Interval = require('interval-arithmetic') -var nextafter = require('nextafter') +[x for x in range(4, 6+1) if x not in range(3, 5)] +# Out: [5, 6] +``` + +Using `np.linspace`, we can approximate what the real versions would look like. -var a = Interval(3, nextafter(5, -Infinity)) -var b = Interval(4, 6) +```python +R = np.linspace(-1, 9, 100) -Interval.intersection(a, b) -// {lo: 4, hi: 4.999999999999999} +# intersection of two float intervals +[x for x in R if 3 <= x < 5 and 4 <= x <= 6] -Interval.union(a, b) -// {lo: 3, hi: 6} +# Union of two float intervals +[x for x in R if 3 <= x < 5 or 4 <= x <= 6] -Interval.difference(a, b) -// {lo: 3, hi: 3.9999999999999996} +# set differences of two float intervals. +[x for x in R if 3 <= x < 5 and not (4 <= x <= 6)] -Interval.difference(b, a) -// {lo: 5, hi: 6} +[x for x in R if 4 <= x <= 6 and not (3 <= x < 5)] ``` +You should definitely run these in repl and try to wrap your head around them. + + ## more... From 0a3c430f3c9f974eb5a5a4abc464e79018fb9074 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 02:33:04 -0400 Subject: [PATCH 07/17] a lot of proof reading --- PYTHON-README.md | 82 +++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index 9ec428c..260caa4 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -284,20 +284,20 @@ k = [1, 2] j = [2, 3] tmp = multiply(k, j) -result = multiplyScalar(tmp, s) +result = multiply_scalar(tmp, s) # Out: [6, 18] ``` -Our `multiply` and `multiplyScalar` functions look like this: +Our `multiply` and `multiply_scalar` functions look like this: ```python -def multiply(a, b) { +def multiply(a, b): return [aa * bb for aa,bb in zip(a,b) -def multiplyScalar(a, scalar) { +def multiply_scalar(a, scalar): return [scalar * aa for aa in a] -} + ``` Similarly, matrix multiplication typically does not use the dot `·` or cross symbol `×`. Matrix multiplication will be covered in a later section. @@ -358,11 +358,8 @@ def cross(a, b): return rx, ry, rz ``` -For other implementations of vector multiplication, cross product, and dot product: - -- [gl-vec3](https://github.com/stackgl/gl-vec3) -- [gl-vec2](https://github.com/stackgl/gl-vec2) -- [vectors](https://github.com/hughsk/vectors) - includes n-dimensional +It's good to practice and grok these operations, but in real life you'll use +Numpy. ## sigma @@ -572,12 +569,12 @@ squished-stretched-rotated**. np.linalg.det(np.identity(100)) # the determinant of the 100 x 100 identity matrix is still one, because the identity matrix doesn't squish, stretch, or rotate at all. # Out: 1.0 -np.linalg.det(np.array([[x, y], [z, w]])) # look up the 90 degree rotation. +np.linalg.det(np.array([[0, -1], [1, 0]])) # 90 degree rotation. # Out: 1.0 -# ``` +The second matrix was the [**2D rotation**](https://en.wikipedia.org/wiki/Rotation_matrix) at 90 degrees. ## hat @@ -655,7 +652,7 @@ A = [ 3, 9, 14 ] # Out: True ``` -Python also has set. You can wrap any iterable or generator with the set keyword to delete +Python also has `set`. You can wrap any iterable or generator with the `set` keyword to delete repeats. ```python @@ -719,11 +716,17 @@ Rational numbers are real numbers that can be expressed as a fraction, or Imagine taking `ℝ` and removing radicals (like `np.sqrt`) and logarithms (in a family called [transcendentals](https://en.wikipedia.org/wiki/Transcendental_function)), -that's basically what `ℚ` is. +that's basically what `ℚ` is, at least enough for a rough first approximation. This also means that all integers are rational numbers, since the denominator can be expressed as 1. -An irrational number, on the other hand, is one that cannot be expressed as a ratio, like π (PI). +An irrational number, on the other hand, is one that cannot be expressed as a ratio, like π (`math.pi`). + +A reason a programmer might care about the difference between Q and R is in the +design of unit tests--- *fractions are terminating decimals*, and sometimes when +you're a 100% sure that a number will be a basic rational (like counting change, +`0.25, 0.10, 0.05`, etc.), you're allowed to use `==` in unit tests rather than +`isclose` or `assert_almost_equal`. You can work with rationals without dividing them into floatiness with the [`fractions` standard module](https://docs.python.org/3.7/library/fractions.html) @@ -741,14 +744,22 @@ assert isinstance(8/7, int), "GO DIRECTLY TO JAIL" #### `ℕ` natural numbers -A natural number, a positive and non-negative integer. +A natural number, a non-negative integer. + +This is actually the only set invented by the [flying spaghetti monster](https://www.brainyquote.com/quotes/leopold_kronecker_338745): as for the +others, humans have only themselves to blame. Depending on the context and field of study, the set may or **start with zero**. ...ok but, between you and me, they start with zero. -`ℕ` is not a datatype in python, we can't use typechecking to disambiguate `int` -from `non-negative int`, but in a pinch you could easily write up +`ℕ` also happens to be the first **inductive construction** in the [study of +datatypes](https://en.wikipedia.org/wiki/Semantics_(computer_science)), consisting of a single axiom ("Zero is a `ℕ`") and a single +inference rule ("if `n` is a `ℕ` then `n + 1` is also a `ℕ`") + +`ℕ` is not a datatype in python, we can't use *typechecking* to disambiguate `int` +from `non-negative int`, but in a pinch you could easily write up something that +combines `x >= 0` judgments with `isinstance(x, int)`. #### `ℂ` complex numbers @@ -761,10 +772,10 @@ We can say `ℂ = {a + b*i | a,b ∈ ℝ}`, which is a notation called ## Set builder notation -Pythoners call **Set builder notation* is just comprehension +Pythoners have a name for *Set builder notation*; and the name is comprehension - `{ }`: delimiter around iterable (curlybois for `dict` or `set`, `[` for list) -- `a + b * i`: an expression (for instance, for a list of odd numbers this +- `a + b * i`: an expression (for instance, earlier when we made a list of odd numbers this expression was `2*k + 1`) to be evaluated for each item in source list. - `|`: `for` - `a,b ∈ ℝ`: this just shows that `a,b` are drawn from a particular place, in @@ -793,7 +804,7 @@ R = np.linspace(-2, 2, 100) [Functions](https://en.wikipedia.org/wiki/Function_%28mathematics%29) are fundamental features of mathematics, and the concept is fairly easy to translate into code. -A function relates an input to an output value. For example, the following is a function: +A **function** maps an input to an output value. For example, the following is a function: ![function1](http://latex.codecogs.com/svg.latex?x%5E%7B2%7D) @@ -808,9 +819,9 @@ We can give this function a *name*. Commonly, we use `ƒ` to describe a function In code, we might name it `square` and write it like this: ```python -def square(x): { +def square(x): return math.pow(x, 2) -} + ``` Sometimes a function is not named, and instead the output is written. @@ -844,6 +855,23 @@ Functions can also have multiple parameters, like in a programming language. The +### dictionaries are functions + +Sometimes mathematicians, like software developers, need to specify *maps* by +*enumerating each input-output pair** when there is no expression that computes +output from input. + +**Note**: formally, mathematicians require that **functions not be ambiguous***, +so when you have a function and you have an input, there can be no uncertainty +as to what the output should be; you mustn't be confused about whether an apple +is red or purple (in introductory algebra courses this is called +the "vertical line test", but it applies to all maps). Notice that the +implementation of hash maps already guarantees this in the case of dictionaries! +Notice also that we make no such requirement on +*outputs*, both an apple *and* a banana can land on purple! With caveats like +these, we can [study the properties of different kinds of functions](https://en.wikipedia.org/wiki/Bijection,_injection_and_surjection) +into different kinds, important in compression and security engineering. + ### piecewise function Some functions will use different relationships depending on the input value, *x*. @@ -985,7 +1013,7 @@ In code: def f(x): return Math.pow(x, 2) -def fPrime(x): +def f_prime(x): return 2 * x ``` @@ -1091,8 +1119,7 @@ The following shows conjunction `∧`, the logical `AND`. In Python, we just say `and`. Assuming *k* is a natural number, the logic implies that *k* is 3: ```python -lambda k: if (k > 2 and k < 4): assert k == 3, "Exercise: can this error ever be -raised?" +lambda k: if (k > 2 and k < 4): assert k == 3, "Exercise: can this error ever be raised?" ``` Since both sides are equivalent `⇔`, it also implies the following: @@ -1123,8 +1150,7 @@ Here is a simple example using the *not* symbol: An example of how we might interpret this in code: ```python -lambda x, y: if (x != y): assert not x == y, "arrr, buried treasure lost -forever. " +lambda x, y: if (x != y): assert not x == y, "arrr, buried treasure lost forever. " ``` *Note:* The tilde `~` has many different meanings depending on context. For example, *row equivalence* (matrix theory) or *same order of magnitude* (discussed in [equality](#equality)). From 833eb5b137162a3eae0a540d21a05be51d1085af Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 02:47:09 -0400 Subject: [PATCH 08/17] done --- PYTHON-README.md | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index 260caa4..fe9bd88 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -622,6 +622,7 @@ Go, I mean it! ```python def normalize(vec): + ''' *sigh* if you don't do it yourself you'll never learn! ''' vec = np.array(vec) # ensure that input is casted to numpy length = np.linalg.norm(vec) if length > 0: @@ -630,7 +631,7 @@ def normalize(vec): Notice that **broadcasting** here is just short for `[x / length for x in vec]`. But it's actually **faster** on large input, because arrays. -[*read* the Numpy docs. *Be* the Numpy docs](https://docs.scipy.org/doc/numpy/reference/routines.linalg.html) +[*Read* the Numpy docs. *BE* the Numpy docs](https://docs.scipy.org/doc/numpy/reference/routines.linalg.html) ## element @@ -726,7 +727,8 @@ A reason a programmer might care about the difference between Q and R is in the design of unit tests--- *fractions are terminating decimals*, and sometimes when you're a 100% sure that a number will be a basic rational (like counting change, `0.25, 0.10, 0.05`, etc.), you're allowed to use `==` in unit tests rather than -`isclose` or `assert_almost_equal`. +`isclose` or `assert_almost_equal`. The point is that you know not to use exact +equality `==` when anything like `sqrt` or `log` is involved! You can work with rationals without dividing them into floatiness with the [`fractions` standard module](https://docs.python.org/3.7/library/fractions.html) @@ -747,11 +749,11 @@ assert isinstance(8/7, int), "GO DIRECTLY TO JAIL" A natural number, a non-negative integer. This is actually the only set invented by the [flying spaghetti monster](https://www.brainyquote.com/quotes/leopold_kronecker_338745): as for the -others, humans have only themselves to blame. +others, humans have themselves to blame. -Depending on the context and field of study, the set may or **start with zero**. +Depending on the context and field of study, the set may or may not **start with zero**. -...ok but, between you and me, they start with zero. +...ok but, between you and me, **they 200% start with zero**. `ℕ` also happens to be the first **inductive construction** in the [study of datatypes](https://en.wikipedia.org/wiki/Semantics_(computer_science)), consisting of a single axiom ("Zero is a `ℕ`") and a single @@ -772,7 +774,7 @@ We can say `ℂ = {a + b*i | a,b ∈ ℝ}`, which is a notation called ## Set builder notation -Pythoners have a name for *Set builder notation*; and the name is comprehension +Pythoners have a name for *set builder notation*; and the name is comprehension - `{ }`: delimiter around iterable (curlybois for `dict` or `set`, `[` for list) - `a + b * i`: an expression (for instance, earlier when we made a list of odd numbers this @@ -800,11 +802,11 @@ R = np.linspace(-2, 2, 100) # too much to print but try it yourself. ``` -## function +## functions [Functions](https://en.wikipedia.org/wiki/Function_%28mathematics%29) are fundamental features of mathematics, and the concept is fairly easy to translate into code. -A **function** maps an input to an output value. For example, the following is a function: +A **function** transforms an input into an output value. For example, the following is a function: ![function1](http://latex.codecogs.com/svg.latex?x%5E%7B2%7D) @@ -830,7 +832,7 @@ Sometimes a function is not named, and instead the output is written. -In the above example, *x* is the input, the relationship is *squaring*, and *y* +In the above example, *x* is the input, the transformation is *squaring*, and *y* is the output. We can express this as an equation because, conventionally, we think of *x* as input and *y* as output. @@ -981,6 +983,20 @@ The python of a more glorious future as described in checker for Python, but no one's proposed anything shrewd enough to *prevent code with type errors from compiling* for Python yet. +As we will see in the following sample of `pep484` Python, the set +interpretation of domain and codomain makes way for a *types* interpretation of +domain and codomain + +```python +def square(x: float) -> float: + ''' a pep484 annotation isn't that different from if i declared in the docstring; + + input/domain: a float + output/codomain: another float + ''' + return x**2 +``` + Other languages, like Java, allow for true method overloading based on the static types of a function's input/output. This is closer to mathematics: two functions are not the same if they use a different *domain*. This is also called From 64a1c2a117349b567e9e70427124c2cc6e8e31c3 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 02:57:27 -0400 Subject: [PATCH 09/17] changed header --- PYTHON-README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index fe9bd88..3c3b9a8 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -1,6 +1,7 @@ # math-as-code ->[Chinese translation (中文版)](./README-zh.md) +>[Chinese translation of javascript version (中文版)](./README-zh.md) +>[Javascript version in english](./README.md) This is a reference to ease developers into mathematical notation by showing comparisons with Python code. From 433eac26b39cbeafc6ebde6344badec003b413f5 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 02:57:45 -0400 Subject: [PATCH 10/17] changed header --- PYTHON-README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index 3c3b9a8..7a5a364 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -1,4 +1,4 @@ -# math-as-code +# math-as-code-python >[Chinese translation of javascript version (中文版)](./README-zh.md) >[Javascript version in english](./README.md) From 47dab517ed2ee212061e2272fafb81c0ae02f03a Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 03:00:47 -0400 Subject: [PATCH 11/17] added z.real and z.imag --- PYTHON-README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index 7a5a364..7ba3447 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -209,8 +209,10 @@ assert cmath.sqrt(complex(-1, 0)) == complex(0,1) As you can see, it uses `j` to denote the imaginary unit, instead of `i`. -The **conjugate** of a complex number is flipping the plus in the middle to -minus. +The **conjugate** of a complex number is **flipping the sign of the imaginary part**. + +If `z` is a python `complex` number, `z.real` gets the real part (exactly as an +object attribute) and `z.imag` gets the imaginary part. Just as complex numbers can be interpreted as a sort of wrapper around tuples of reals, a complex number data type wraps two floats. Numpy uses this to implement From 40048cd523b27b402583be95c8c3d712e9d8c828 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 09:47:22 -0400 Subject: [PATCH 12/17] fixed scalars, added broadcasting --- PYTHON-README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index 7ba3447..375f6cd 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -298,12 +298,19 @@ def multiply(a, b): return [aa * bb for aa,bb in zip(a,b) -def multiply_scalar(a, scalar): +def multiply_scalar(scalar, a): return [scalar * aa for aa in a] ``` -Similarly, matrix multiplication typically does not use the dot `·` or cross symbol `×`. Matrix multiplication will be covered in a later section. +Similarly, matrix multiplication typically does not use the dot `·` or cross symbol `×`. + +Numpy's broadcasted syntax for scaling looks like this: + +```python +def multiply_scalar(scalar, a): + return scalar * np.array(a) +``` #### dot product From 9ef2a4ed05f3d7ec8bca30c3c5f23ad8a6f31aa2 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Mon, 13 May 2019 12:09:49 -0400 Subject: [PATCH 13/17] added link to trevtutor for list indexing --- PYTHON-README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PYTHON-README.md b/PYTHON-README.md index 375f6cd..1e09145 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -1,6 +1,7 @@ # math-as-code-python >[Chinese translation of javascript version (中文版)](./README-zh.md) + >[Javascript version in english](./README.md) This is a reference to ease developers into mathematical notation by showing comparisons with Python code. @@ -415,6 +416,8 @@ sum([2*k + 1 for k in range(100)]) upper bound*, meaning that `... for k in range(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](https://youtu.be/TDpQSa3hJRw) on youtube. + 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. However, in the following case, since we are From 27ffc0f4bf1eb464e9d9b712f3111295926bd121 Mon Sep 17 00:00:00 2001 From: Quinn Dougherty Date: Wed, 29 May 2019 17:58:05 -0400 Subject: [PATCH 14/17] added link to PYTHON-README in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f540dab..fed565f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # math-as-code >[Chinese translation (中文版)](./README-zh.md) +>[Python version in english](./PYTHON-README.md) This is a reference to ease developers into mathematical notation by showing comparisons with JavaScript code. From 252c1b961b1c1eb15c33d8a410faf771ba21364c Mon Sep 17 00:00:00 2001 From: Matt DesLauriers Date: Thu, 30 May 2019 09:25:49 +0100 Subject: [PATCH 15/17] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fed565f..8ce9549 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # math-as-code ->[Chinese translation (中文版)](./README-zh.md) ->[Python version in english](./PYTHON-README.md) +>[Chinese translation (中文版)](./README-zh.md) +>[Python version (English)](./PYTHON-README.md) This is a reference to ease developers into mathematical notation by showing comparisons with JavaScript code. From fbe480c58ca6442bd60d22c8cd08456625426c5f Mon Sep 17 00:00:00 2001 From: Matt DesLauriers Date: Thu, 30 May 2019 09:26:27 +0100 Subject: [PATCH 16/17] Update PYTHON-README.md --- PYTHON-README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index 1e09145..f025b1f 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -1,8 +1,7 @@ # math-as-code-python ->[Chinese translation of javascript version (中文版)](./README-zh.md) - ->[Javascript version in english](./README.md) +>[Chinese translation of javascript version (中文版)](./README-zh.md) +>[JavaScript version (English)](./README.md) This is a reference to ease developers into mathematical notation by showing comparisons with Python code. From 85d55a7191ecd1c0b258b674f2d46b805a3cc454 Mon Sep 17 00:00:00 2001 From: Matt DesLauriers Date: Thu, 30 May 2019 09:26:49 +0100 Subject: [PATCH 17/17] Update PYTHON-README.md --- PYTHON-README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PYTHON-README.md b/PYTHON-README.md index f025b1f..9106af9 100644 --- a/PYTHON-README.md +++ b/PYTHON-README.md @@ -1,4 +1,4 @@ -# math-as-code-python +# math-as-code (Python version) >[Chinese translation of javascript version (中文版)](./README-zh.md) >[JavaScript version (English)](./README.md)