kauri 1.0.0__py3-none-any.whl

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/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ """
2
+ Algebraic manipulation of rooted trees for the analysis of B-series and Runge-Kutta schemes.
3
+ """
4
+ from .trees import Tree, Forest, ForestSum, TensorProductSum
5
+ from .maps import Map, ident, sign, exact_weights, omega
6
+ from .display import display
7
+ from .gentrees import trees_of_order, trees_up_to_order
8
+ from .rk import RK, rk_symbolic_weight, rk_order_cond
9
+
10
+ from .rk_methods import (euler, heun_rk2, midpoint, kutta_rk3, heun_rk3,
11
+ ralston_rk3, rk4, ralston_rk4, nystrom_rk5, backward_euler,
12
+ implicit_midpoint, crank_nicolson, gauss6, radau_iia, lobatto6)
13
+
14
+ from .bseries import BSeries, elementary_differential
15
+
16
+ from .trees import EMPTY_TREE, EMPTY_FOREST, EMPTY_FOREST_SUM, ZERO_FOREST_SUM
17
+
18
+ import kauri.bck
19
+ import kauri.cem
kauri/bck/__init__.py ADDED
@@ -0,0 +1,28 @@
1
+ """
2
+ The ``kauri.bck`` sub-package implements the Butcher-Connes-Kreimer (BCK) :cite:`connes1999hopf` Hopf algebra
3
+ :math:`(H, \\Delta_{BCK}, \\mu, \\varepsilon_{BCK}, \\emptyset, S_{BCK})`, defined as follows.
4
+
5
+ - :math:`H` is the set of all non-planar rooted trees.
6
+ - The unit :math:`\\emptyset` is the empty forest.
7
+ - The counit map is defined by :math:`\\varepsilon_{BCK}(\\emptyset) = 1`,
8
+ :math:`\\varepsilon_{BCK}(t) = 0` for all :math:`\\emptyset \\neq t \\in H`.
9
+ - Multiplication :math:`\\mu : H \\otimes H \\to H` is defined as the
10
+ commutative juxtaposition of two forests.
11
+ - Comultiplication :math:`\\Delta : H \\to H \\otimes H` is defined as
12
+
13
+ .. math::
14
+
15
+ \\Delta_{BCK}(t) = t \\otimes \\emptyset + \\emptyset \\otimes t + \\sum_{s \\subset t} [t \\setminus s] \\otimes s
16
+
17
+ where the sum runs over all proper rooted subtrees :math:`s` of :math:`t`, and :math:`[t \\setminus s]`
18
+ is the forest of all trees remaining after erasing :math:`s` from :math:`t`.
19
+ - The antipode :math:`S_{BCK}` is defined by :math:`S_{BCK}(\\bullet) = -\\bullet` and
20
+
21
+ .. math::
22
+
23
+ S_{BCK}(t) = -t - \\sum_{s \\subset t} (-1)^{n(t \\setminus s)} S_{BCK}([t \\setminus s]) s,
24
+
25
+ where :math:`n(t \\setminus s)` is the number of trees in the forest :math:`[t \\setminus s]`.
26
+ """
27
+
28
+ from .bck import antipode, counit, coproduct, map_power, map_product
kauri/bck/bck.py ADDED
@@ -0,0 +1,119 @@
1
+ """
2
+ Front-end for the BCK module
3
+ """
4
+ from ..bck_impl import _counit, _coproduct, _antipode
5
+ from ..maps import Map
6
+ from ..trees import Tree, TensorProductSum
7
+
8
+ counit = Map(_counit)
9
+ counit.__doc__ = """
10
+ The counit :math:`\\varepsilon_{BCK}` of the BCK Hopf algebra.
11
+
12
+ :type: Map
13
+
14
+ Example usage::
15
+
16
+ import kauri as kr
17
+ import kauri.bck as bck
18
+
19
+ bck.counit(kr.Tree(None)) # Returns 1
20
+ bck.counit(kr.Tree([])) # Returns 0
21
+ """
22
+
23
+ antipode = Map(_antipode)
24
+ antipode.__doc__ = """
25
+ The antipode :math:`S_{BCK}` of the BCK Hopf algebra.
26
+
27
+ :type: Map
28
+
29
+ Example usage::
30
+
31
+ import kauri as kr
32
+ import kauri.bck as bck
33
+
34
+ t = kr.Tree([[[]],[]])
35
+ bck.antipode(t)
36
+ """
37
+
38
+ def coproduct(t : Tree) -> TensorProductSum:
39
+ """
40
+ The coproduct :math:`\\Delta_{BCK}` of the BCK Hopf algebra.
41
+
42
+ :param t: tree
43
+ :type t: Tree
44
+ :rtype: TensorProductSum
45
+
46
+ Example usage::
47
+
48
+ import kauri as kr
49
+ import kauri.bck as bck
50
+
51
+ bck.coproduct(kr.Tree([])) # Returns 1 ∅ ⊗ []+1 [] ⊗ ∅
52
+ bck.coproduct(kr.Tree([[]])) # Returns 1 [[]] ⊗ ∅+1 ∅ ⊗ [[]]+1 [] ⊗ []
53
+ """
54
+ if not isinstance(t, Tree):
55
+ raise TypeError("Argument to bck.coproduct must be a Tree, not " + str(type(t)))
56
+ return _coproduct(t)
57
+
58
+ def map_product(f : Map, g : Map) -> Map:
59
+ """
60
+ Returns the product of maps in the BCK Hopf algebra, defined by
61
+
62
+ .. math::
63
+
64
+ (f \\cdot g)(t) := \\mu \\circ (f \\otimes g) \\circ \\Delta_{BCK} (t)
65
+
66
+ .. note::
67
+ `bck.map_product(f,g)` is equivalent to the Map operator `f * g`
68
+
69
+ :param f: f
70
+ :type f: Map
71
+ :param g: g
72
+ :type g: Map
73
+ :rtype: Map
74
+
75
+ Example usage::
76
+
77
+ import kauri as kr
78
+ import kauri.bck as bck
79
+
80
+ ident = kr.Map(lambda x : x)
81
+ counit = bck.map_product(ident, bck.antipode) # Equivalent to indent * bck.antipode
82
+ """
83
+ if not (isinstance(f, Map) and isinstance(g, Map)):
84
+ raise TypeError("Arguments in bck.map_product must be of type Map, not " + str(type(f)) + " and " + str(type(g)))
85
+ return f * g
86
+
87
+ def map_power(f : Map, exponent : int) -> Map:
88
+ """
89
+ Returns the power of a map in the BCK Hopf algebra, where the product of functions is defined by
90
+
91
+ .. math::
92
+
93
+ (f \\cdot g)(t) := \\mu \\circ (f \\otimes g) \\circ \\Delta_{BCK} (t)
94
+
95
+ and negative powers are defined as :math:`f^{-n} = f^n \\circ S_{BCK}`,
96
+ where :math:`S_{BCK}` is the BCK antipode.
97
+
98
+ .. note::
99
+ `bck.map_power(f, n)` is equivalent to the Map operator `f ** n`
100
+
101
+ :param f: f
102
+ :type f: Map
103
+ :param exponent: exponent
104
+ :type exponent: int
105
+
106
+ Example usage::
107
+
108
+ import kauri as kr
109
+ import kauri.bck as bck
110
+
111
+ ident = kr.Map(lambda x : x)
112
+ S = bck.map_power(ident, -1) # antipode, equivalent to ident ** (-1)
113
+ ident_sq = bck.map_power(ident, 2) # identity squared, equivalent to ident ** 2
114
+ """
115
+ if not isinstance(f, Map):
116
+ raise TypeError("f must be a Map, not " + str(type(f)))
117
+ if not isinstance(exponent, int):
118
+ raise TypeError("exponent must be an int, not " + str(type(exponent)))
119
+ return f ** exponent
@@ -0,0 +1,4 @@
1
+ """
2
+ Back-end for the BCK module
3
+ """
4
+ from .bck_impl import _antipode, _counit, _coproduct
@@ -0,0 +1,101 @@
1
+ """
2
+ Back-end for the BCK module
3
+ """
4
+ from functools import cache
5
+ import itertools
6
+ from ..trees import (Tree, Forest, TensorProductSum,
7
+ EMPTY_TREE, EMPTY_FOREST, EMPTY_FOREST_SUM)
8
+ from ..generic_algebra import _forest_apply
9
+
10
+ def _counit(t):
11
+ # Return 1 if t is the empty tree, otherwise 0
12
+ return 1 if t.list_repr is None else 0
13
+
14
+ @cache
15
+ def _antipode(t):
16
+ if t.list_repr is None:
17
+ return EMPTY_FOREST_SUM # Antipode of empty tree is the empty tree
18
+ if t.list_repr == tuple():
19
+ return -t # Antipode of singleton is the negative singleton
20
+
21
+ cp = _coproduct(t)
22
+ out = -t.as_forest_sum() # First term, -t
23
+ for c, branches, subtree_ in cp: # Remaining terms
24
+ subtree = subtree_[0] # Convert from Forest to Tree
25
+ if subtree.equals(t) or subtree.equals(EMPTY_TREE):
26
+ continue # We've already included the -t term at the start, so move on
27
+ out = out - c * _forest_apply(branches, _antipode) * subtree
28
+
29
+ return out.simplify()
30
+
31
+ @cache
32
+ def _coproduct_helper_2(t):
33
+ # This returns the coproduct as a list of (Forest, Tree) tuples
34
+ # The function _coproduct then converts this to a tensor product sum
35
+ # and simplifies.
36
+
37
+ # We compute the coproduct for a tree t = [t_1, t_2, ..., t_k] recursively.
38
+ # As per https://www2.mathematik.hu-berlin.de/~kreimer/wp-content/uploads/Foissy.pdf,
39
+ # the coproduct can be written as a sum over admissible cuts, defined as cuts
40
+ # where every walk from the root to a leaf contains at most one cut edge.
41
+ # The coproduct is then a sum of tensor products, where each term is the
42
+ # product of the forest of branches resulting from a cut and the remaining tree
43
+ # connected to the root. Denote these by P_c(t) and R_c(t) respectively, for a
44
+ # cut c.
45
+
46
+ # The recursion works on the idea that P_c(t) is the union of P_c(t_i),
47
+ # and R_c(t) = [R_c(t_1), R_c(t_2), ..., R_c(t_k)]. This allows us to use the
48
+ # coproducts of t_i to compute the coproduct of t.
49
+
50
+ # Caching of this function makes it fairly efficient for large computations.
51
+
52
+ if t.list_repr is None: # Empty tree
53
+ return [(EMPTY_FOREST, EMPTY_TREE)]
54
+ if t.list_repr == tuple(): # Singleton tree
55
+ return [(EMPTY_FOREST, t), (t.as_forest(), EMPTY_TREE)]
56
+
57
+ # Compute the coproducts of t_1, t_2, ..., t_k
58
+ subtree_coproducts = []
59
+ for rep in t.list_repr[:-1]: # Recall last element is the root label, so take [:-1]
60
+ subtree = Tree(rep)
61
+ subtree_coproducts.append(_coproduct_helper_2(subtree))
62
+
63
+ t_coproduct = [(Forest((t,)), EMPTY_TREE)] # First term of coproduct, t x empty tree
64
+
65
+ # Remaining terms, compute these recursively
66
+ for p in itertools.product(*subtree_coproducts):
67
+ R_c_repr = []
68
+ P_c_tree_list = []
69
+ for f, s in p:
70
+ if s.list_repr is not None:
71
+ R_c_repr += [s.list_repr]
72
+ P_c_tree_list += f.tree_list
73
+ R_c_repr += [t.list_repr[-1]] #Add label of root
74
+ t_coproduct.append((Forest(P_c_tree_list), Tree(R_c_repr)))
75
+
76
+ return t_coproduct
77
+
78
+ def _coproduct_2(t):
79
+ cp = _coproduct_helper_2(t)
80
+ return TensorProductSum(tuple((1, x[0], x[1]) for x in cp)).simplify()
81
+
82
+ @cache
83
+ def _coproduct(t):
84
+ # This follows the recursive definition of https://arxiv.org/pdf/hep-th/9808042
85
+ # using B_- and B_+
86
+ if t == Tree(None):
87
+ return TensorProductSum(( (1, EMPTY_FOREST, EMPTY_FOREST), )) # Tree(None) @ Tree(None)
88
+ if len(t.list_repr) == 1:
89
+ return TensorProductSum(( (1, EMPTY_FOREST, t.as_forest()), (1, t.as_forest(), EMPTY_FOREST) )) # Tree(None) @ t + t @ Tree(None)
90
+
91
+ root_color = t.list_repr[-1]
92
+ branches = t.unjoin()
93
+
94
+ cp_prod = 1
95
+ for subtree in branches:
96
+ cp = _coproduct(subtree)
97
+ cp_prod = cp_prod * cp
98
+
99
+ # Return t \otimes \emptyset + (id \otimes B_+)[\Delta(B_-(t))]
100
+ out = t @ Tree(None) + TensorProductSum(tuple((c, f1, f2.join(root_color)) for c, f1, f2 in cp_prod))
101
+ return out.simplify()
kauri/bseries.py ADDED
@@ -0,0 +1,296 @@
1
+ """
2
+ This module allows for the symbolic manipulation and evaluation of truncated
3
+ B-Series on unlabelled trees, for an ODE of the form
4
+
5
+ .. math::
6
+
7
+ \\frac{dy}{ds} = f(y).
8
+
9
+ Given a weights function :math:`\\varphi`, the associated truncated B-Series is
10
+
11
+ .. math::
12
+
13
+ B_h(\\varphi, y_0) := \\sum_{|t| \\leq n} \\frac{h^{|t|}}{\\sigma(t)} \\varphi(t) F(t)(y_0),
14
+
15
+ where the sum runs over all trees of order at most :math:`n`, :math:`\\sigma(t)` is the symmetry factor
16
+ of a tree, and :math:`F(t)(y_0)` are the elementary differentials, defined recursively on trees by:
17
+
18
+ .. math::
19
+
20
+ F(\\emptyset) = y,
21
+ .. math::
22
+
23
+ F(\\bullet) = f(y),
24
+ .. math::
25
+
26
+ F([t_1, t_2, \\ldots, t_k])(y) = f^{(k)}(y)(F(t_1)(y), F(t_2)(y), \\ldots, F(t_k)(y)).
27
+
28
+ The main objective of this module is to evaluate existing B-series. For more complicated operations,
29
+ it is recommended to work with the underlying character (or elementary weights function) and then
30
+ construct a B-series from the result. For example, take the following B-series corresponding to
31
+ the Euler and RK4 schemes:
32
+
33
+ .. code-block:: python
34
+
35
+ import kauri as kr
36
+
37
+ y1 = sp.symbols('y1')
38
+ y = sp.Matrix([y1])
39
+ f = sp.Matrix([y1 ** 2])
40
+
41
+ bs1 = kr.BSeries(y, f, kr.euler.elementary_weights_map(), 5)
42
+ bs2 = kr.BSeries(y, f, kr.rk4.elementary_weights_map(), 5)
43
+
44
+ At the level of the underlying characters, the composition of two B-series is given by the BCK product
45
+ of the characters, so one can get the composition by doing
46
+
47
+ .. code-block:: python
48
+
49
+ bs_composition = kr.BSeries(y, f, bs2.weights * bs1.weights, 5)
50
+
51
+ Similarly the inverse B-series for the Euler scheme is given by
52
+
53
+ .. code-block:: python
54
+
55
+ bs_inverse = kr.BSeries(y, f, bs1.weights ** (-1), 5)
56
+
57
+ and the symmetric-adjoint method is given by
58
+
59
+ .. code-block:: python
60
+
61
+ bs_adjoint = kr.BSeries(y, f, bs1.weights ** (-1) & kr.sign, 5)
62
+
63
+ where `kr.sign` is the :class:`Map` sending `t` to `-t`.
64
+
65
+ """
66
+ import itertools
67
+ from functools import cache
68
+ from typing import Union
69
+
70
+ import sympy as sp
71
+
72
+ from kauri import Tree, trees_up_to_order, Map
73
+
74
+ def _check_f_y(f, y):
75
+ # Checks that f and y are correctly specified
76
+
77
+ if not isinstance(y, sp.Matrix):
78
+ raise TypeError("y must be of type sympy.Matrix, not " + str(type(y)))
79
+ if not isinstance(f, sp.Matrix):
80
+ raise TypeError("The vector field f must be of type sympy.Matrix, not " + str(type(f)))
81
+
82
+ # Make sure f, y are vectors of the same dimensions
83
+ if not (f.shape[1] == 1 and y.shape[1] == 1 and f.shape[0] == y.shape[0]):
84
+ raise ValueError("""f, y must be column vectors, both of shape (d, 1) for some d.
85
+ Instead, got f of shape """ + str(f.shape) + " and y of shape " + str(y.shape))
86
+
87
+ # Make sure f is in terms of y and nothing else
88
+ allowed_symbols = set(y)
89
+ f_symbols = set().union(*(expr.free_symbols for expr in f))
90
+ if not f_symbols <= allowed_symbols:
91
+ raise ValueError("""The vector field f contains unknown symbols which are not contained in y.
92
+ If these are constants, please add them to the ODE with a derivative of 0. If these represent
93
+ time, please add them to the ODE with a derivative of 1.""")
94
+
95
+ @cache
96
+ def _elementary_differential(tree : Tree,
97
+ f : sp.ImmutableDenseMatrix,
98
+ y_vars : sp.ImmutableDenseMatrix):
99
+ if tree.list_repr is None:
100
+ return y_vars # y
101
+ if len(tree.list_repr) == 1:
102
+ return f # f(y)
103
+
104
+ # tree = [t_1, ..., t_k], sub_diffs = [F(t_1), ..., F(t_k)]
105
+ sub_diffs = tuple(_elementary_differential(subtree, f, y_vars) for subtree in tree.unjoin())
106
+
107
+ # Now compute f^(k) (F(t_1), ..., F(t_k))
108
+ # which equals \sum_{i_j = 1,...,d} F(t_1)_{i_1} ... F(t_k)_{i_k} ( d^k f / dy_{i_1} ... dy_{i_k} )
109
+ result = sp.zeros(*sp.shape(y_vars))
110
+ dim = len(y_vars)
111
+ k = len(tree.list_repr) - 1
112
+
113
+ for idx in itertools.product(range(dim), repeat=k):
114
+ # Compute the derivative d^k f / dy_{i_1} ... dy_{i_k} first
115
+ term = f
116
+ for i in idx:
117
+ term = sp.diff(term, y_vars[i])
118
+
119
+ # Now multiply by F(t_1)_{i_1} ... F(t_k)_{i_k}
120
+ for j, i in enumerate(idx):
121
+ term *= sub_diffs[j][i]
122
+ result += term
123
+
124
+ return result
125
+
126
+ def elementary_differential(tree : Tree,
127
+ f : sp.Matrix,
128
+ y : sp.Matrix
129
+ ) -> sp.Matrix:
130
+ """
131
+ Returns the elementary differential of a vector field.
132
+ These are defined recursively on trees by:
133
+
134
+ .. math::
135
+
136
+ F(\\emptyset) = y,
137
+ .. math::
138
+
139
+
140
+ F(\\bullet) = f(y),
141
+ .. math::
142
+
143
+
144
+ F([t_1, t_2, \\ldots, t_k])(y) = f^{(k)}(y)(F(t_1)(y), F(t_2)(y), \\ldots, F(t_k)(y)).
145
+
146
+ :param tree: Unlabelled tree corresponding to the elementary differential
147
+ :type tree: Tree
148
+ :param f: Vector field
149
+ :type f: sympy.Matrix
150
+ :param y: Symbolic variables y
151
+ :type y: sympy.Matrix
152
+
153
+ Example usage::
154
+
155
+ import kauri as kr
156
+ import sympy as sp
157
+
158
+ y1, y2 = sp.symbols('y1 y2')
159
+ y = sp.Matrix([y1, y2])
160
+ f = sp.Matrix([y1 ** 2, y1 * y2])
161
+
162
+ t = kr.Tree([[[]],[]])
163
+ elementary_differential(t, f, y) # Returns sp.Matrix([[4 * y1**5 ], [ 4 * y1**4 * y2]])
164
+ """
165
+ if not isinstance(tree, Tree):
166
+ raise TypeError("The argument 'tree' must be of type Tree, not " + str(type(tree)))
167
+ if tree.colors() > 1:
168
+ raise ValueError("Tree passed to elementary differential must be unlabelled.")
169
+
170
+ _check_f_y(f, y)
171
+
172
+ return _elementary_differential(tree, sp.ImmutableDenseMatrix(f), sp.ImmutableDenseMatrix(y))
173
+
174
+
175
+ class BSeries:
176
+ """
177
+ This class allows for the symbolic manipulation and evaluation of truncated
178
+ B-Series on unlabelled trees, for a given vector field f. Given a weights
179
+ function :math:`\\varphi`, the associated truncated B-Series is
180
+
181
+ .. math::
182
+
183
+ B_h(\\varphi, y_0) := \\sum_{|t| \\leq n} \\frac{h^{|t|}}{\\sigma(t)} \\varphi(t) F(t)(y_0),
184
+
185
+ where the sum runs over all trees of order at most :math:`n`.
186
+
187
+ :param y: Symbolic variables y
188
+ :type y: sympy.Matrix
189
+ :param f: Vector field
190
+ :type f: sympy.Matrix
191
+ :param weights: The weights function :math:`\\varphi` corresponding to the B-Series.
192
+ :type weights: kauri.Map
193
+ :param order: The truncation order of the B-Series
194
+ :type order: int
195
+
196
+ Example usage::
197
+
198
+ import kauri as kr
199
+ import sympy as sp
200
+
201
+ y1 = sp.symbols('y1')
202
+ y = sp.Matrix([y1])
203
+ f = sp.Matrix([y1 ** 2])
204
+
205
+ m = kr.rk4.elementary_weights_map()
206
+ bs = BSeries(y, f, m, 5)
207
+
208
+ print(bs.series()) # Print the B-Series as a sympy expression
209
+ print(bs(1, 0.1)) # Evaluate the B-Series at y = 1, h = 0.1
210
+ """
211
+
212
+ def __init__(self, y : sp.Matrix, f : sp.Matrix, weights : Map, order : int):
213
+ if not isinstance(weights, Map):
214
+ raise TypeError("weights must be a Map, not " + str(type(weights)))
215
+ if not isinstance(order, int):
216
+ raise TypeError("order must be an int, not " + str(type(order)))
217
+ if order < 0:
218
+ raise ValueError("order cannot be negative")
219
+
220
+ _check_f_y(f, y)
221
+
222
+ self.f = f
223
+ self.y = y
224
+ self.f_imm = sp.ImmutableDenseMatrix(f) #Immutable for cache in elementary_differential
225
+ self.y_imm = sp.ImmutableDenseMatrix(y) #Immutable for cache in elementary_differential
226
+ self.h = sp.symbols('h')
227
+ self.order = order
228
+ self.weights = weights
229
+ self.dim = len(y)
230
+ self.symbolic_expr = sp.zeros(*sp.shape(y))
231
+ for t in trees_up_to_order(order):
232
+ self.symbolic_expr = self.symbolic_expr + self.h ** t.nodes() * weights(t) * _elementary_differential(t, self.f_imm, self.y_imm) / t.sigma()
233
+
234
+ def __call__(self, y : list, h : Union[int, float]) -> list:
235
+ """
236
+ Evalutes the B-series at the given values for y and h.
237
+
238
+ :param y: List of values to substitute for y
239
+ :type y: list
240
+ :param h: Value to substitute for the step size h
241
+ :type h: int | float
242
+ :return: Value of the B-series evaluated for the given values of y and h
243
+ :rtype: list
244
+
245
+ """
246
+ if not isinstance(y, list):
247
+ raise ValueError("y must be a list, not " + str(type(y)))
248
+ if len(y) != self.dim:
249
+ raise ValueError("List of values for y is of incorrect length. Expected " + str(self.dim) + " got " + str(len(y)))
250
+ if not isinstance(h, (int, float)):
251
+ raise ValueError("h must be an int or float, not " + str(type(h)))
252
+
253
+ out = self.symbolic_expr.subs(self.h, h)
254
+ for i in range(self.dim):
255
+ out = out.subs(self.y[i], y[i])
256
+ return [float(x) for x in out]
257
+
258
+ def series(self) -> sp.Matrix:
259
+ """
260
+ Returns the truncated B-series as a sympy Matrix.
261
+
262
+ :rtype: sympy.Matrix
263
+ """
264
+ return self.symbolic_expr
265
+
266
+ def __repr__(self):
267
+ return repr(self.symbolic_expr)
268
+
269
+ # def __and__(self, other : 'BSeries') -> 'BSeries':
270
+ # """
271
+ # Returns the composition of two B-Series, assuming they are with respect
272
+ # to the same variables and vector field. That is, given two B-Series:
273
+ #
274
+ # .. math::
275
+ #
276
+ # B_h(\\varphi, y_0) := \\sum_{|t| \\leq n} \\frac{h^{|t|}}{\\sigma(t)} \\varphi(t) F(t)(y_0),
277
+ #
278
+ # .. math::
279
+ #
280
+ # B_h(\\psi, y_0) := \\sum_{|t| \\leq n} \\frac{h^{|t|}}{\\sigma(t)} \\psi(t) F(t)(y_0),
281
+ #
282
+ # returns their composition :math:`B_h(\\psi, B_h(\\varphi, y_0)) = B_h(\\varphi * \\psi, y_0)`,
283
+ # where the product is the BCK product of characters. The truncation order of the resulting B-series
284
+ # is the minimum of the truncation orders of the original two.
285
+ #
286
+ # :param other: other
287
+ # :type other: BSeries
288
+ # """
289
+ # if not isinstance(other, BSeries):
290
+ # raise TypeError("Cannot multiply BSeries and object of type " + str(type(other)))
291
+ # if self.y != other.y:
292
+ # raise ValueError("Cannot compose B-Series: symbolic variables y of the two series do not match")
293
+ # if self.f != other.f:
294
+ # raise ValueError("Cannot compose B-Series: vector fields of the two series do not match")
295
+ #
296
+ # return BSeries(self.y, self.f, self.weights * other.weights, min(self.order, other.order))
kauri/cem/__init__.py ADDED
@@ -0,0 +1,43 @@
1
+ """
2
+ The ``kauri.cem`` sub-package implements the Calaque, Ebrahimi-Fard and Manchon (CEM) :cite:`calaque2011two` Hopf algebra
3
+ :math:`(\\widetilde{H}, \\Delta_{CEM}, \\mu, \\varepsilon_{CEM}, \\bullet, S_{CEM})`, defined as follows.
4
+
5
+ - :math:`\\widetilde{H}` is the space of non-empty trees, defined as
6
+ :math:`\\widetilde{H} = H / J` where :math:`J` is the ideal
7
+ generated by :math:`\\bullet - \\emptyset`.
8
+ - The unit is the single-node tree, :math:`\\bullet`.
9
+ - The counit map is defined by :math:`\\varepsilon_{CEM}(\\bullet) = 1`,
10
+ :math:`\\varepsilon_{CEM}(t) = 0` for all :math:`\\bullet \\neq t \\in \\widetilde{H}`.
11
+ - Multiplication :math:`\\mu : \\widetilde{H} \\otimes \\widetilde{H} \\to \\widetilde{H}` is defined as the
12
+ commutative juxtaposition of two forests.
13
+ - Comultiplication :math:`\\Delta : \\widetilde{H} \\to \\widetilde{H} \\otimes \\widetilde{H}` is defined as
14
+
15
+ .. math::
16
+
17
+ \\Delta_{CEM}(t) = \\sum_{s \\subset t} s \\otimes t / s
18
+
19
+ where the sum runs over all possible subforests :math:`s` of :math:`t`, and
20
+ :math:`t / s` is the tree obtained by contracting each connected component of
21
+ :math:`s` onto a vertex :cite:`calaque2011two`.
22
+ - The antipode :math:`S_{CEM}` is defined by :math:`S_{CEM}(\\bullet) = \\bullet` and
23
+
24
+ .. math::
25
+
26
+ S_{CEM}(t) = -t - \\sum_{t, \\bullet \\neq s \\subset t} S_{CEM}(s) \\, t / s.
27
+
28
+ .. note::
29
+
30
+ We adopt the singleton-reduced coproduct, where the singleton tree :math:`\\bullet`
31
+ appears in each forest at most once. This defines a Hopf algebra
32
+ on :math:`\\widetilde{H} = H / J`. There are alternative forms of the
33
+ coproduct defined directly on :math:`H`, but these do not define a Hopf
34
+ algebra.
35
+
36
+ .. note::
37
+
38
+ Since :math:`\\bullet` is the unit, characters :math:`\\phi` on the resulting
39
+ Hopf algebra must satisfy :math:`\\phi(\\bullet) = 1`.
40
+
41
+ """
42
+
43
+ from .cem import antipode, counit, coproduct, map_power, map_product