kauri 2.0.0__tar.gz → 2.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.
- {kauri-2.0.0 → kauri-2.1.0}/PKG-INFO +16 -7
- {kauri-2.0.0 → kauri-2.1.0}/README.md +8 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/__init__.py +8 -9
- {kauri-2.0.0 → kauri-2.1.0}/kauri/bseries.py +14 -2
- {kauri-2.0.0 → kauri-2.1.0}/kauri/gentrees.py +187 -2
- {kauri-2.0.0 → kauri-2.1.0}/kauri/rk.py +24 -8
- {kauri-2.0.0 → kauri-2.1.0}/kauri/trees.py +7 -2
- {kauri-2.0.0 → kauri-2.1.0}/kauri/utils.py +1 -1
- {kauri-2.0.0 → kauri-2.1.0}/kauri.egg-info/PKG-INFO +16 -7
- {kauri-2.0.0 → kauri-2.1.0}/kauri.egg-info/requires.txt +6 -4
- {kauri-2.0.0 → kauri-2.1.0}/pyproject.toml +11 -10
- {kauri-2.0.0 → kauri-2.1.0}/LICENSE +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/_protocols.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/bck/__init__.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/bck/bck.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/cem/__init__.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/cem/cem.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/cf.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/display.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/generic_algebra.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/gl/__init__.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/gl/gl.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/manifold_ees.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/maps.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/mkw/__init__.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/mkw/mkw.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/nck/__init__.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/nck/nck.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/oddeven.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/pgl/__init__.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/pgl/pgl.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/planar_oddeven.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri/rk_methods.py +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri.egg-info/SOURCES.txt +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri.egg-info/dependency_links.txt +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/kauri.egg-info/top_level.txt +0 -0
- {kauri-2.0.0 → kauri-2.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kauri
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Hopf algebras, B-series, and Runge-Kutta methods on rooted trees
|
|
5
5
|
Author-email: Daniil Shmelev <daniil.shmelev23@imperial.ac.uk>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,14 +16,15 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
16
16
|
Classifier: Operating System :: Unix
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
19
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist:
|
|
22
|
+
Provides-Extra: full
|
|
23
|
+
Requires-Dist: matplotlib>=3.9.2; extra == "full"
|
|
24
|
+
Requires-Dist: numpy>=1.26.4; extra == "full"
|
|
25
|
+
Requires-Dist: sympy>=1.12; extra == "full"
|
|
26
|
+
Requires-Dist: scipy>=1.13; extra == "full"
|
|
27
|
+
Requires-Dist: tqdm; extra == "full"
|
|
27
28
|
Provides-Extra: dev
|
|
28
29
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
29
30
|
Requires-Dist: ruff>=0.9.0; extra == "dev"
|
|
@@ -51,6 +52,14 @@ Kauri is a Python package for symbolic and algebraic manipulation of rooted tree
|
|
|
51
52
|
pip install kauri
|
|
52
53
|
```
|
|
53
54
|
|
|
55
|
+
The base install is lightweight (pure Python, no external dependencies) and provides tree algebra, enumeration, indexing, and Hopf algebraic operations.
|
|
56
|
+
|
|
57
|
+
For visualization, Runge-Kutta analysis, and B-series (requires matplotlib, numpy, scipy, sympy, tqdm):
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
pip install kauri[full]
|
|
61
|
+
```
|
|
62
|
+
|
|
54
63
|
## Documentation
|
|
55
64
|
|
|
56
65
|
Full documentation is available at [https://kauri.readthedocs.io](https://kauri.readthedocs.io)
|
|
@@ -20,6 +20,14 @@ Kauri is a Python package for symbolic and algebraic manipulation of rooted tree
|
|
|
20
20
|
pip install kauri
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
The base install is lightweight (pure Python, no external dependencies) and provides tree algebra, enumeration, indexing, and Hopf algebraic operations.
|
|
24
|
+
|
|
25
|
+
For visualization, Runge-Kutta analysis, and B-series (requires matplotlib, numpy, scipy, sympy, tqdm):
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
pip install kauri[full]
|
|
29
|
+
```
|
|
30
|
+
|
|
23
31
|
## Documentation
|
|
24
32
|
|
|
25
33
|
Full documentation is available at [https://kauri.readthedocs.io](https://kauri.readthedocs.io)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
Algebraic manipulation of rooted trees for the analysis of B-series and Runge-Kutta schemes.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
__version__ = "2.
|
|
20
|
+
__version__ = "2.1.0"
|
|
21
21
|
|
|
22
22
|
__all__ = [
|
|
23
23
|
# Core types
|
|
@@ -30,6 +30,8 @@ __all__ = [
|
|
|
30
30
|
# Tree generation
|
|
31
31
|
"trees_of_order", "trees_up_to_order",
|
|
32
32
|
"colored_trees_of_order", "colored_trees_up_to_order",
|
|
33
|
+
"colored_trees", "colored_tree_to_idx", "idx_to_colored_tree",
|
|
34
|
+
"canonical_to_recursive_permutation", "recursive_to_canonical_permutation",
|
|
33
35
|
"planar_trees_of_order", "planar_trees_up_to_order",
|
|
34
36
|
"colored_planar_trees_of_order", "colored_planar_trees_up_to_order",
|
|
35
37
|
# Display
|
|
@@ -53,23 +55,23 @@ __all__ = [
|
|
|
53
55
|
|
|
54
56
|
from .trees import (Tree, Forest, CommutativeForest, ForestSum, TensorProductSum,
|
|
55
57
|
NoncommutativeForest, PlanarTree, OrderedForest, EMPTY_PLANAR_TREE)
|
|
58
|
+
from .trees import EMPTY_TREE, EMPTY_FOREST, EMPTY_ORDERED_FOREST, EMPTY_FOREST_SUM, ZERO_FOREST_SUM
|
|
56
59
|
from .maps import Map, ident, sign, exact_weights, omega
|
|
57
60
|
from .display import display
|
|
58
61
|
from .gentrees import (trees_of_order, trees_up_to_order,
|
|
59
62
|
colored_trees_of_order, colored_trees_up_to_order,
|
|
63
|
+
colored_trees, colored_tree_to_idx, idx_to_colored_tree,
|
|
64
|
+
canonical_to_recursive_permutation, recursive_to_canonical_permutation,
|
|
60
65
|
planar_trees_of_order, planar_trees_up_to_order,
|
|
61
66
|
colored_planar_trees_of_order, colored_planar_trees_up_to_order)
|
|
62
67
|
from .rk import RK, rk_symbolic_weight, rk_order_cond
|
|
63
68
|
from .cf import CFMethod
|
|
64
|
-
|
|
65
69
|
from .rk_methods import (euler, heun_rk2, midpoint, kutta_rk3, heun_rk3,
|
|
66
70
|
ralston_rk3, rk4, ralston_rk4, nystrom_rk5, backward_euler,
|
|
67
71
|
implicit_midpoint, crank_nicolson, gauss6, radau_iia, lobatto6,
|
|
68
72
|
EES25, EES27)
|
|
69
|
-
|
|
70
73
|
from .bseries import BSeries, elementary_differential
|
|
71
|
-
|
|
72
|
-
from .trees import EMPTY_TREE, EMPTY_FOREST, EMPTY_ORDERED_FOREST, EMPTY_FOREST_SUM, ZERO_FOREST_SUM
|
|
74
|
+
from .oddeven import id_sqrt, minus, plus
|
|
73
75
|
|
|
74
76
|
import kauri.bck
|
|
75
77
|
import kauri.cem
|
|
@@ -77,8 +79,5 @@ import kauri.gl
|
|
|
77
79
|
import kauri.mkw
|
|
78
80
|
import kauri.nck
|
|
79
81
|
import kauri.pgl
|
|
80
|
-
|
|
81
|
-
from .oddeven import id_sqrt, minus, plus
|
|
82
|
-
|
|
83
82
|
import kauri.oddeven
|
|
84
|
-
import kauri.planar_oddeven
|
|
83
|
+
import kauri.planar_oddeven
|
|
@@ -78,15 +78,25 @@ and the symmetric-adjoint method is given by
|
|
|
78
78
|
where `kr.sign` is the :class:`Map` sending `t` to `(-1)^|t| * t`.
|
|
79
79
|
|
|
80
80
|
"""
|
|
81
|
+
from __future__ import annotations
|
|
81
82
|
import itertools
|
|
82
83
|
from functools import cache
|
|
83
84
|
from typing import Union
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
try:
|
|
87
|
+
import sympy as sp
|
|
88
|
+
except ImportError:
|
|
89
|
+
sp = None
|
|
86
90
|
|
|
87
91
|
from kauri import Tree, trees_up_to_order, Map
|
|
88
92
|
from kauri.trees import _is_scalar
|
|
89
93
|
|
|
94
|
+
def _require_sympy():
|
|
95
|
+
if sp is None:
|
|
96
|
+
raise ImportError(
|
|
97
|
+
"B-series requires sympy. Install with: pip install kauri[full]"
|
|
98
|
+
)
|
|
99
|
+
|
|
90
100
|
def _check_f_y(f, y):
|
|
91
101
|
# Checks that f and y are correctly specified
|
|
92
102
|
|
|
@@ -179,6 +189,7 @@ def elementary_differential(tree : Tree,
|
|
|
179
189
|
t = Tree([[[]],[]])
|
|
180
190
|
print(elementary_differential(t, f, y))
|
|
181
191
|
"""
|
|
192
|
+
_require_sympy()
|
|
182
193
|
if not isinstance(tree, Tree):
|
|
183
194
|
raise TypeError("The argument 'tree' must be of type Tree, not " + str(type(tree)))
|
|
184
195
|
if tree.colors() > 1:
|
|
@@ -227,7 +238,8 @@ class BSeries:
|
|
|
227
238
|
print(bs([1], 0.1)) # Evaluate the B-Series at y = [1], h = 0.1
|
|
228
239
|
"""
|
|
229
240
|
|
|
230
|
-
def __init__(self, y
|
|
241
|
+
def __init__(self, y, f, weights : Map, order : int):
|
|
242
|
+
_require_sympy()
|
|
231
243
|
if not isinstance(weights, Map):
|
|
232
244
|
raise TypeError("weights must be a Map, not " + str(type(weights)))
|
|
233
245
|
if not isinstance(order, int):
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
Functions for generating rooted trees in lexicographic order, based on the algorithms of :cite:`beyer1980constant`.
|
|
18
18
|
"""
|
|
19
19
|
from typing import Generator
|
|
20
|
-
from functools import lru_cache
|
|
20
|
+
from functools import lru_cache, cache
|
|
21
21
|
from itertools import product
|
|
22
22
|
|
|
23
|
-
from .trees import Tree
|
|
23
|
+
from .trees import Tree, EMPTY_TREE
|
|
24
24
|
from .utils import _level_sequence_to_list_repr, _apply_color_sequence
|
|
25
25
|
|
|
26
26
|
def trees_up_to_order(order : int) -> Generator[Tree, None, None]:
|
|
@@ -282,3 +282,188 @@ def colored_planar_trees_up_to_order(order: int, d: int):
|
|
|
282
282
|
_validate_num_colors(d)
|
|
283
283
|
for current_order in range(order + 1):
|
|
284
284
|
yield from colored_planar_trees_of_order(current_order, d)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# ---------------------------------------------------------------------------
|
|
288
|
+
# Colored tree indexing
|
|
289
|
+
# ---------------------------------------------------------------------------
|
|
290
|
+
|
|
291
|
+
@cache
|
|
292
|
+
def _colored_tree_list_cached(max_order: int, d: int) -> tuple:
|
|
293
|
+
"""Cached tuple of all colored trees up to max_order with d colors."""
|
|
294
|
+
return tuple(colored_trees_up_to_order(max_order, d))
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@cache
|
|
298
|
+
def _colored_tree_lookup_cached(max_order: int, d: int) -> dict:
|
|
299
|
+
"""Cached dict mapping Tree -> index."""
|
|
300
|
+
trees = _colored_tree_list_cached(max_order, d)
|
|
301
|
+
return {t: i for i, t in enumerate(trees)}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def colored_trees(d: int, max_order: int) -> list[Tree]:
|
|
305
|
+
"""
|
|
306
|
+
Returns all distinct colored rooted trees up to a given order with *d* colors,
|
|
307
|
+
starting with the empty tree.
|
|
308
|
+
|
|
309
|
+
:param d: Number of colors (path dimension).
|
|
310
|
+
:type d: int
|
|
311
|
+
:param max_order: Maximum number of nodes.
|
|
312
|
+
:type max_order: int
|
|
313
|
+
:return: List of colored trees.
|
|
314
|
+
:rtype: list[Tree]
|
|
315
|
+
"""
|
|
316
|
+
_validate_num_colors(d)
|
|
317
|
+
return list(_colored_tree_list_cached(max_order, d))
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def colored_tree_to_idx(tree: Tree, d: int, max_order: int) -> int:
|
|
321
|
+
"""
|
|
322
|
+
Returns the index of a colored tree in the canonical enumeration.
|
|
323
|
+
|
|
324
|
+
Index 0 is the empty tree. Non-empty trees are enumerated by order,
|
|
325
|
+
then by shape, then by coloring.
|
|
326
|
+
|
|
327
|
+
:param tree: A colored rooted tree.
|
|
328
|
+
:type tree: Tree
|
|
329
|
+
:param d: Number of colors (path dimension).
|
|
330
|
+
:type d: int
|
|
331
|
+
:param max_order: Maximum number of nodes.
|
|
332
|
+
:type max_order: int
|
|
333
|
+
:return: Index in the enumeration.
|
|
334
|
+
:rtype: int
|
|
335
|
+
"""
|
|
336
|
+
_validate_num_colors(d)
|
|
337
|
+
lookup = _colored_tree_lookup_cached(max_order, d)
|
|
338
|
+
if tree not in lookup:
|
|
339
|
+
raise ValueError(f"Tree {tree} not found in enumeration for d={d}, max_order={max_order}")
|
|
340
|
+
return lookup[tree]
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def idx_to_colored_tree(idx: int, d: int, max_order: int) -> Tree:
|
|
344
|
+
"""
|
|
345
|
+
Returns the colored tree at a given index in the canonical enumeration.
|
|
346
|
+
|
|
347
|
+
:param idx: Index (0 = empty tree).
|
|
348
|
+
:type idx: int
|
|
349
|
+
:param d: Number of colors (path dimension).
|
|
350
|
+
:type d: int
|
|
351
|
+
:param max_order: Maximum number of nodes.
|
|
352
|
+
:type max_order: int
|
|
353
|
+
:return: The colored tree at the given index.
|
|
354
|
+
:rtype: Tree
|
|
355
|
+
"""
|
|
356
|
+
_validate_num_colors(d)
|
|
357
|
+
trees = _colored_tree_list_cached(max_order, d)
|
|
358
|
+
if idx < 0 or idx >= len(trees):
|
|
359
|
+
raise ValueError(f"idx {idx} out of range [0, {len(trees)}) for d={d}, max_order={max_order}")
|
|
360
|
+
return trees[idx]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# ---------------------------------------------------------------------------
|
|
364
|
+
# Recursive tree ordering and canonical-recursive permutation
|
|
365
|
+
#
|
|
366
|
+
# The "recursive" ordering enumerates decorated trees by building them
|
|
367
|
+
# bottom-up from child multisets, cycling root labels innermost. This
|
|
368
|
+
# matches the C++ enumeration in pySigLib's cp_branched_trees.h.
|
|
369
|
+
#
|
|
370
|
+
# The "canonical" ordering (used by colored_trees_of_order etc.) enumerates
|
|
371
|
+
# by shape first, then colorings.
|
|
372
|
+
# ---------------------------------------------------------------------------
|
|
373
|
+
|
|
374
|
+
def _enumerate_child_multisets(target_nodes, min_idx, tree_nodes, total_count):
|
|
375
|
+
"""Enumerate multisets of tree indices whose total node count equals target_nodes."""
|
|
376
|
+
if target_nodes == 0:
|
|
377
|
+
yield ()
|
|
378
|
+
return
|
|
379
|
+
for idx in range(min_idx, total_count):
|
|
380
|
+
n = tree_nodes[idx]
|
|
381
|
+
if n > target_nodes:
|
|
382
|
+
break
|
|
383
|
+
for rest in _enumerate_child_multisets(target_nodes - n, idx, tree_nodes, total_count):
|
|
384
|
+
yield (idx,) + rest
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
@cache
|
|
388
|
+
def _enumerate_trees_recursive(d: int, max_order: int) -> tuple:
|
|
389
|
+
"""Enumerate decorated trees in recursive ordering (child-multiset first, root label innermost)."""
|
|
390
|
+
trees = []
|
|
391
|
+
tree_nodes = []
|
|
392
|
+
for order in range(1, max_order + 1):
|
|
393
|
+
if order == 1:
|
|
394
|
+
for label in range(d):
|
|
395
|
+
trees.append((1, label, ()))
|
|
396
|
+
tree_nodes.append(1)
|
|
397
|
+
else:
|
|
398
|
+
current_count = len(trees)
|
|
399
|
+
for children in _enumerate_child_multisets(order - 1, 0, tree_nodes, current_count):
|
|
400
|
+
if not children:
|
|
401
|
+
continue
|
|
402
|
+
for label in range(d):
|
|
403
|
+
trees.append((order, label, children))
|
|
404
|
+
tree_nodes.append(order)
|
|
405
|
+
return tuple(trees)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def _recursive_tree_to_kauri(tree_idx, all_trees):
|
|
409
|
+
"""Convert a recursive-order internal tree to a kauri Tree object."""
|
|
410
|
+
from .trees import Forest
|
|
411
|
+
_num_nodes, label, child_ids = all_trees[tree_idx]
|
|
412
|
+
if not child_ids:
|
|
413
|
+
return Tree([label])
|
|
414
|
+
children = [_recursive_tree_to_kauri(c, all_trees) for c in child_ids]
|
|
415
|
+
return Forest(children).join(root_color=label)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
@cache
|
|
419
|
+
def canonical_to_recursive_permutation(d: int, max_order: int):
|
|
420
|
+
"""
|
|
421
|
+
Compute the permutation mapping canonical tree indices to recursive tree indices.
|
|
422
|
+
|
|
423
|
+
``perm[i] = j`` means the tree at canonical position ``i`` is at recursive
|
|
424
|
+
position ``j``. Both are 0-indexed and exclude the empty tree.
|
|
425
|
+
|
|
426
|
+
:param d: Number of colors (path dimension).
|
|
427
|
+
:type d: int
|
|
428
|
+
:param max_order: Maximum number of nodes.
|
|
429
|
+
:type max_order: int
|
|
430
|
+
:return: Permutation array of shape ``(num_trees,)``.
|
|
431
|
+
:rtype: numpy.ndarray
|
|
432
|
+
"""
|
|
433
|
+
try:
|
|
434
|
+
import numpy as np
|
|
435
|
+
except ImportError:
|
|
436
|
+
raise ImportError("Permutation functions require numpy. Install with: pip install kauri[full]")
|
|
437
|
+
_validate_num_colors(d)
|
|
438
|
+
rec_trees = _enumerate_trees_recursive(d, max_order)
|
|
439
|
+
rec_kauri = [_recursive_tree_to_kauri(i, rec_trees) for i in range(len(rec_trees))]
|
|
440
|
+
rec_lookup = {t: i for i, t in enumerate(rec_kauri)}
|
|
441
|
+
|
|
442
|
+
canonical = _colored_tree_list_cached(max_order, d)
|
|
443
|
+
perm = [rec_lookup[kt] for kt in canonical[1:]]
|
|
444
|
+
return np.array(perm, dtype=np.int64)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
@cache
|
|
448
|
+
def recursive_to_canonical_permutation(d: int, max_order: int):
|
|
449
|
+
"""
|
|
450
|
+
Compute the permutation mapping recursive tree indices to canonical tree indices.
|
|
451
|
+
|
|
452
|
+
Inverse of :func:`canonical_to_recursive_permutation`.
|
|
453
|
+
|
|
454
|
+
:param d: Number of colors (path dimension).
|
|
455
|
+
:type d: int
|
|
456
|
+
:param max_order: Maximum number of nodes.
|
|
457
|
+
:type max_order: int
|
|
458
|
+
:return: Inverse permutation array of shape ``(num_trees,)``.
|
|
459
|
+
:rtype: numpy.ndarray
|
|
460
|
+
"""
|
|
461
|
+
try:
|
|
462
|
+
import numpy as np
|
|
463
|
+
except ImportError:
|
|
464
|
+
raise ImportError("Permutation functions require numpy. Install with: pip install kauri[full]")
|
|
465
|
+
_validate_num_colors(d)
|
|
466
|
+
perm = canonical_to_recursive_permutation(d, max_order)
|
|
467
|
+
inv = np.empty_like(perm)
|
|
468
|
+
inv[perm] = np.arange(len(perm))
|
|
469
|
+
return inv
|
|
@@ -16,15 +16,26 @@
|
|
|
16
16
|
"""
|
|
17
17
|
Runge-Kutta Schemes
|
|
18
18
|
"""
|
|
19
|
+
from __future__ import annotations
|
|
19
20
|
import copy
|
|
20
21
|
from typing import Union, Callable, Tuple
|
|
21
22
|
import warnings
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
try:
|
|
25
|
+
import numpy as np
|
|
26
|
+
import sympy
|
|
27
|
+
from scipy.optimize import root as _scipy_root
|
|
28
|
+
import matplotlib.pyplot as plt
|
|
29
|
+
from tqdm import tqdm
|
|
30
|
+
except ImportError:
|
|
31
|
+
np = sympy = _scipy_root = plt = tqdm = None
|
|
32
|
+
|
|
33
|
+
def _require_full():
|
|
34
|
+
if np is None:
|
|
35
|
+
raise ImportError(
|
|
36
|
+
"This feature requires additional dependencies. "
|
|
37
|
+
"Install with: pip install kauri[full]"
|
|
38
|
+
)
|
|
28
39
|
|
|
29
40
|
from .gentrees import trees_of_order, planar_trees_of_order
|
|
30
41
|
from .trees import Tree, Forest, ForestSum, PlanarTree, _is_scalar
|
|
@@ -158,6 +169,7 @@ def rk_symbolic_weight(
|
|
|
158
169
|
if not isinstance(rationalise, bool):
|
|
159
170
|
raise TypeError("rationalise must be a bool, not " + str(type(rationalise)))
|
|
160
171
|
|
|
172
|
+
_require_full()
|
|
161
173
|
t_ = t
|
|
162
174
|
if _is_scalar(t):
|
|
163
175
|
t_ = t * Tree(None).as_forest_sum()
|
|
@@ -214,6 +226,7 @@ def rk_order_cond(
|
|
|
214
226
|
print(rk_order_cond(t, 2, a_mask = a_mask, b_mask = b_mask))
|
|
215
227
|
|
|
216
228
|
"""
|
|
229
|
+
_require_full()
|
|
217
230
|
if not isinstance(t, (int, float, TreeLike, ForestLike, ForestSumLike)):
|
|
218
231
|
raise TypeError("t must be a Tree, Forest, ForestSum (or planar equivalent), int or float, not " + str(type(t)))
|
|
219
232
|
|
|
@@ -237,9 +250,10 @@ class RK:
|
|
|
237
250
|
:param b: The Runge--Kutta parameter vector :math:`b`.
|
|
238
251
|
"""
|
|
239
252
|
def __init__(self, a, b, name = None):
|
|
240
|
-
|
|
253
|
+
_valid_types = (list, np.ndarray) if np is not None else (list,)
|
|
254
|
+
if not isinstance(a, _valid_types):
|
|
241
255
|
raise TypeError("a must be a list or array, not " + str(type(a)))
|
|
242
|
-
if not isinstance(b,
|
|
256
|
+
if not isinstance(b, _valid_types):
|
|
243
257
|
raise TypeError("b must be a list or array, not " + str(type(b)))
|
|
244
258
|
|
|
245
259
|
self.name = name
|
|
@@ -343,7 +357,7 @@ class RK:
|
|
|
343
357
|
|
|
344
358
|
return np.concatenate(G_vec)
|
|
345
359
|
|
|
346
|
-
sol =
|
|
360
|
+
sol = _scipy_root(G, k0, method='hybr', tol=tol, options={'maxfev': max_iter})
|
|
347
361
|
|
|
348
362
|
if not sol.success:
|
|
349
363
|
warnings.warn(f"Implicit RK solver failed: {sol.message}")
|
|
@@ -379,6 +393,7 @@ class RK:
|
|
|
379
393
|
:rtype: list | array
|
|
380
394
|
"""
|
|
381
395
|
|
|
396
|
+
_require_full()
|
|
382
397
|
if not isinstance(y0, (list, np.ndarray)):
|
|
383
398
|
raise TypeError("y0 must be a list or array, not " + str(type(y0)))
|
|
384
399
|
if not isinstance(t0, float):
|
|
@@ -440,6 +455,7 @@ class RK:
|
|
|
440
455
|
:return: t_vals, y_vals - the lists of values of t and y respectively
|
|
441
456
|
:rtype: tuple[list, list]
|
|
442
457
|
"""
|
|
458
|
+
_require_full()
|
|
443
459
|
|
|
444
460
|
if not isinstance(y0, (list, np.ndarray)):
|
|
445
461
|
raise TypeError("y0 must be a list or array, not " + str(type(y0)))
|
|
@@ -37,7 +37,6 @@ from functools import total_ordering
|
|
|
37
37
|
from typing import Union
|
|
38
38
|
import warnings
|
|
39
39
|
|
|
40
|
-
import sympy
|
|
41
40
|
|
|
42
41
|
from .utils import (_nodes, _height, _factorial, _sigma,
|
|
43
42
|
_sorted_list_repr, _list_repr_to_level_sequence,
|
|
@@ -1431,8 +1430,14 @@ class ForestSum:
|
|
|
1431
1430
|
##############################################
|
|
1432
1431
|
##############################################
|
|
1433
1432
|
|
|
1433
|
+
try:
|
|
1434
|
+
import sympy as _sympy
|
|
1435
|
+
_SCALAR_TYPES = (numbers.Real, _sympy.Expr)
|
|
1436
|
+
except ImportError:
|
|
1437
|
+
_SCALAR_TYPES = (numbers.Real,)
|
|
1438
|
+
|
|
1434
1439
|
def _is_scalar(obj):
|
|
1435
|
-
return isinstance(obj,
|
|
1440
|
+
return isinstance(obj, _SCALAR_TYPES)
|
|
1436
1441
|
|
|
1437
1442
|
def _is_tree_or_forest(obj):
|
|
1438
1443
|
return isinstance(obj, (TreeLike, ForestLike))
|
|
@@ -18,7 +18,6 @@ Back-end utility functions
|
|
|
18
18
|
"""
|
|
19
19
|
import math
|
|
20
20
|
from functools import cache
|
|
21
|
-
import sympy as sp
|
|
22
21
|
|
|
23
22
|
def _to_list(obj):
|
|
24
23
|
# Convert a tuple representation to a list representation
|
|
@@ -241,6 +240,7 @@ def _next_planar_layout(layout):
|
|
|
241
240
|
|
|
242
241
|
def _rationalise(c, tol = 1e-10):
|
|
243
242
|
# rationalised float
|
|
243
|
+
import sympy as sp
|
|
244
244
|
return str(sp.nsimplify(c, tolerance=tol, rational = True))
|
|
245
245
|
|
|
246
246
|
def _str(c, rationalise = False, tol = 1e-10):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kauri
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Hopf algebras, B-series, and Runge-Kutta methods on rooted trees
|
|
5
5
|
Author-email: Daniil Shmelev <daniil.shmelev23@imperial.ac.uk>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,14 +16,15 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
16
16
|
Classifier: Operating System :: Unix
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
19
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist:
|
|
22
|
+
Provides-Extra: full
|
|
23
|
+
Requires-Dist: matplotlib>=3.9.2; extra == "full"
|
|
24
|
+
Requires-Dist: numpy>=1.26.4; extra == "full"
|
|
25
|
+
Requires-Dist: sympy>=1.12; extra == "full"
|
|
26
|
+
Requires-Dist: scipy>=1.13; extra == "full"
|
|
27
|
+
Requires-Dist: tqdm; extra == "full"
|
|
27
28
|
Provides-Extra: dev
|
|
28
29
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
29
30
|
Requires-Dist: ruff>=0.9.0; extra == "dev"
|
|
@@ -51,6 +52,14 @@ Kauri is a Python package for symbolic and algebraic manipulation of rooted tree
|
|
|
51
52
|
pip install kauri
|
|
52
53
|
```
|
|
53
54
|
|
|
55
|
+
The base install is lightweight (pure Python, no external dependencies) and provides tree algebra, enumeration, indexing, and Hopf algebraic operations.
|
|
56
|
+
|
|
57
|
+
For visualization, Runge-Kutta analysis, and B-series (requires matplotlib, numpy, scipy, sympy, tqdm):
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
pip install kauri[full]
|
|
61
|
+
```
|
|
62
|
+
|
|
54
63
|
## Documentation
|
|
55
64
|
|
|
56
65
|
Full documentation is available at [https://kauri.readthedocs.io](https://kauri.readthedocs.io)
|
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "kauri"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.1.0"
|
|
8
8
|
description = "Hopf algebras, B-series, and Runge-Kutta methods on rooted trees"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
11
|
license = { text = "Apache-2.0" }
|
|
12
12
|
authors = [
|
|
13
13
|
{ name = "Daniil Shmelev", email = "daniil.shmelev23@imperial.ac.uk" },
|
|
@@ -24,19 +24,20 @@ classifiers = [
|
|
|
24
24
|
"Programming Language :: Python",
|
|
25
25
|
"Topic :: Scientific/Engineering :: Mathematics",
|
|
26
26
|
]
|
|
27
|
-
dependencies = [
|
|
28
|
-
"matplotlib>=3.9.2",
|
|
29
|
-
"numpy>=1.26.4",
|
|
30
|
-
"sympy>=1.12",
|
|
31
|
-
"scipy>=1.13",
|
|
32
|
-
"tqdm",
|
|
33
|
-
]
|
|
27
|
+
dependencies = []
|
|
34
28
|
|
|
35
29
|
[project.urls]
|
|
36
30
|
Homepage = "https://github.com/daniil-shmelev/kauri"
|
|
37
31
|
Documentation = "https://kauri.readthedocs.io"
|
|
38
32
|
|
|
39
33
|
[project.optional-dependencies]
|
|
34
|
+
full = [
|
|
35
|
+
"matplotlib>=3.9.2",
|
|
36
|
+
"numpy>=1.26.4",
|
|
37
|
+
"sympy>=1.12",
|
|
38
|
+
"scipy>=1.13",
|
|
39
|
+
"tqdm",
|
|
40
|
+
]
|
|
40
41
|
dev = [
|
|
41
42
|
"pytest>=8.0",
|
|
42
43
|
"ruff>=0.9.0",
|
|
@@ -46,7 +47,7 @@ dev = [
|
|
|
46
47
|
include = ["kauri*"]
|
|
47
48
|
|
|
48
49
|
[tool.ruff]
|
|
49
|
-
target-version = "
|
|
50
|
+
target-version = "py39"
|
|
50
51
|
line-length = 100
|
|
51
52
|
src = ["kauri", "unit_tests"]
|
|
52
53
|
exclude = ["build", "docs/_build", ".venv"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|