ndplex 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
4
+ build/
5
+ dist/
6
+ *.egg-info/
@@ -0,0 +1,58 @@
1
+ ## Attribution & References
2
+
3
+ ### Provenance
4
+
5
+ This repository contains work of three kinds. They are kept apart so that credit, and responsibility for errors, lands in the right place.
6
+
7
+ **A. Original work: Ted Teske (2019–2024), sole author**
8
+
9
+ - The n-Dimensional m-Plex ("ndplex") array: a generic Pascal's array of any dimension, as an `np.ndarray` subclass with user-defined edges, node-generating function (`func`) and argument locations (`arg_locs`, including the `None`-means-on-the-edge convention).
10
+ - `diag`, the general line or cross-section through the array (loc, one or more moves, optional sums), and `as_tri`, the array as a triangle, tetrahedron or n-simplex.
11
+ - `oeis` (row-by-row OEIS lookup), `diag_as_hist` (weighted histograms with a normal overlay) and `plot_ndplex` (the array or triangle with highlighted diagonals).
12
+ - All of `ndplex_array.ipynb`: the examples and the observations in them, including the simplex/duplex/triplex triangles, the binomial-to-normal histograms, Fibonacci and Narayana's cows from steep diagonals, the slope table, the moving-cost and rental-rate model, and the explorations with primes.
13
+
14
+ **B. Collaborative work: Ted Teske with Claude (Anthropic), 2026**
15
+
16
+ - *Phase 1 audit* (`docs/phase1_audit.md`): running the notebook against current libraries; the findings on import side effects, the ndarray subclass, integer overflow (where it starts, why residues modulo powers of 2 survive it, and that part of the Fibonacci-mod-7 heatmap is overflow), arg_locs that wrap around the array, diag trimming genuine zeros, and oeis hiding network errors; the proposed 0.1.0 API.
17
+ - *Phase 1 regression suite* (`tests/`): the notebook's printed results pinned as tests, the property checks (binomial, multinomial, Lucas's theorem, digital roots), the known-issue tests, the offline OEIS stub and primes sieve, and the mutation check (`tests/mutation_check.py`).
18
+ - *Phase 2 package* (`src/ndplex`, `pyproject.toml`, `docs/phase2_package.md`): the package layout and lazy loading of `oeis`; the fixes for the Phase 1 findings (no import side effects, a single overflow warning naming the first overflowed node, exact `dtype=object` sums, derived arrays that keep working and pickle, zero padding for `arg_locs` outside the array, `diag` ending by geometry rather than at the first trailing zeros, argument checks, and `oeis` over https with failures reported and a `quiet` mode). The class's design, its node-generation loop, the edge-filling rotation, the `diag` move arithmetic and `as_tri` as a series of cross-sections (with its 2-D and 3-D moves) are Ted's, kept from the notebook (tier A). The `as_tri` moves for 4-D and up were changed in review so that layers are left-justified in every dimension (Ted's choice between the two layouts; the finding and the new move rule were made with Claude).
19
+ - *Phase 2 tests*: the suite moved to the package, the known-issue tests rewritten to the fixed behaviour, the new package, overflow, pickling and `oeis` tests, the differential check against the notebook code, and the mutation check adapted to the package.
20
+ - *Phase 3 tests and CI* (`.github/workflows/tests.yml`, `tests/test_platform.py`, `docs/phase3_ci.md`): the CI matrix (adapted from Ted's bounded-lognorm), the finding that overflow in narrow integer dtypes went unreported on NumPy < 2 and its fix (C12), and the platform tests.
21
+ - *Phase 4 packaging and release* (`.github/workflows/publish.yml`, `README.md`, `CITATION.cff`, `tests/test_readme.py`, `docs/phase4_release.md`): the publish workflow (adapted from Ted's bounded-lognorm), the README rewritten as the package's documentation with its examples run as tests, the citation metadata, and the release procedure. The choice of Green (2015) as a general source, the Zenodo and CITATION decisions, and the release itself are Ted's.
22
+
23
+ **C. Third-party work, cited below**
24
+
25
+ Attribution suggestion, not a rule: the usual convention in published work is that an AI system is not listed as an author, since authorship carries accountability an AI cannot hold. The conventional form is to claim the work as your own and acknowledge AI assistance, e.g. *"The test suite and audit were developed with assistance from Claude (Anthropic)."*
26
+
27
+ ### References
28
+
29
+ **General source.** Ted cites Thomas M. Green's *The Simplex, Duplex and Pascal's Triangles* (2015; reference 10 below) as a general source for ndplex. The book covers Pascal's triangle and its relatives, simplexes, duplexes and higher-dimensional extensions, and recurrent sequences such as Fibonacci.
30
+
31
+ **Code adapted in the notebook's library cell (not in the package)**
32
+
33
+ 1. `trim_trailing_zeros` (inside `diag`) is adapted from an answer to "Trim trailing zeros of a numpy array along each axis", Stack Overflow question 55917328. https://stackoverflow.com/questions/55917328
34
+ 2. The query inside `oeis` is adapted from Andrew Walker's `oeis` package. https://github.com/AndrewWalker/oeis — its `setup.py` declares a BSD license; the repository has no LICENSE file (checked November 2026).
35
+
36
+ *Both are kept here as history. The package does not contain either (decision D9): `diag` now ends where its moves leave the array (`_trim_outside` in `src/ndplex/_core.py`, written from scratch for the change in behaviour) and `oeis`'s request is written from scratch with the standard library. The notebook (`ndplex_array.ipynb`) still contains the originals.*
37
+
38
+ **Code and data used only in the notebook's examples**
39
+
40
+ 3. `sum_digits`, from Andreas K.'s answer to Stack Overflow question 48817036. https://stackoverflow.com/questions/48817036
41
+ 4. `primesfrom2to`, from an answer to Stack Overflow question 2068372. https://stackoverflow.com/questions/2068372
42
+ 5. The first 1000 primes, downloaded from a GitHub gist by cblanc. https://gist.github.com/cblanc/46ebbba6f42f61e60666
43
+
44
+ **Mathematics and sources cited in the notebook**
45
+
46
+ 6. The On-Line Encyclopedia of Integer Sequences (OEIS). https://oeis.org — the source of the sequence names `oeis()` prints; its text output format is described at https://oeis.org/eishelp1.html.
47
+ 7. Pascal's triangle and the bell curve. AMS Feature Column. http://www.ams.org/publicoutreach/feature-column/fcarc-normal
48
+ 8. Pascal's triangle modulo n (Sierpinski's gasket). http://orion.math.iastate.edu/reu/oldREU/modupasc.htm
49
+ 9. Hypercube; Simplex. Wikipedia. https://en.wikipedia.org/wiki/Hypercube, https://en.wikipedia.org/wiki/Simplex
50
+ 10. Green, Thomas M. *The Simplex, Duplex and Pascal's Triangles: Relatives of Pascal's Triangle, with Excursions Into Hyperspace*. CreateSpace Independent Publishing Platform, 2015. 176 pp. ISBN 9781514677094. https://books.google.com/books/about/The_Simplex_Duplex_and_Pascal_s_Triangle.html?id=P0cDswEACAAJ (also cited in the notebook, via https://www.amazon.com/Simplex-Duplex-Pascals-Triangles-Excursions/dp/1514677091). **A general source for the project** (see the note above), not only for the notebook section that cites it.
51
+ 11. Narayana's cows sequence, OEIS A000930 (https://oeis.org/A000930), and the paper linked from the notebook (JIM 96 proceedings). http://jim.afim-asso.org/jim96/actes/Allouche.ps
52
+
53
+ **Used in the Phase 1 tests**
54
+
55
+ 12. Lucas's theorem (a binomial coefficient modulo a prime, from base-p digits); for p = 2 it gives: C(i + j, i) is odd exactly when i AND j = 0. https://en.wikipedia.org/wiki/Lucas%27s_theorem
56
+ 13. The multinomial theorem: the nodes of layer r of the d-dimensional default ndplex sum to d^r.
57
+
58
+ *Links in the notebook have not yet been checked; a link audit is planned with the examples notebook (Phase 5).*
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The project follows [Semantic Versioning](https://semver.org/); while the version is 0.x, minor releases may change the API.
4
+
5
+ ## 0.1.0 (2026-11-06)
6
+
7
+ First release, and the first version as a package. Before this, ndplex was the first code cell of `ndplex_array.ipynb` (Ted Teske, 2019–2024). The notebook's results are kept except where a change is listed below; each change is documented, with migration notes, in `docs/phase2_package.md`, `docs/phase3_ci.md` and `docs/phase4_release.md`.
8
+
9
+ - **Package layout.** `src/ndplex`: the `ndplex` class (`_core.py`) and `oeis` (`_oeis.py`). `from ndplex import ndplex, oeis` works as before. `import ndplex` needs only NumPy (≥ 1.23.5); `oeis` uses the standard library and is loaded on first use.
10
+ - **Plotting is left out.** `plot_ndplex` and `diag_as_hist` are not in the package; importing them raises an `ImportError` that points to the notebook in the repository, which has the original versions (Phase 4, C13; an examples notebook with working copies is planned).
11
+ - **No import side effects.** Importing no longer silences warnings or restyles matplotlib.
12
+ - **Overflow is reported.** If any node overflows the dtype, construction issues one `RuntimeWarning` naming the first node that did. Values are unchanged. `dtype=object` is exact, now including sums in `diag` and `as_tri` past 2^63.
13
+ - **Overflow is reported for every integer dtype, on every NumPy version** (Phase 3, `docs/phase3_ci.md`, C12). On NumPy < 2, overflow in int8, int16, int32 and unsigned arrays went unreported. An integer from `func` that does not fit the dtype is now wrapped and reported, where it used to wrap silently (NumPy < 2) or raise `OverflowError` (NumPy 2, or a Python int beyond int64).
14
+ - **CI.** Tests run on Linux (Python 3.10–3.15), macOS and Windows, on NumPy 1.23.5 and the latest NumPy.
15
+ - **Derived arrays keep working.** `diag` works on slices, arithmetic results, copies and unpickled arrays. Reductions (`sum`, `max`, ...) return plain scalars. Pickling keeps `edges`, `func` and `arg_locs` (a `func` that cannot be pickled comes back as `None`).
16
+ - **`arg_locs` outside the array read 0** instead of wrapping around to the far side. Positive offsets raise `ValueError`. The default is `arg_locs=None`.
17
+ - **`diag` ends where the moves leave the array**, so genuine trailing zeros are kept and all-zero lines return zeros instead of raising `IndexError`.
18
+ - **`as_tri` layers are left-justified in every dimension.** 2-D and 3-D are unchanged; from 4-D up the notebook's layers were sheared. Values and sums are unchanged.
19
+ - **Arguments.** NumPy integer `edges` accepted; bad arguments raise `ValueError` with a message; the default dtype is `np.int64` on every platform; the default `func` is the builtin `sum`.
20
+ - **`oeis`.** https and a timeout; requests name ndplex in their User-Agent header, because oeis.org now refuses Python's default one (C14); network or format failures print `lookup failed (...)` instead of `no match`; `quiet=True` returns the results instead of printing.
21
+ - **Release.** Published to PyPI with Trusted Publishing (`.github/workflows/publish.yml`). The README is now the package's own documentation, and its examples run as tests. `CITATION.cff` added.
@@ -0,0 +1,42 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it using the metadata from this file."
3
+ type: software
4
+ title: "ndplex: the n-Dimensional m-Plex array, a generic Pascal's array for NumPy"
5
+ abstract: >-
6
+ A generic Pascal's array of any dimension, as a NumPy array subclass: by
7
+ default Pascal's rectangle, cube or hypercube, with user-defined edges,
8
+ node-generating function and argument locations. Includes lines and
9
+ cross-sections through the array (diag), the array as a triangle,
10
+ tetrahedron or n-simplex (as_tri), and row-by-row lookup in the On-Line
11
+ Encyclopedia of Integer Sequences (oeis).
12
+ authors:
13
+ - family-names: Teske
14
+ given-names: Ted
15
+ version: 0.1.0
16
+ license: MIT
17
+ # The Zenodo concept DOI and the version DOIs are added after the first
18
+ # Zenodo-archived release (0.1.0).
19
+ repository-code: "https://github.com/tedteske/ndplex"
20
+ url: "https://pypi.org/project/ndplex/"
21
+ keywords:
22
+ - Pascal's triangle
23
+ - Pascal's array
24
+ - combinatorics
25
+ - binomial coefficients
26
+ - multinomial coefficients
27
+ - simplex
28
+ - integer sequences
29
+ - OEIS
30
+ - NumPy
31
+ references:
32
+ - type: book
33
+ title: "The Simplex, Duplex and Pascal's Triangles: Relatives of Pascal's Triangle, with Excursions Into Hyperspace"
34
+ authors:
35
+ - family-names: Green
36
+ given-names: Thomas M.
37
+ year: 2015
38
+ publisher:
39
+ name: CreateSpace Independent Publishing Platform
40
+ isbn: "9781514677094"
41
+ pages: 176
42
+ notes: "A general source for ndplex."
ndplex-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Ted Teske
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ndplex-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: ndplex
3
+ Version: 0.1.0
4
+ Summary: The n-Dimensional m-Plex array: a generic Pascal's array (rectangle, cube or higher) with user-defined edges and node-generating function.
5
+ Project-URL: Homepage, https://github.com/tedteske/ndplex
6
+ Project-URL: Source, https://github.com/tedteske/ndplex
7
+ Project-URL: Issues, https://github.com/tedteske/ndplex/issues
8
+ Project-URL: Changelog, https://github.com/tedteske/ndplex/blob/master/CHANGELOG.md
9
+ Author: Ted Teske
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: binomial,combinatorics,integer sequences,multinomial,numpy,oeis,pascal's array,pascal's triangle,simplex
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Programming Language :: Python :: 3.15
25
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
26
+ Requires-Python: >=3.10
27
+ Requires-Dist: numpy>=1.23.5
28
+ Description-Content-Type: text/markdown
29
+
30
+ # ndplex
31
+
32
+ **The n-Dimensional m-Plex array: a generic Pascal's array for NumPy.**
33
+
34
+ `ndplex` builds Pascal's rectangle, cube or hypercube as a NumPy array, and lets you change everything that defines it: the values on its edges, the rule that generates each interior node, and which neighbours that rule reads. It can then cut lines and cross-sections through the array, show the array as a triangle, tetrahedron or n-simplex, and look its rows up in the [On-Line Encyclopedia of Integer Sequences](https://oeis.org).
35
+
36
+ ## Install
37
+
38
+ ```
39
+ pip install ndplex
40
+ ```
41
+
42
+ In Google Colab, run `!pip install ndplex` in a cell. ndplex needs Python 3.10 or later and NumPy 1.23.5 or later, nothing else. `oeis` needs a network connection when it is called.
43
+
44
+ ## Quick start
45
+
46
+ By default, `ndplex(n)` is Pascal's rectangle: ones on the top and left edges, and every other node the sum of the node above it and the node to its left.
47
+
48
+ ```pycon
49
+ >>> from ndplex import ndplex
50
+ >>> p = ndplex(6)
51
+ >>> print(p)
52
+ [[ 1 1 1 1 1 1]
53
+ [ 1 2 3 4 5 6]
54
+ [ 1 3 6 10 15 21]
55
+ [ 1 4 10 20 35 56]
56
+ [ 1 5 15 35 70 126]
57
+ [ 1 6 21 56 126 252]]
58
+ ```
59
+
60
+ `as_tri` shows the same array as Pascal's triangle, justified left, or returns the sum of each row.
61
+
62
+ ```pycon
63
+ >>> print(p.as_tri())
64
+ [[ 1 0 0 0 0 0]
65
+ [ 1 1 0 0 0 0]
66
+ [ 1 2 1 0 0 0]
67
+ [ 1 3 3 1 0 0]
68
+ [ 1 4 6 4 1 0]
69
+ [ 1 5 10 10 5 1]]
70
+ >>> print(p.as_tri(sums=True))
71
+ [ 1 2 4 8 16 32]
72
+ ```
73
+
74
+ `diag` walks through the array from a starting node by a fixed step. Row 4 of the triangle is an anti-diagonal of the rectangle; the main diagonal holds the central binomial coefficients.
75
+
76
+ ```pycon
77
+ >>> print(p.diag(loc=(4, 0), move=(-1, 1)))
78
+ [1 4 6 4 1]
79
+ >>> print(p.diag())
80
+ [ 1 2 6 20 70 252]
81
+ ```
82
+
83
+ The steeper diagonals sum to the Fibonacci numbers:
84
+
85
+ ```pycon
86
+ >>> f = ndplex(10)
87
+ >>> print([int(f.diag(loc=(k, 0), move=(-2, 1)).sum()) for k in range(10)])
88
+ [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
89
+ ```
90
+
91
+ ## Other arrays
92
+
93
+ `ndplex(nrows, edges=(1, 1), arg_locs=None, func=sum, dtype=np.int64)`
94
+
95
+ - **`edges`** sets the front edges, as numbers or as sequences of length `nrows`. Their number sets the dimension. In 2-D, `edges[0]` fills column 0 and `edges[1]` fills row 0.
96
+ - **`func`** generates each interior node from a list of argument values. The default is `sum`.
97
+ - **`arg_locs`** gives where those arguments are, relative to the node. The default in 2-D is `[[-1, 0], [0, -1]]`, the nodes above and to the left. A `None` coordinate means "on the edge".
98
+
99
+ Odd numbers along the top edge give the squares, the square pyramidal numbers, and so on down the rows:
100
+
101
+ ```pycon
102
+ >>> print(ndplex(5, edges=(1, [1, 3, 5, 7, 9])))
103
+ [[ 1 3 5 7 9]
104
+ [ 1 4 9 16 25]
105
+ [ 1 5 14 30 55]
106
+ [ 1 6 20 50 105]
107
+ [ 1 7 27 77 182]]
108
+ ```
109
+
110
+ Pascal's rule modulo 2 gives the Sierpinski pattern:
111
+
112
+ ```pycon
113
+ >>> print(ndplex(6, func=lambda x: sum(x) % 2))
114
+ [[1 1 1 1 1 1]
115
+ [1 0 1 0 1 0]
116
+ [1 1 0 0 1 1]
117
+ [1 0 0 0 1 0]
118
+ [1 1 1 1 0 0]
119
+ [1 0 1 0 0 0]]
120
+ ```
121
+
122
+ Adding the diagonal neighbour as a third argument gives the Delannoy numbers:
123
+
124
+ ```pycon
125
+ >>> print(ndplex(6, arg_locs=[[-1, 0], [0, -1], [-1, -1]]))
126
+ [[ 1 1 1 1 1 1]
127
+ [ 1 3 5 7 9 11]
128
+ [ 1 5 13 25 41 61]
129
+ [ 1 7 25 63 129 231]
130
+ [ 1 9 41 129 321 681]
131
+ [ 1 11 61 231 681 1683]]
132
+ ```
133
+
134
+ ## Higher dimensions
135
+
136
+ Three edges make Pascal's cube; `as_tri` shows it as a tetrahedron, layer by layer. Each layer holds the trinomial coefficients, and the layers sum to powers of 3.
137
+
138
+ ```pycon
139
+ >>> c = ndplex(4, edges=3)
140
+ >>> print(c.as_tri()[2])
141
+ [[1 2 1 0]
142
+ [2 2 0 0]
143
+ [1 0 0 0]
144
+ [0 0 0 0]]
145
+ >>> print(c.as_tri(sums=True))
146
+ [ 1 3 9 27]
147
+ ```
148
+
149
+ The same works in any number of dimensions: `ndplex(5, edges=4)` is a 4-D array, and its `as_tri` is a 4-simplex.
150
+
151
+ ## Large values
152
+
153
+ Node values grow quickly. With the default `int64`, the 2-D array is exact for nodes with `i + j <= 66`. If any node overflows, construction issues one `RuntimeWarning` naming the first node that did. For exact integers of any size, use `dtype=object`:
154
+
155
+ ```pycon
156
+ >>> print(ndplex(70, dtype=object)[69, 69])
157
+ 23623985175715118288974865541854103729000
158
+ ```
159
+
160
+ When only a residue is needed, reduce inside `func` instead, as in the Sierpinski example above.
161
+
162
+ ## Looking rows up in OEIS
163
+
164
+ `oeis` searches oeis.org for each row of an array and prints the best match. It sends one request per row, so please search a few rows at a time.
165
+
166
+ ```python
167
+ from ndplex import ndplex, oeis
168
+
169
+ oeis(ndplex(5))
170
+ oeis(ndplex(9).as_tri(), trim=True, size=5) # rows of the triangle, zeros trimmed
171
+ matches = oeis(ndplex(5), quiet=True) # a list of (A-number, name) or None
172
+ ```
173
+
174
+ The output has one line per row, for example `row 2: https://oeis.org/A000217 Triangular numbers: ...`. The names come from OEIS and can change. If oeis.org cannot be reached or refuses the request, the row's line says `lookup failed (...)`. Each request identifies itself to oeis.org as `ndplex/<version>`.
175
+
176
+ ## Plotting
177
+
178
+ From 0.1.0, plotting is left to the user, so `import ndplex` needs only NumPy. The notebook's `plot_ndplex` and `diag_as_hist` are in [`ndplex_array.ipynb`](https://github.com/tedteske/ndplex/blob/master/ndplex_array.ipynb) in the repository; an examples notebook that installs from PyPI is planned.
179
+
180
+ ## Changes from the notebook
181
+
182
+ ndplex began as the first code cell of `ndplex_array.ipynb` (2019–2024). The package keeps the notebook's results except where a change is documented. The changes are listed in the [changelog](https://github.com/tedteske/ndplex/blob/master/CHANGELOG.md), with migration notes in [`docs/`](https://github.com/tedteske/ndplex/tree/master/docs). In brief: no import side effects, plotting left out, overflow reported, `arg_locs` outside the array read 0, `diag` ends where its moves leave the array, and `as_tri` layers are justified left in every dimension.
183
+
184
+ ## Background and credits
185
+
186
+ ndplex is the original work of Ted Teske (2019–2024). A general source for it is Thomas M. Green, *The Simplex, Duplex and Pascal's Triangles: Relatives of Pascal's Triangle, with Excursions Into Hyperspace* (CreateSpace, 2015; ISBN 9781514677094).
187
+
188
+ The packaging, tests and audit (2026) were developed with assistance from Claude (Anthropic). [`ATTRIBUTION.md`](https://github.com/tedteske/ndplex/blob/master/ATTRIBUTION.md) sets out which work is whose, and the references.
189
+
190
+ ## Citing
191
+
192
+ Citation metadata are in [`CITATION.cff`](https://github.com/tedteske/ndplex/blob/master/CITATION.cff); GitHub's "Cite this repository" button reads it.
193
+
194
+ ## License
195
+
196
+ MIT. Copyright (c) 2019 Ted Teske.
ndplex-0.1.0/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # ndplex
2
+
3
+ **The n-Dimensional m-Plex array: a generic Pascal's array for NumPy.**
4
+
5
+ `ndplex` builds Pascal's rectangle, cube or hypercube as a NumPy array, and lets you change everything that defines it: the values on its edges, the rule that generates each interior node, and which neighbours that rule reads. It can then cut lines and cross-sections through the array, show the array as a triangle, tetrahedron or n-simplex, and look its rows up in the [On-Line Encyclopedia of Integer Sequences](https://oeis.org).
6
+
7
+ ## Install
8
+
9
+ ```
10
+ pip install ndplex
11
+ ```
12
+
13
+ In Google Colab, run `!pip install ndplex` in a cell. ndplex needs Python 3.10 or later and NumPy 1.23.5 or later, nothing else. `oeis` needs a network connection when it is called.
14
+
15
+ ## Quick start
16
+
17
+ By default, `ndplex(n)` is Pascal's rectangle: ones on the top and left edges, and every other node the sum of the node above it and the node to its left.
18
+
19
+ ```pycon
20
+ >>> from ndplex import ndplex
21
+ >>> p = ndplex(6)
22
+ >>> print(p)
23
+ [[ 1 1 1 1 1 1]
24
+ [ 1 2 3 4 5 6]
25
+ [ 1 3 6 10 15 21]
26
+ [ 1 4 10 20 35 56]
27
+ [ 1 5 15 35 70 126]
28
+ [ 1 6 21 56 126 252]]
29
+ ```
30
+
31
+ `as_tri` shows the same array as Pascal's triangle, justified left, or returns the sum of each row.
32
+
33
+ ```pycon
34
+ >>> print(p.as_tri())
35
+ [[ 1 0 0 0 0 0]
36
+ [ 1 1 0 0 0 0]
37
+ [ 1 2 1 0 0 0]
38
+ [ 1 3 3 1 0 0]
39
+ [ 1 4 6 4 1 0]
40
+ [ 1 5 10 10 5 1]]
41
+ >>> print(p.as_tri(sums=True))
42
+ [ 1 2 4 8 16 32]
43
+ ```
44
+
45
+ `diag` walks through the array from a starting node by a fixed step. Row 4 of the triangle is an anti-diagonal of the rectangle; the main diagonal holds the central binomial coefficients.
46
+
47
+ ```pycon
48
+ >>> print(p.diag(loc=(4, 0), move=(-1, 1)))
49
+ [1 4 6 4 1]
50
+ >>> print(p.diag())
51
+ [ 1 2 6 20 70 252]
52
+ ```
53
+
54
+ The steeper diagonals sum to the Fibonacci numbers:
55
+
56
+ ```pycon
57
+ >>> f = ndplex(10)
58
+ >>> print([int(f.diag(loc=(k, 0), move=(-2, 1)).sum()) for k in range(10)])
59
+ [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
60
+ ```
61
+
62
+ ## Other arrays
63
+
64
+ `ndplex(nrows, edges=(1, 1), arg_locs=None, func=sum, dtype=np.int64)`
65
+
66
+ - **`edges`** sets the front edges, as numbers or as sequences of length `nrows`. Their number sets the dimension. In 2-D, `edges[0]` fills column 0 and `edges[1]` fills row 0.
67
+ - **`func`** generates each interior node from a list of argument values. The default is `sum`.
68
+ - **`arg_locs`** gives where those arguments are, relative to the node. The default in 2-D is `[[-1, 0], [0, -1]]`, the nodes above and to the left. A `None` coordinate means "on the edge".
69
+
70
+ Odd numbers along the top edge give the squares, the square pyramidal numbers, and so on down the rows:
71
+
72
+ ```pycon
73
+ >>> print(ndplex(5, edges=(1, [1, 3, 5, 7, 9])))
74
+ [[ 1 3 5 7 9]
75
+ [ 1 4 9 16 25]
76
+ [ 1 5 14 30 55]
77
+ [ 1 6 20 50 105]
78
+ [ 1 7 27 77 182]]
79
+ ```
80
+
81
+ Pascal's rule modulo 2 gives the Sierpinski pattern:
82
+
83
+ ```pycon
84
+ >>> print(ndplex(6, func=lambda x: sum(x) % 2))
85
+ [[1 1 1 1 1 1]
86
+ [1 0 1 0 1 0]
87
+ [1 1 0 0 1 1]
88
+ [1 0 0 0 1 0]
89
+ [1 1 1 1 0 0]
90
+ [1 0 1 0 0 0]]
91
+ ```
92
+
93
+ Adding the diagonal neighbour as a third argument gives the Delannoy numbers:
94
+
95
+ ```pycon
96
+ >>> print(ndplex(6, arg_locs=[[-1, 0], [0, -1], [-1, -1]]))
97
+ [[ 1 1 1 1 1 1]
98
+ [ 1 3 5 7 9 11]
99
+ [ 1 5 13 25 41 61]
100
+ [ 1 7 25 63 129 231]
101
+ [ 1 9 41 129 321 681]
102
+ [ 1 11 61 231 681 1683]]
103
+ ```
104
+
105
+ ## Higher dimensions
106
+
107
+ Three edges make Pascal's cube; `as_tri` shows it as a tetrahedron, layer by layer. Each layer holds the trinomial coefficients, and the layers sum to powers of 3.
108
+
109
+ ```pycon
110
+ >>> c = ndplex(4, edges=3)
111
+ >>> print(c.as_tri()[2])
112
+ [[1 2 1 0]
113
+ [2 2 0 0]
114
+ [1 0 0 0]
115
+ [0 0 0 0]]
116
+ >>> print(c.as_tri(sums=True))
117
+ [ 1 3 9 27]
118
+ ```
119
+
120
+ The same works in any number of dimensions: `ndplex(5, edges=4)` is a 4-D array, and its `as_tri` is a 4-simplex.
121
+
122
+ ## Large values
123
+
124
+ Node values grow quickly. With the default `int64`, the 2-D array is exact for nodes with `i + j <= 66`. If any node overflows, construction issues one `RuntimeWarning` naming the first node that did. For exact integers of any size, use `dtype=object`:
125
+
126
+ ```pycon
127
+ >>> print(ndplex(70, dtype=object)[69, 69])
128
+ 23623985175715118288974865541854103729000
129
+ ```
130
+
131
+ When only a residue is needed, reduce inside `func` instead, as in the Sierpinski example above.
132
+
133
+ ## Looking rows up in OEIS
134
+
135
+ `oeis` searches oeis.org for each row of an array and prints the best match. It sends one request per row, so please search a few rows at a time.
136
+
137
+ ```python
138
+ from ndplex import ndplex, oeis
139
+
140
+ oeis(ndplex(5))
141
+ oeis(ndplex(9).as_tri(), trim=True, size=5) # rows of the triangle, zeros trimmed
142
+ matches = oeis(ndplex(5), quiet=True) # a list of (A-number, name) or None
143
+ ```
144
+
145
+ The output has one line per row, for example `row 2: https://oeis.org/A000217 Triangular numbers: ...`. The names come from OEIS and can change. If oeis.org cannot be reached or refuses the request, the row's line says `lookup failed (...)`. Each request identifies itself to oeis.org as `ndplex/<version>`.
146
+
147
+ ## Plotting
148
+
149
+ From 0.1.0, plotting is left to the user, so `import ndplex` needs only NumPy. The notebook's `plot_ndplex` and `diag_as_hist` are in [`ndplex_array.ipynb`](https://github.com/tedteske/ndplex/blob/master/ndplex_array.ipynb) in the repository; an examples notebook that installs from PyPI is planned.
150
+
151
+ ## Changes from the notebook
152
+
153
+ ndplex began as the first code cell of `ndplex_array.ipynb` (2019–2024). The package keeps the notebook's results except where a change is documented. The changes are listed in the [changelog](https://github.com/tedteske/ndplex/blob/master/CHANGELOG.md), with migration notes in [`docs/`](https://github.com/tedteske/ndplex/tree/master/docs). In brief: no import side effects, plotting left out, overflow reported, `arg_locs` outside the array read 0, `diag` ends where its moves leave the array, and `as_tri` layers are justified left in every dimension.
154
+
155
+ ## Background and credits
156
+
157
+ ndplex is the original work of Ted Teske (2019–2024). A general source for it is Thomas M. Green, *The Simplex, Duplex and Pascal's Triangles: Relatives of Pascal's Triangle, with Excursions Into Hyperspace* (CreateSpace, 2015; ISBN 9781514677094).
158
+
159
+ The packaging, tests and audit (2026) were developed with assistance from Claude (Anthropic). [`ATTRIBUTION.md`](https://github.com/tedteske/ndplex/blob/master/ATTRIBUTION.md) sets out which work is whose, and the references.
160
+
161
+ ## Citing
162
+
163
+ Citation metadata are in [`CITATION.cff`](https://github.com/tedteske/ndplex/blob/master/CITATION.cff); GitHub's "Cite this repository" button reads it.
164
+
165
+ ## License
166
+
167
+ MIT. Copyright (c) 2019 Ted Teske.