760 lines
24 KiB
Plaintext
760 lines
24 KiB
Plaintext
---
|
|
title: "Counting in 2D: Lines, Leaves, and Sand"
|
|
format:
|
|
html:
|
|
html-math-method: katex
|
|
date: "2021-02-23"
|
|
date-modified: "2025-2-14"
|
|
jupyter: python3
|
|
categories:
|
|
- algebra
|
|
- python
|
|
execute:
|
|
eval: false
|
|
---
|
|
|
|
```{python}
|
|
#| echo: false
|
|
|
|
import sympy
|
|
from sympy.abc import x, y
|
|
from sympy.plotting import plot_implicit
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
|
|
from IPython.display import (
|
|
Video,
|
|
Latex,
|
|
display,
|
|
display_latex,
|
|
)
|
|
|
|
from carry2d.carry import Carry
|
|
from carry2d.anim import animate_carry_count
|
|
from carry2d.expansion import (
|
|
poly_from_array,
|
|
latex_polynumber,
|
|
add as carry_add,
|
|
)
|
|
```
|
|
|
|
|
|
Previously, I've written about positional systems and so-called "[polynomial counting](../1)".
|
|
Basically, this generalizes familiar integer bases like two and ten into less familiar irrational ones like the golden ratio.
|
|
However, all systems I presented have something in common: they all use polynomials of a single variable.
|
|
Does it make sense to count in two variables?
|
|
|
|
|
|
Preliminaries
|
|
-------------
|
|
|
|
Before proceeding, I will summarize some of the restrictions on which polynomials can be
|
|
used as carries in positional systems.
|
|
Carry polynomials must be nonpositive when evaluated at 1 (otherwise the digital root of
|
|
an expansion is unbounded) and either:
|
|
|
|
- All coefficients have the same sign but one, which has a much larger
|
|
magnitude than the rest (and forces the digital root condition)
|
|
- These are *explicit* irrational carries
|
|
- They are monic (have leading coefficient 1) and the remaining coefficients are all negative
|
|
and monotonically increase toward 0 after the second coefficient (0's are allowed anywhere)
|
|
- These are *implicit* irrational carries
|
|
|
|
Implicit carries must be multiplied, typically by cylotomic polynomials, to obtain additional
|
|
rules for larger numerals.
|
|
|
|
|
|
A Game of Che...ckers
|
|
---------------------
|
|
|
|
[Conway's checkers](https://en.wikipedia.org/wiki/Conway%27s_Soldiers) is a game played by attempting to
|
|
advance game pieces to the furthest extent beyond a line.
|
|
A piece can only be moved by jumping it horizontally or vertically over others, which removes them from play.
|
|
Squinting hard enough, in one dimension, this is similar to how in phinary, a "1" in an
|
|
expansion can jump over an adjacent "1" to its left.
|
|
|
|
$$
|
|
\begin{array}{}
|
|
\bullet \\ \hline \bullet \\ \hline \vphantom{\bullet}
|
|
\end{array} \huge⤸ \normalsize
|
|
~\longrightarrow~
|
|
\begin{array}{}
|
|
\vphantom{\bullet} \\ \hline \vphantom{\bullet} \\ \hline \bullet
|
|
\end{array}
|
|
\quad \iff \quad
|
|
0\textcolor{red}{11} = \textcolor{red}{1}00
|
|
$$
|
|
|
|
In fact, it is exactly the same.
|
|
The best solution of the game relies on a geometric series in *φ*, as seen in
|
|
[this video by Numberphile](https://www.youtube.com/watch?v=Or0uWM9bT5w).
|
|
|
|
|
|
### Pairs of Checkers
|
|
|
|
To actually take phinary and systems like it from 1D to 2D, we really just need a carry rule that respects the
|
|
second dimension.
|
|
To do this, we use a new variable *y* and invent a carry rule based on it.
|
|
|
|
The smallest way to extend our one-dimensional string of digits is do just introduce a second string.
|
|
Based on this, my intuition was to replace the standard hop with the movement of a knight in chess.
|
|
|
|
$$
|
|
\begin{gather*}
|
|
\begin{array}{c|c}
|
|
\bullet & \phantom{\bullet} \\
|
|
\hline \bullet \\ \hline \vphantom{\bullet}
|
|
\end{array}
|
|
~\longrightarrow~
|
|
\begin{array}{c|c}
|
|
\phantom{\bullet} & \phantom{\bullet} \\
|
|
\hline \\ \hline & \bullet
|
|
\end{array}
|
|
~~ \iff ~~
|
|
\left. \begin{array}{}
|
|
0\textcolor{red}{11}_x \\
|
|
000_y
|
|
\end{array} \right]
|
|
= \left. \begin{array}{}
|
|
000_x \\
|
|
\textcolor{red}{1}00_y
|
|
\end{array} \right ] \\ \\
|
|
y^2 = x + 1,~ x^2 = y + 1
|
|
\end{gather*}
|
|
$$
|
|
|
|
This looks good on the surface, but it turns out that the polynomials aren't sufficiently intertwined.
|
|
Since each variable is a function of the other, they can easily be coaxed into a single polynomial:
|
|
|
|
$$
|
|
y = x^2 - 1 \longrightarrow y^2 = (x^2 - 1)^2 = x + 1 \\[6pt]
|
|
x^4 - 2x^2 + 1 = x + 1 \\
|
|
x^3 - 2x - 1 = 0 \\
|
|
(x + 1)(x^2 - x - 1) = 0
|
|
$$
|
|
|
|
The same argument applies symmetrically for *y*.
|
|
One of the resultant polynomials is the minimal polynomial of *φ*, so the prospective
|
|
2D system reduces back to standard phinary.
|
|
|
|
There are additional problems with this "pairs of strings" model.
|
|
For example, terms like *xy* never appear, and there is no way to represent them by just using two strings.
|
|
Also, both sequences have a ones' place, but should agree that $x^0 = y^0 = 1$.
|
|
|
|
|
|
Building the Plane
|
|
------------------
|
|
|
|
If it is the case that both powers of variables should share the one's digit, then why not
|
|
join the two strings there?
|
|
In other words, for each variable, we think of a line with a distinguished point for zero.
|
|
We then match the distinguished points together.
|
|
Thus, instead of a point separator between positive and negative powers,
|
|
the radix point is now a pair of lines that share a vertex.
|
|
From this pair of lines, according to Euclid, we can make a plane.
|
|
|
|
$$
|
|
\begin{array}{|c}
|
|
\hline
|
|
f & d & a \\
|
|
e & b \\
|
|
c
|
|
\end{array}
|
|
\begin{matrix} ~\equiv~
|
|
ax^2 + bxy + cy^2 + dx + ey + f, \\
|
|
\text{the general form of a conic}
|
|
\end{matrix}
|
|
$$
|
|
|
|
Since the horizontal and vertical correspond with the *x* and *y* appearing in polynomials,
|
|
it may be easy to confuse this with the *x*- and *y*-axes of the typical Cartesian plane.
|
|
The key differences here are that these are discrete (two-dimensional) strings, rather than a continuous plane,
|
|
and along each point, there exists a cell in which an integer value lives.
|
|
|
|
|
|
### An Arithmetical Aside
|
|
|
|
Expressed in this way, it is very simple to compute the sum of two polynomials.
|
|
Just like in the 1D system, we can align the one's places, add them together term-by-term, then apply carry rules.
|
|
With luck, this produces a canonical form.
|
|
|
|
It should also be possible to multiply two polynomials together.
|
|
In a single variable, multiplication is a somewhat involved 1D convolution.
|
|
In two, it is naturally [2D convolution](https://en.wikipedia.org/wiki/Kernel_(image_processing)),
|
|
an operation typically encountered in image processing and signal analysis.
|
|
I won't bother building up an intuition for this, since it won't be relevant to discussion.
|
|
|
|
Even if multiplication is difficult, division is always harder.
|
|
In polynomials of a single variable, we can do synthetic (or long) division.
|
|
Notationally, we work in the direction that the digits do not extend (i.e., downward).
|
|
With 2D numbers, we have digits in both dimensions.
|
|
Fortunately, computer algebra systems can divide and factor polynomials of two variables
|
|
reasonably quickly, so this point is moot if we allow ourselves to use one.
|
|
|
|
|
|
1, 2, 3, Takeoff
|
|
----------------
|
|
|
|
Where should we start in a world of planar numbers?
|
|
Well, we want to assign an integer to a polynomial with extent in both *x* and *y*.
|
|
The simplest way to do this is by letting $n = x + y$: when the one's place has
|
|
a magnitude of *n*, it flows into *x* and *y*.
|
|
|
|
If $n = 1$, then the sum of coefficients is skewed toward the LHS, and the digital root is unbounded.
|
|
We had a similar restriction in one dimension.
|
|
Therefore, let $n = 2$.
|
|
The expansions of the powers of 2 are:
|
|
|
|
$$
|
|
\begin{matrix}
|
|
&&\text{Carry:}&\begin{array}{|c} \hline
|
|
-2 & 1 \\
|
|
1
|
|
\end{array}\\
|
|
1 & 2 & ... & 4 & ... & 8 \\
|
|
\begin{array}{|c} \hline
|
|
1
|
|
\end{array} &
|
|
\begin{array}{|c} \hline
|
|
0 & 1 \\
|
|
1
|
|
\end{array} & ... &
|
|
\begin{array}{|c} \hline
|
|
0 & 2 \\
|
|
2
|
|
\end{array} & ... &
|
|
\begin{array}{|c} \hline
|
|
0 & 4 \\
|
|
4
|
|
\end{array} \\ \\ &&&
|
|
\begin{array}{|c} \hline
|
|
0 & 0 & 1 \\
|
|
0 & 0 & 1 \\
|
|
1 & 1
|
|
\end{array} & ... &
|
|
\begin{array}{|c} \hline
|
|
0 & 0 & 2 \\
|
|
0 & 0 & 2 \\
|
|
2 & 2
|
|
\end{array} \\ \\ &&&&&
|
|
\begin{array}{|c} \hline
|
|
0 & 0 & 0 & 1 \\
|
|
0 & 0 & 1 & 1 \\
|
|
0 & 1 & 0 & 1 \\
|
|
1 & 1 & 1
|
|
\end{array}
|
|
\end{matrix}
|
|
$$
|
|
|
|
The final entry of each column is achieved by applying the carry enough times, reducing all digits
|
|
to "0"s and "1"s.
|
|
In doing so, the typical binary expansion is visible along first row and column, albeit reversed.
|
|
This is always the case, since when the carry is restricted to a single dimension (i.e., let $y = 0$),
|
|
we get the 1D binary carry.
|
|
More visually, there is no mechanism for the carry to disturb the column and row by spreading either left or up.
|
|
I'll call this system *dibinary*, since it is binary across two variables.
|
|
|
|
An additional property of this system is that the number of "1"s that appear in the expansion is equal
|
|
to the number itself.
|
|
These two properties make it a sort of hybrid between unary and binary, bridging their
|
|
binary expansion and its [Hamming weight](https://en.wikipedia.org/wiki/Hamming_weight).
|
|
|
|
This notation is very heavy, but fortunately lends itself well visualization.
|
|
Each integer's expansion is more or less an image, so we can arrange each as frames in a video:
|
|
|
|
```{python}
|
|
#| code-fold: true
|
|
|
|
animate_carry_count(
|
|
carry = Carry([
|
|
[-2, 1],
|
|
[ 1, 0]
|
|
]),
|
|
frames=list(range(200))
|
|
).save("count_xy2.mp4")
|
|
|
|
Video("count_xy2.mp4")
|
|
```
|
|
|
|
Starting at the expansion of twelve, the furthest extent is not in the first column or row.
|
|
It instead grows faster along the diagonal, along which larger triangles appear when
|
|
counting to higher and higher integers.
|
|
Meanwhile, lower degrees (toward the upper left) appear to be slightly less predictable.
|
|
|
|
|
|
### Higher *n*
|
|
|
|
By incrementing *n*, the expansions of numbers shrink exponentially, and simply incrementing is not fast enough.
|
|
Fortunately, this is a simple enough system that we can scalar-multiply base expansions, then assiduously apply the carry.
|
|
Ascending the powers of *n* in this manner for the carries where *n* = 3 and 4:
|
|
|
|
```{python}
|
|
#| code-fold: true
|
|
#| layout-ncol: 2
|
|
|
|
animate_carry_count(
|
|
carry = Carry([
|
|
[-3, 1],
|
|
[ 1, 0]
|
|
]),
|
|
operation="multiply",
|
|
op_val=3,
|
|
frames=list(range(200))
|
|
).save("count_xy3.mp4")
|
|
|
|
animate_carry_count(
|
|
carry = Carry([
|
|
[-4, 1],
|
|
[ 1, 0]
|
|
]),
|
|
operation="multiply",
|
|
op_val=4,
|
|
frames=list(range(200)),
|
|
).save("count_xy4.mp4")
|
|
|
|
display(
|
|
Video("count_xy3.mp4"),
|
|
Video("count_xy4.mp4")
|
|
)
|
|
```
|
|
|
|
Yellow corresponds to the highest allowed numeral, $n - 1$.
|
|
It seems like the lower-right grows much rounder as *n* increases.
|
|
However, neither appears to have an emergent pattern as dibinary did.
|
|
|
|
You might be familiar with the way hexadecimal is a more compact version of binary.
|
|
For example,
|
|
|
|
$$\textcolor{red}{1}\textcolor{blue}{2}_{16} = \textcolor{red}{0001}\textcolor{blue}{0010}_{2}$$
|
|
|
|
because both one hexadecimal digit and four binary digits can range over zero to fifteen.
|
|
Consequently, by grouping group *m* digits of an expansion in base *b*, one can easily convert to base $b^m$.
|
|
|
|
If such a procedure still exists in 2D, applying it somehow destroys the pattern in the $n = 2$ case,
|
|
as the quaternary system is very chaotic.
|
|
|
|
|
|
Carry Curves
|
|
------------
|
|
|
|
Since the carry is a polynomial in both *x* and *y*, it lends itself to interpretation as an algebraic curve.
|
|
Taking *x* or *y* to higher powers will dilate expansions along that axis.
|
|
|
|
The carry is not the only curve which exists in a system, as each integer is also a polynomial unto itself.
|
|
Adding a constant term will not change anything unless it causes carries to occur.
|
|
Starting at the carry digit, some of the curves made by the carry $x + y = 2$ are shown below.
|
|
|
|
```{python}
|
|
#| code-fold: true
|
|
#| layout: [[3, 3], [2, 2, 2]]
|
|
|
|
expansion = np.zeros((50, 50), dtype=np.int64)
|
|
xy2 = Carry([
|
|
[-2, 1],
|
|
[ 1, 0],
|
|
])
|
|
|
|
for i in range(2):
|
|
carry_add(expansion, xy2, 2)
|
|
plot_implicit(
|
|
poly_from_array(expansion) - (2 * i + 2),
|
|
title=f"{2*i + 2}"
|
|
)
|
|
plt.show()
|
|
|
|
carry_add(expansion, xy2, 8)
|
|
|
|
for i in range(3):
|
|
plot_implicit(
|
|
poly_from_array(expansion) - (26*i + 12),
|
|
title=f"{26*i + 12}",
|
|
)
|
|
plt.show()
|
|
carry_add(expansion, xy2, 26)
|
|
```
|
|
|
|
In each of these figures, shapes like hyperbolae show up and disappear, only to be replaced with
|
|
higher-order curves.
|
|
These higher order curves can be difficult to graph, so they show up as thicker lines.
|
|
At sixty-four, it appears as though the reflection of the carry across the origin
|
|
(the curve $x + y = -2$) "wants" to appear.
|
|
|
|
The continued presence of the original carry curve in larger integers suggests that
|
|
the points it passes through are important.
|
|
This curve happens to intersect with the *x* and *y* axes, which we can interpret as the "base"
|
|
with respect to a single variable.
|
|
For dibinary, these are the aforementioned "typical binary expansions".
|
|
We can also evaluate the polynomial at $(x, y) = (1, 1)$, a point on the carry curve,
|
|
to compute its digital root, which is also equal to the integer it represents.
|
|
|
|
This underlies a key difference from the 1D case.
|
|
In higher dimensions, there is no proper "base" we can plug in to "test" that an expansion
|
|
is valid like we can with the golden ratio in phinary.
|
|
1D systems are special in this regard: the carry can be equated with a base (or bases), which
|
|
is a discrete zero-dimensional point on a number line.
|
|
|
|
|
|
Casting the Line
|
|
----------------
|
|
|
|
We may want to extend the "unary" property to other carries.
|
|
To do this, we construct a curve which passes through $(1, 1)$.
|
|
|
|
Lines of the form $(n - 1)x + y = n$ trivially satisfy this relationship.
|
|
Further, while the digits along the *y* column will be expansions of base *n*, those along *x* will
|
|
be in base $n / (n - 1)$.
|
|
|
|
For example, for $n = 3$
|
|
|
|
$$
|
|
\begin{matrix}
|
|
&&&\text{Carry:}&
|
|
\begin{array}{|c} \hline
|
|
-3 & 2 \\
|
|
1
|
|
\end{array}\\
|
|
1 & ... & 3 & ... & 6 & ... & 9 \\
|
|
\begin{array}{|c} \hline
|
|
1
|
|
\end{array} & ... &
|
|
\begin{array}{|c} \hline
|
|
0 & 2 \\
|
|
1
|
|
\end{array} & ... &
|
|
\begin{array}{|c} \hline
|
|
0 & 4 \\
|
|
2
|
|
\end{array} & ... &
|
|
\begin{array}{|c} \hline
|
|
0 & 6 \\
|
|
3
|
|
\end{array} \\ \\ &&&&
|
|
\begin{array}{|c} \hline
|
|
0 & 1 & 2 \\
|
|
2 & 1
|
|
\end{array} & ... &
|
|
\begin{array}{|c} \hline
|
|
0 & 3 & 2 \\
|
|
3 & 1
|
|
\end{array} \\ \\ &&&&&&
|
|
\begin{array}{|c} \hline
|
|
0 & 0 & 1 & 2 \\
|
|
0 & 1 & 0 & 2 \\
|
|
1 & 1 & 1
|
|
\end{array}
|
|
\end{matrix}
|
|
$$
|
|
|
|
The expansion in the *y* column looks trivially correct, since $y^2 = 9$ when $x = 0$.
|
|
We can check the base of the expansion of nine ("2100") by factoring a polynomial:
|
|
|
|
```{python}
|
|
#| code-fold: true
|
|
|
|
nine_x2y3 = 2*x**3 + x**2 - 9
|
|
|
|
display_latex(
|
|
sympy.Eq(nine_x2y3, nine_x2y3.factor())
|
|
)
|
|
```
|
|
|
|
The linear polynomial has 3/2 as a root, while the quadratic one has only complex roots.
|
|
|
|
Naturally, these expansions work symmetrically by considering *y* to have the $n - 1$ coefficient instead of *x*.
|
|
This transposes the carry (and hence the expansion).
|
|
|
|
|
|
### Slanted Counting
|
|
|
|
For good measure, let's see what it looks like when we count in the $n = 3$ and $n = 4$ cases.
|
|
|
|
```{python}
|
|
#| code-fold: true
|
|
#| layout-ncol: 2
|
|
|
|
animate_carry_count(
|
|
carry = Carry([
|
|
[-3, 2],
|
|
[ 1, 0]
|
|
]),
|
|
operation="add",
|
|
op_val=3,
|
|
frames=list(range(200))
|
|
).save("count_x2y3.mp4")
|
|
|
|
animate_carry_count(
|
|
carry = Carry([
|
|
[-4, 3],
|
|
[ 1, 0]
|
|
]),
|
|
operation="add",
|
|
op_val=4,
|
|
frames=list(range(200)),
|
|
).save("count_x3y4.mp4")
|
|
|
|
display(
|
|
Video("count_x2y3.mp4"),
|
|
Video("count_x3y4.mp4")
|
|
)
|
|
```
|
|
|
|
The pattern produced is similar to the one from dibinary.
|
|
However, the triangular pattern no longer grows all at once, and instead, the bottom leads the top.
|
|
Meanwhile, the disordered part in the upper left of the expansion appears to grow larger as *n* increases,
|
|
not to mention that it contains many artifacts from smaller colors.
|
|
|
|
The slope of the pattern is $1 / (n - 1)$, since this is the ratio of growth between *x* and *y*.
|
|
As *n* grows, the diagonal pattern gets thinner and takes longer to show up.
|
|
This is because the size of the alphabet causing expansions to shrink *geometrically*, while the digital
|
|
root is always increasing *arithmetically*.
|
|
The binary alphabet is the smallest possible, so the minimal shrinkage is in dibinary.
|
|
|
|
|
|
### Incremented Curves
|
|
|
|
Below are the incremented curves for $n = 3$.
|
|
These curves show an interesting phenomenon: an extra line is present in in the expansion of six.
|
|
This line turns into a spike at twelve, then at fifteen, it seems to grow to encompass the point at infinity.
|
|
|
|
```{python}
|
|
#| code-fold: true
|
|
#| layout-ncol: 2
|
|
|
|
x2y3 = Carry([
|
|
[-3, 2],
|
|
[ 1, 0]
|
|
])
|
|
|
|
expansion = np.zeros((50, 50), dtype=np.int64)
|
|
last_i = 0
|
|
for i in [3, 6, 12, 15]:
|
|
carry_add(expansion, x2y3, i - last_i)
|
|
plot_implicit(
|
|
poly_from_array(expansion) - i,
|
|
(x, -10, 10),
|
|
(y, -10, 10),
|
|
title=f"{i}",
|
|
)
|
|
last_i = i
|
|
```
|
|
|
|
|
|
Leaves in the Mirror
|
|
--------------------
|
|
|
|
Algebraic curves are not my forte.
|
|
However, I believe it would be a good idea to briefly examine some classical examples.
|
|
|
|
All Fermat curves $x^n + y^n = 1$ are not suitable as carries, since their digital root is unbounded.
|
|
Scaling up the curve, as by $x^n + y^n = 2$ will just degenerate back to the previous examples with lines,
|
|
but spaced out by zeros between entries.
|
|
|
|
The [folium of Descartes](https://en.wikipedia.org/wiki/Folium_of_Descartes) is another
|
|
classical curve that played a role in the development of calculus, described by the equation:
|
|
|
|
$$
|
|
\begin{gather*}
|
|
x^3 + y^3 - 3axy = 0
|
|
\\ \\
|
|
\begin{array}{|c} \hline
|
|
0 & 0 & 0 & 1 \\
|
|
0 & -3a \\
|
|
0 & \\
|
|
1
|
|
\end{array}
|
|
\end{gather*}
|
|
$$
|
|
|
|
The position of the 3*a* term may be confusing.
|
|
As the negative coefficient, we must place it atop the digit we are carrying, meaning that
|
|
expansions will propagate toward negative powers of *x* and *y* as well as positive.
|
|
While the shape of the curve is different from an ordinary line, the coefficients are arranged
|
|
too similarly; for $a = 2/3$ (so the curve passes through the point (1, 1)) incrementing appears as:
|
|
|
|
```{python}
|
|
#| echo: false
|
|
#| layout-ncol: 2
|
|
|
|
folium = Carry([
|
|
[ 0, 0, 0, 1],
|
|
[ 0,-2, 0, 0],
|
|
[ 0, 0, 0, 0],
|
|
[ 1, 0, 0, 0],
|
|
])
|
|
dims = (50, 50)
|
|
center = (dims[0] // 2, dims[1] // 2)
|
|
two_folium = carry_add(np.zeros(dims, dtype=np.int64), folium, 2, center=center)
|
|
four_folium = carry_add(np.zeros(dims, dtype=np.int64), folium, 4, center=center)
|
|
|
|
display_latex(
|
|
Latex(
|
|
r"\begin{gather*}"
|
|
+ "2: &"
|
|
+ latex_polynumber(two_folium, center=center, show_zero=True) + r"\\"
|
|
+ r"\\ \\ 4: &"
|
|
+ latex_polynumber(four_folium, center=center, show_zero=True) + r"\\"
|
|
+ r"\end{gather*}"
|
|
)
|
|
)
|
|
|
|
animate_carry_count(
|
|
carry=folium,
|
|
operation="add",
|
|
op_val=4,
|
|
frames=list(range(200)),
|
|
).save("count_folium.mp4")
|
|
|
|
display(Video("count_folium.mp4"))
|
|
```
|
|
|
|
Clearly, this also tends back toward dibinary, but with the yellow digits spaced out more.
|
|
|
|
|
|
### Discounting Curves
|
|
|
|
The interpretation of the carry as a curve is secondary, so I'd like to disqualify
|
|
some archetypal quadratic objects.
|
|
|
|
- Unit circle ($x^2 + y^2 - 1 = 0$) and hyperbola ($\pm x^2 \mp y^2 - 1 = 0$)
|
|
- The sum of coefficients is positive, which means that expansions are unbounded.
|
|
- Alternatively, coefficients are of an improper form, since one positive coefficient
|
|
does not outweigh all remaining negative coefficients.
|
|
- The curve as a function in *x* and *y* can be written in simpler terms, i.e., $F(x, y) = G(x^2, y^2)$
|
|
- Hyperbola ($xy \pm 1 = 0$)
|
|
- The curve is a function of only *xy*, i.e., $F(x, y) = P(xy)$,
|
|
so the curve degenerates to one-dimensional unary.
|
|
- Parabolae ($y = \pm x^2,~ x = \pm y^2$)
|
|
- One coefficient does not outweigh the other.
|
|
- The system degenerates to a single dimeension due to the fact that either *y* is a polynomial in *x* or vice versa
|
|
|
|
Other polynomials in *x* and *y* may produce rotations or dilations of these curves,
|
|
but the key fact is that this interpretation is secondary.
|
|
What truly matters is that one cell affects relatively close cells in a certain way.
|
|
|
|
|
|
Laplace's Sandstorm
|
|
-------------------
|
|
|
|
As stated previously, polynomials naturally multiply by convolution.
|
|
In this context, a form which frequently arises is an approximation of the 2D Laplacian.
|
|
While commonly presented as a matrix, it has very little to do with the machinery of
|
|
linear algebra which normally empowers matrices.
|
|
|
|
::: {layout-ncol="2"}
|
|
$$
|
|
\begin{gather*}
|
|
\begin{bmatrix}
|
|
0 & 1 & 0 \\
|
|
1 & -4 & 1 \\
|
|
0 & 1 & 0
|
|
\end{bmatrix} ~\implies~
|
|
\begin{array}{c|cc}
|
|
& 1 & \\ \hline
|
|
1 & -4 & 1 \\
|
|
& 1
|
|
\end{array} \\ \\
|
|
x + x^{-1} + y + y^{-1} - 4 = 0 \\
|
|
x + xy^2 + y + yx^2 - 4xy = 0
|
|
\end{gather*}
|
|
$$
|
|
|
|
```{python}
|
|
#| echo: false
|
|
|
|
plot_implicit(x + y + x*y**2 + y*x**2 - 4*x*y, (x, -10, 10), (y, -10, 10))
|
|
```
|
|
:::
|
|
|
|
From the above, it is clear that this is a cubic curve.
|
|
Aside from the trivial solution $x = y = 0$ (which obviously also satisfies *x* = *y*),
|
|
the curve made by the carry does not intersect either axis.
|
|
There is an additional isolated point at (1, 1), implying some is in unary-ness this system.
|
|
If we make it a carry, then the first few powers of 4 are
|
|
|
|
$$
|
|
\begin{matrix}
|
|
1 & ... & 4 & ... & 16 \\
|
|
\begin{array}{|c} \hline
|
|
1
|
|
\end{array} & ... &
|
|
\begin{array}{c|cc}
|
|
0 & 1 & 0 \\ \hline
|
|
1 & 0 & 1 \\
|
|
0 & 1 &
|
|
\end{array} & ... &
|
|
\begin{array}{c|cc}
|
|
0 & 4 & 0 \\ \hline
|
|
4 & 0 & 4 \\
|
|
0 & 4 &
|
|
\end{array} \\ \\ &&&&
|
|
\begin{array}{cc|cc}
|
|
& & 1 \\
|
|
& 2 & 0 & 2 \\ \hline
|
|
1 & 0 & 4 & 0 & 1 \\
|
|
& 2 & 0 & 2 \\
|
|
& & 1
|
|
\end{array} \\ \\ &&&&
|
|
\begin{array}{cc|cc}
|
|
& & 1 \\
|
|
& 2 & 1 & 2 \\ \hline
|
|
1 & 1 & 0 & 1 & 1 \\
|
|
& 2 & 1 & 2 \\
|
|
& & 1
|
|
\end{array}
|
|
\end{matrix}
|
|
$$
|
|
|
|
As the negative (and largest) term, we carry at the "4" entry, as shown above.
|
|
Terms progress along lower and higher powers of *x* and *y*.
|
|
However, rather than just rightward propagation interfering with downward propagation (and vice versa),
|
|
all directions can interfere with each other.
|
|
|
|
This system can be interpreted more naturally as a
|
|
[sandpile](https://en.wikipedia.org/wiki/Abelian_sandpile_model).
|
|
As a cellular automaton, it is rather popular as a coding challenge
|
|
([video by The Coding Train](https://www.youtube.com/watch?v=diGjw5tghYU)),
|
|
and when the grid is finite, certain subsets of all possible elements form finite groups under addition
|
|
([video by Numberphile](https://www.youtube.com/watch?v=1MtEUErz7Gg)).
|
|
These videos discuss toppling the initial value, but do not point out the analogy to polynomial expansions.
|
|
|
|
```{python}
|
|
#| code-fold: true
|
|
#| layout-ncol: 2
|
|
|
|
animate_carry_count(
|
|
carry = Carry([
|
|
[ 0, 1, 0],
|
|
[ 1,-4, 1],
|
|
[ 0, 1, 0],
|
|
]),
|
|
operation="add",
|
|
op_val=4,
|
|
frames=list(range(200)),
|
|
).save("count_laplace.mp4")
|
|
|
|
Video("count_laplace.mp4")
|
|
```
|
|
|
|
The shapes produced by this pattern are well-known, with larger numbers appearing as fractals.
|
|
Generally, the expansions are enclosed within a circle, with shrinking triangular looking sections.
|
|
This is eerily similar to the same shrinking triangles which appear in other unary-like carries.
|
|
|
|
|
|
Closing
|
|
-------
|
|
|
|
All of the 2D carries presented here are explicit carries.
|
|
Unlike phinary, they have no additional rules hidden in mutliples of the carry.
|
|
It seems to be the case that implicit carries in two variables can be found by
|
|
selecting a cyclotomic polynomial (in *x*, *y*, and $x + y$)
|
|
and computing an explicit carry by multiplying it with another polynomial.
|
|
Some of my investigations into this topic are available [here](../cell2).
|
|
|
|
I should also mention that my notational inspiration for arranging digits things in
|
|
a 2D array is Norman Wildberger's variable-free approach to
|
|
"[bipolynumbers](https://www.youtube.com/watch?v=_9FvrO1JTTI)", a term he uses
|
|
to refer to polynomials of two variables.
|
|
|
|
Just as learning to count on polynomials in a single variable opens up a world of possibilities,
|
|
learning to count on those in two variables is a perplexing topic that is worth exploring.
|