Skip to content

Commit c5dfe07

Browse files
feat: add main feat (#1)
* feat: wip * feat: wip * feat: wip * feat: wip * feat: wip * feat: wip * feat: wip * feat: wip * feat: wip * feat: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore(pre-commit.ci): auto fixes * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: wip * chore: update skills * chore: update skills * chore: update skills * chore: update skills * chore: update skills * test: remove torch * feat: add simpler test * feat: add simpler test * feat: add simpler test * feat: add simpler test * feat: add simpler test * feat: add simpler test * feat: add simpler test * feat: add simpler test * ci: update ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 93567d1 commit c5dfe07

26 files changed

Lines changed: 3129 additions & 357 deletions

.agents/skills/array-api/SKILL.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
---
2+
name: array-api
3+
description: Conventions that MUST be followed when implementing array API compatible functions and their tests.
4+
---
5+
6+
# Conventions for implementing array API compatible functions and their tests
7+
8+
- **Think (not plan) about the formulation and the way to implement mathematically beautifully a LOT beforehand, ask questions to the user or internet, then finally write code.** Besides being a intelligent assistant, you have to admit that you are **extremely bad at numerical** programming due to the lack of such dataset (clean pure-Python numerical code is very rare). Think about the math formulation, the way to implement it, then at the **very final stage**, write code.
9+
10+
## Function implementation
11+
12+
- This repository is about numerical analysis. You need to make very sure about the math formulation before implementing the code.
13+
- If documentation about the formulation is provided, always read it first, to very make sure the functions are implemented mathematically correctly and beautifully.
14+
- All methods should be array API compatible.
15+
- **Function: name** Name functions without `compute_`, `calculate_` suffix (e.g. `prime_number(i: int, /)`, not `compute_prime_number`, `p_i`).
16+
- **Arguments: name**: The function name variable names in the code should not the same as the variable name math formulation (e.g. `n`) but very readable (e.g. `n_iterations`). In the variable description in the docstring, the corresponding variable name in the math formulation should be mentioned:
17+
18+
```text
19+
n_iterations : Array
20+
The number of iterations $n$ of shape (...,).
21+
```
22+
23+
Try not to add the word "corresponding to" because it is redundant.
24+
25+
- **Output shape depending on integer output**: If output shape depends on the input argument, it should match the input integer when possible. For example, to compute $0, 1, ...$, set argument name to `n_*_end` (`n_end` if it is obvious) and return `0, 1, ..., n_*_end - 1`, so that the output shape is `(n_*_end,)` and it is intuitive, matches `range(n_end)` convention and `np.arange(n_end)` syntax.
26+
- **GUFunc compatibility**: If an array is passed to function, the function should be GUFunc-compatible, i.e. the function should only remove / append extra dimensions from the LAST dimensions of the input / output arrays, e.g. `(..., a, b) -> (..., c, d, e)`.
27+
- **Arguments: arrays** If array is passed to function, use `array_api.latest.Array` as the array type hint, and use `array_api_compat.array_namespace()` to get the array API namespace `xp`. `array_api_compat.array_namespace()` should be ideally called with all input arrays / evaluated function as arguments IF POSSIBLE, e.g. `xp = array_api_compat.array_namespace(x, y, z)` to validate the input arrays are on the same array API and device. The arguments may also contain None, Python scalars, useful when arguments are optional.
28+
- **Arguments: no arrays -> special kw-only arguments** If NO array is passed to function, add `xp`, `device`, `dtype` as an required keyword-only argument with type hint `array_api.latest.ArrayNamespace`, `Any`, `Any` respectively. Do not add these arguments if an array is passed to function.
29+
- **Arguments: function -> GUFunc-compatible** If the function needs function arguments, assume that to be also GUFunc-compatible. The argument description should end with `of (..., a, b) -> (..., c, d, e)`. Don't add any word in the following sentence: "GUFunc-compatible vectorized function from array to array", just explain the mathematical meaning of the function and its shape convention.
30+
- **Arguments & Docstring: function shape convention should be reasonable to the function**: Conceptually (mathematically) unrelated axes to function should be placed at the _beginning_ of the shape as `...`.
31+
- _Example_: When mathetically integrating `n_func` functions using `n_quad` quadrature points, the function shape convention should be written as `(...) -> (..., n_func)` (`(:) -> (:, n_func)` if the user explicitly specifies to implement function not GUFunc-compatible), not `(n_quad) -> (n_quad, n_func)` or `(n_quad) -> (n_func, n_quad)`, because `n_quad` has nothing to do with functions mathematically.
32+
- `xp.moveaxis()` may be used without worrying about performance, do not worry about internal complexity whcn choosing convention.
33+
- Do not worry about axis performance when choosing convention.
34+
- **Docstring: function output-dependent shape**: When the shape is variable (depending on function etc.) one can do `(..., ...(f))` where `f` implies the function but may replaced with something more suitable. `(..., *something)` is also possible but less preferred, yet sometimes it might be more suitable.
35+
- **Docstring**: The docstring should be Numpydoc style.
36+
37+
```python
38+
from array_api.latest import Array, ArrayNamespace
39+
40+
def func(x: Array, power: Array) -> Array:
41+
"""
42+
Computes $x^p$.
43+
44+
Extended description of function.
45+
46+
Parameters
47+
----------
48+
x : Array
49+
The base array of shape (...,).
50+
y : Array
51+
The power to which $x$ is raised $p$ of shape (...,).
52+
53+
Returns
54+
-------
55+
Array
56+
$x^p$ of shape (...,).
57+
"""
58+
```
59+
60+
- **Docstring: shape**: The docstring should mention the shape of the input / output arrays and the argument description should end with `of shape (..., a, b)`.
61+
- **Shape checking**: The function should check the shape at the very beginning of its implementation.
62+
- Check every shape variable (etc. `N`) is correct
63+
- Check every variable-length shape variable (etc. `...`, `...(f)`) is both broadcastable and moreover has the same dimensions. (Does not need to have same shape.)
64+
- These should be done using `array_api_shape_check.check_shapes()` function, which syntax is as follows. The result may be useful for later computation in some cases.
65+
66+
```python
67+
def func(x: Array, y: Array) -> Array:
68+
"""
69+
Parameters
70+
----------
71+
x : Array
72+
Array of shape (..., A, B).
73+
y : Array
74+
Array of shape (..., ...(C), D, E).
75+
"""
76+
info = check_shapes("...AB,...*CDE", x, y, names="x,y")
77+
# use info for later computation if useful
78+
z = xp.zeros(info.unique["C"].shape_broadcasted, device=x.device, dtype=x.dtype)
79+
```
80+
81+
```python
82+
def check_shapes(
83+
subscripts: str, /, *operands: Array | tuple[int, ...], names: str | None = None
84+
) -> SubscriptInfoFromShape:
85+
"""
86+
Parse variable subscript ndims by solving linear equations.
87+
88+
Parameters
89+
----------
90+
subscripts : str
91+
Subscripts separated by "," per operand.
92+
93+
1. Subscripts must be of length 1
94+
2. Subscripts must not be "*" or ".".
95+
3. If start with "*", the subscript is treated as variable.
96+
4. "..." is replaced with "*.".]
97+
operands : Array or tuple[int, ...]
98+
Arrays or shape tuples corresponding to check.
99+
ndims : Sequence[int]
100+
The number of dimensions for each operand.
101+
names : str | None
102+
The names of operands separated by ",",
103+
used for error messages. If None, operand indices are used instead.
104+
105+
Returns
106+
-------
107+
SubscriptInfoFromSubcript
108+
The parsed subscript info.
109+
110+
Raises
111+
------
112+
ValueError
113+
If the subscript is invalid.
114+
115+
Examples
116+
--------
117+
>>> info = check_shapes("ij,*k*l,*li", (1, 4), (5, 6, 7), (1, 7, 3))
118+
>>> info.all
119+
((i:1->3, j:4), (*k:(5,), *l:(6, 7)), (*l:(1, 7)->(6, 7), i:3))
120+
>>> info.unique
121+
{'i': i:3, 'j': j:4, 'k': *k:(5,), 'l': *l:(6, 7)}
122+
123+
Internally `check_shapes()` calls `parse_variable_ndim()`,
124+
which determines the number of dimensions for variable subscripts by least squares.
125+
If this is successful, checks if each subscript is consistent,
126+
then finnaly raises error for all inconsistencies at once.
127+
128+
Diving into the details of the first item:
129+
130+
>>> item = info.all[0][0]
131+
>>> item.name # the name of the subscript
132+
'i'
133+
>>> item.is_variable # whether the subscript is variable (starts with "*")
134+
False
135+
>>> item.shape_current # the current shape of the subscript
136+
(1,)
137+
>>> item.shape_broadcasted # the broadcasted shape of the subscript
138+
(3,)
139+
140+
Not enough information to determine variable subscript ndims:
141+
142+
>>> import pytest
143+
>>> with pytest.raises(InconsistentNdimErrorMultipleSolutions, match="number of variables"):
144+
... check_shapes("*i*j", (1, 1))
145+
>>> with pytest.raises(InconsistentNdimErrorMultipleSolutions, match="rank"):
146+
... check_shapes("*i*j,*i*j", (1, 1), (1, 1))
147+
148+
No solution to determine variable subscript ndims:
149+
150+
>>> with pytest.raises(InconsistentNdimErrorNoSolutions, match="residuals"):
151+
... check_shapes("*i,*i", (1, 1), (1, 1, 1))
152+
>>> with pytest.raises(InconsistentNdimErrorNoSolutions, match="negative"):
153+
... check_shapes("*ij", ())
154+
155+
Does not match:
156+
>>> with pytest.raises(InconsistentShapeError):
157+
... check_shapes("ij,*k*l,*li", (3, 4), (5, 6), (1, 7, 3))
158+
159+
"""
160+
...
161+
```
162+
163+
- **Importing Numpy allowed only for constants**: Never import `numpy` directly, unless for constants like `np.pi` for context when `xp` is not available.
164+
- **Type promotion**: Understand Type promotion rules, i.e. float64 + complex64 -> complex128. Mixed integer and floating-point type promotion rules are not specified, but we assume that for every floating (including complex) dtype x, x + (int type) -> x.
165+
- **Type promotion: no wrapping Python scalars**: Avoid wrapping `int` arrays, Python scalars with `xp.asarray()` but use them directly (because it is redundant). The exception is when you need to divide int by int (in this case you only need to wrap one of them).
166+
- **Type promotion: avoid conversion as much as possible and make it inline**:Avoid creating variables for `int` version, `float` version, `complex` version of the same array as much as possible.
167+
- **Type promotion**: The type can be converted by `xp.astype(x, dtype, /)`.
168+
- **Avoid float when possible**: Avoid expressing integer as float. `1` instead of `1.0` whenever possible.
169+
- **Type promotion: Scipy -> input cpu, output asarray**: As an exception, if Scipy functions are needed (e.g. `scipy.special.yv`), do `xp.asarray(yv(xp.asarray(x, device="cpu")), device=x.device, dtype=x.dtype)`. (Do not specify dtype in the inner `asarray`). Note that every array has property `device` (including NumPy >= 2.0), you don't need `getattr`.
170+
- **Expand dimensions using []**: When expanding dimensions, prefer something like `x[(...,) + (None,) * n + (slice(None),) * m]` or `x[(slice(None),) * m + (None,) * n + (...,)]`. Never use `xp.reshape()` or `xp.expand_dims()` when the above method is possible. Avoid creating "expanded version` and "non-expanded version" of the same array, unless both of them are frequently used.
171+
- **Type promotion: no complex -> float (terrible undetectable bug)** Do not `asarray(x, dtype=dtype)` if `x` is complex dtype and `dtype` is float. This sometimes happens when `dtype` is an variable (trying to make function that is any-float compatible e.g. float32 -> float32 / complex64, complex64 -> complex64, float64 -> float64 / complex128, complex128 -> complex128). It will be equivalent to `xp.real(x)` which may cause severe numerical issues. Instead do `xp.asarray(x, dtype=xp.result_type(dtype, 1j))`.
172+
173+
## Tests
174+
175+
- Tests should be also array API compatible.
176+
- In `tests/conftest.py`, there are fixtures named `xp: ArrayNamespace`, `device: Any`, `dtype: Any`. Any test function must use these fixtures as arguments, and create arrays (i.e. `zeros()`) within the test function.
177+
- If there is an array passed as fixture / parameter to the test function. wrap it with `xp.asarray(..., device=device, dtype=dtype)` at the beginning of the test function. If it is a scalar, never wrap it but use it directly.
178+
- Parameterize tests using `pytest.mark.parametrize`.
179+
- Do not try to read the contents of `tests/conftest.py`.
180+
- To run python commands, use `uv run python`, `uv run pytest`, etc. Never run `python` directly. You may run `uv run pytest` on your own.

.agents/skills/general/SKILL.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
name: general
3+
description: "The attitude towards solving the problem"
4+
---
5+
6+
- Do not always try to completely solve the problem. The problem might be unable to fixed because
7+
- The underlying math documentation is wrong. Try finding the error in the math documentation and report it to the user.
8+
- The package function is not implemented correctly. In this case it should be fixed there, not here.
9+
- Do not be too concerned about failing tests.
10+
- The user is using NixOS, which is not very common. In this case the NixOS configuration needs to be fixed or flake.nix should be added.

.agents/skills/python/SKILL.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
name: python
3+
description: "Best practices for python code"
4+
---
5+
6+
- Function arguments should be either argument-only or keyword-only (deterministic), by using `/` and `*` in function signature. Do not add too many argument-only arguments, make it argument-only only if it is very obvious. Ideally argument-only arguments should be 1 (best) or 2, avoid making it 0 or more than 3.
7+
- Do not import within function. If the package is not installed in the current environment, install them via `uv add <package>` or tell the user to do so.
8+
- To run python commands, use `uv run python`, `uv run pytest`, etc. Never run `python` directly. You may run `uv run pytest` on your own.

.github/workflows/ci.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ jobs:
3434
fail-fast: false
3535
matrix:
3636
python-version:
37-
- "3.10"
38-
- "3.11"
3937
- "3.12"
4038
- "3.13"
4139
- "3.14"
@@ -49,7 +47,7 @@ jobs:
4947
- uses: astral-sh/setup-uv@v8.1.0
5048
with:
5149
python-version: ${{ matrix.python-version }}
52-
- run: uv sync --no-python-downloads
50+
- run: uv sync
5351
shell: bash
5452
- run: uv run pytest
5553
shell: bash

.pre-commit-config.yaml

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,21 @@ repos:
4343
rev: v0.15.12
4444
hooks:
4545
- id: ruff-check
46-
args: [--fix, --exit-non-zero-on-fix]
46+
args: [--fix, --exit-non-zero-on-fix, --unsafe-fixes]
4747
- id: ruff-format
4848
- repo: https://github.com/codespell-project/codespell
4949
rev: v2.4.2
5050
hooks:
5151
- id: codespell
52-
- repo: local
53-
hooks:
54-
- id: local-ty
55-
name: ty check
56-
entry: uv run ty check
57-
require_serial: true
58-
language: system
59-
pass_filenames: false
52+
- repo: https://github.com/typstyle-rs/pre-commit-typstyle
53+
rev: 'v0.14.4' # The revision or tag you want to use
54+
hooks:
55+
- id: typstyle
56+
# - repo: local
57+
# hooks:
58+
# - id: local-ty
59+
# name: ty check
60+
# entry: uv run --group docs ty check
61+
# require_serial: true
62+
# language: system
63+
# pass_filenames: false

docs/conf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"sphinx.ext.napoleon",
2929
"sphinx.ext.autodoc",
3030
"sphinx.ext.viewcode",
31+
"sphinx.ext.mathjax",
32+
"sphinx_math_dollar",
3133
]
3234
napoleon_google_docstring = False
3335

