def series_commutator(a, b): """Corresponds to tree commutator, just for series.""" # TODO: TEST ME! def new_rule(tree): order = tree.order() if order in new_rule.orders: return new_rule.storage[tree] * tree.symmetry() # TODO: Move the correction by symmetry to initialisation. else: result = LinearCombination() for tree1, tree2 in tree_tuples_of_order(order): result += \ Fraction(a(tree1) * b(tree2), tree1.symmetry() * tree2.symmetry()) * \ tree_commutator(tree1, tree2) new_rule.orders.add(order) new_rule.storage += result return new_rule.storage[tree] * tree.symmetry() new_rule.storage = LinearCombination() new_rule.orders = set( (0, )) # the coefficient of the empty tree is always 0. return BseriesRule(new_rule)
def phi(self): def rule(tree): 'elementary weight' if tree == empty_tree: return 1 # We haven't even allowed for non-consistent RK-methods. return np.dot(self.b, self.g_vector(tree))[0] return BseriesRule(rule)
def stepsize_adjustment(a, A): """Corresponds to letting h -> A h.""" base_rule = a._call def new_rule(tree): return A**tree.order() * base_rule(tree) return BseriesRule(new_rule)
def adjoint(a): """The adjoint is the inverse with reversed time step. Returns a BseriesRule. """ def new_rule(tree): return (-1)**tree.order() * inverse(a)(tree) return BseriesRule(new_rule)
def inverse(a): r"""Return the inverse of *a* in the Butcher group. The returned BseriesRule is calculated as :math:`\left(a \circ S\right)(\tau)`, where :math:`S` denotes the antipode. """ # TODO: Test that a is of the right kind. @memoized def new_rule(tree): return a(antipode_ck(tree)) return BseriesRule(new_rule)
def conjugate_by_commutator(a, c): """The conjugate of ``a`` with change of coordinates ``c``. Calculated using the commutator. """ def new_rule(tree): if tree == empty_tree: return 0 tmp = a result = 0 for n in range(tree.order() + 1): result += Fraction((-1)**n, factorial(n)) * tmp(tree) tmp = series_commutator(c, tmp) return result return BseriesRule(new_rule)
def test_it(self): method = RKimplicitMidpoint.phi() result = conjugate_to_symplectic(method) self.assertEqual(4, result) method = RKlobattoIIIA4.phi() result = conjugate_to_symplectic(method) self.assertEqual(6, result) method = RKlobattoIIIB4.phi() result = conjugate_to_symplectic(method) self.assertEqual(4, result) method = BseriesRule(_kahan) result = conjugate_to_symplectic(method, quadratic_vectorfield=True) self.assertEqual(4, result)
def composition(a, b): r"""Composition of methods, b after a. Return the composition :math:`a \circ b`. The returned object is a :class:`BseriesRule` if :math:`a(\emptyset) = 1` and a :class:`ForestRule` otherwise. """ @memoized def new_rule(arg): # arg is tree or forest. result = 0 for pair, multiplicity in subtrees(arg).items(): result += a(pair[0]) * b(pair[1]) * multiplicity return result if a(empty_tree) != 1: return ForestRule(new_rule) else: return BseriesRule(new_rule)
def composition_ssa(a, b): """Same as :func:`composition`, except that it halves the stepsize afterwards. Equivalent to ``stepsize_adjustment(composition(a,b),Fraction(1, 2))``. """ if a(empty_tree) != 1: raise ValueError( 'Composition can only be performed on consistent B-series.') @memoized def new_rule(tree): sub_trees = subtrees(tree) result = 0 for pair, multiplicity in sub_trees.items(): result += a(pair[0]) * b(pair[1]) * multiplicity return Fraction(result, 2**tree.order()) return BseriesRule(new_rule)
def hf_composition(a): r"""The composition :math:`a b`, where :math:`b` is the B-series representing the vector field corresponding to the exact solution. That is :math:`b = \delta_{\circ}`. """ # TODO: include some trees in the sphinx docs. if a(empty_tree) != 1: raise ValueError( 'Composition can only be performed on consistent B-series.') def new_rule(tree): if tree == empty_tree: return 0 else: result = 1 for subtree, multiplicity in tree.items(): result *= a(subtree)**multiplicity return result return BseriesRule(new_rule)
def exp(a, quadratic_vectorfield=False): """The exponential. If ``a`` is the rule for the B-serie of some modified equation, it returns the B-series rule for the numerical method. """ if a(empty_tree) != 0: raise ValueError('Can not calculate the exponential for this rule.') @memoized def new_rule(tree): if tree == empty_tree: return 1 result = a(tree) b = a for n in range(2, tree.order() + 1): b = composition(b, a) result += Fraction(b(tree), factorial(n)) return result result = BseriesRule(new_rule) if quadratic_vectorfield: result = remove_non_binary(result) return result
del tmp print('let t =', str(t) + ', then:') print('order(t) = ', order(t)) print('number_of_children(t) = ', number_of_children(t)) print('density(t) = ', density(t)) print('symmetry(t) = ', symmetry(t)) print('alpha(t) =', alpha(t)) print('F(t) = "' + F(t) + '" (A string)') print('t.multiplicities() =', t.multiplicities(), '(A, list)') print("") print('Tree commutator: treeCommutator(t, ButcherTree.basetree()) = ', treeCommutator(t, ButcherTree.basetree())) print('Splitting: split(tree) = ', split(tree)) print("") print('B-series:') a = BseriesRule('exact') print("Let a = BseriesRule('exact')") b = hf_composition(a) print("Then b = hf_composition(a) gives:") b = hf_composition(a) tmp = treeGenerator() tmp_tree = tmp.next() print(tmp_tree, ', b = ', b(tmp_tree)) tmp_tree = tmp.next() print(tmp_tree, ', b = ', b(tmp_tree)) tmp_tree = tmp.next() print(tmp_tree, ', b = ', b(tmp_tree)) tmp_tree = tmp.next() print(tmp_tree, ', b = ', b(tmp_tree)) c = modifiedEquation(a)
def test_sum_series(self): thesum = LinearCombination() thesum += leaf rule1 = BseriesRule(thesum) self.assertEqual(0, rule1(empty_tree)) self.assertEqual(1, rule1(leaf))
def test_zero_series(self): rule1 = BseriesRule() tree1 = leaf self.assertEqual(0, rule1(tree1))