docs/quadrature.bib

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
@book{kress_linear_2014,
3+
location = {New York, {NY}},
4+
title = {Linear Integral Equations},
5+
volume = {82},
6+
rights = {https://www.springernature.com/gp/researchers/text-and-data-mining},
7+
isbn = {978-1-4614-9592-5 978-1-4614-9593-2},
8+
url = {https://link.springer.com/10.1007/978-1-4614-9593-2},
9+
series = {Applied Mathematical Sciences},
10+
publisher = {Springer New York},
11+
author = {Kress, Rainer},
12+
urldate = {2024-11-17},
13+
date = {2014},
14+
langid = {english},
15+
doi = {10.1007/978-1-4614-9593-2},
16+
file = {Kress - 2014 - Linear Integral Equations.pdf:/home/z/Zotero/storage/PNDWPHKS/Kress - 2014 - Linear Integral Equations.pdf:application/pdf},
17+
}
18+
19+
@article{trefethen_exponentially_2014,
20+
title = {The Exponentially Convergent Trapezoidal Rule},
21+
volume = {56},
22+
issn = {0036-1445, 1095-7200},
23+
url = {http://epubs.siam.org/doi/10.1137/130932132},
24+
doi = {10.1137/130932132},
25+
abstract = {It is well known that the trapezoidal rule converges geometrically when applied to analytic functions on periodic intervals or the real line. The mathematics and history of this phenomenon are reviewed, and it is shown that far from being a curiosity, it is linked with computational methods all across scientific computing, including algorithms related to inverse Laplace transforms, special functions, complex analysis, rational approximation, integral equations, and the computation of functions and eigenvalues of matrices and operators.},
26+
pages = {385--458},
27+
number = {3},
28+
journaltitle = {{SIAM} Review},
29+
shortjournal = {{SIAM} Rev.},
30+
author = {Trefethen, Lloyd N. and Weideman, J. A. C.},
31+
urldate = {2025-12-15},
32+
date = {2014-01},
33+
langid = {english},
34+
file = {PDF:/home/z/Zotero/storage/JJMSYSE7/Trefethen と Weideman - 2014 - The Exponentially Convergent Trapezoidal Rule.pdf:application/pdf},
35+
}

docs/quadrature.pdf

225 KB
Binary file not shown.

0 commit comments

Comments
 (0)