a field-theory motivated approach to computer algebra

# cdb.utils.develop

Helper functions to aid development, debugging and testing
This package contains some standardised functionality to aid in development of library code and Python algorithms
import inspect

## class Algorithm(ex: Ex)

Base class for user defined tree-traversal algorithms

## algo(pre_order: bool = False, pred: function = (node) -> True) -> function

Decorator for creating tree-traversal algorithms
This decorator takes a function or class derived from cdb.utils.develop.Algorithm and creates a tree-traversal algorithm which iterates through an expression applying the function or apply method of a class to each node in turn. It optionally takes the arguments pre_order which determines the order of traversal of the tree (False reverts to post-order iteration) and pred which, if decorating a function, will be called at each node in the tree and the function only applied if the predicate returns true. If an Algorithm class is instead decorated, the behaviour of pred is implemented by the can_apply method which must be overloaded
@algo def switch_indices(node): "Switch positions on all indices" if node.parent_rel == parent_rel_t.sub: node.parent_rel = parent_rel_t.super return result_t.changed if node.parent_rel == parent_rel_t.super: node.parent_rel = parent_rel_t.sub return result_t.changed return result_t.unchanged # also takes optional 'deep', 'repeat' and 'depth' arguments switch_indices($A_{\mu} + B_{\mu}$); help(switch_indices)
$$\displaystyle{}A^{\mu}+B^{\mu}$$
A^{\mu} + B^{\mu}
Help on function switch_indices in module __main__:

switch_indices(ex, *, deep=True, repeat=False, depth=0)
Switch positions on all indices


@algo(pre_order=True, pred=lambda node: node.name == 'x') def x_to_y(node): node.name = 'y' return result_t.changed x_to_y($x + y$); help(x_to_y)
$$\displaystyle{}2y$$
2y
Help on function x_to_y in module __main__:

x_to_y(ex, *, deep=True, repeat=False, depth=0)


@algo class a_to_b(Algorithm): def __init__(self, ex, a, b, *args, **kwargs): Algorithm.__init__(self, ex) self.a, self.b = a, b def can_apply(self, node): return node.name == self.a.head() def apply(self, node): node.name = self.b.head() return result_t.changed a_to_b($s_{\mu} + t_{\mu}$, $s_{\mu}$, $t_{\mu}$); help(a_to_b)
$$\displaystyle{}2t_{\mu}$$
2t_{\mu}
Help on function a_to_b in module __main__:

a_to_b(ex, a, b, *args, deep=True, repeat=False, depth=0, **kwargs)


try: @algo def function_with_too_many_arguments(a,b,c): pass raise AssertionError("TypeError not raised") except TypeError: pass try: @algo class does_not_inherit_from_Algorithm: pass raise AssertionError("TypeError not raised") except TypeError: pass try: @algo(pred=lambda n: n.name == 'x') class does_not_need_pred(Algorithm): def can_apply(self,node): return n.name =='x' def apply(self,node): return result_t.unchanged raise AssertionError("ValueError not raised") except ValueError: pass

## time_algo(algo: function, ex: Ex, *args: <mixed>, iterations: int = 100) -> float

Simple function to time the execution of an algorithm with given inputs.
The arguments in *args are passed directly, but ex is copied before each invocation and so remains unmodified.
time_algo(substitute, $a + b$, $a -> b$);
0:00:00.000875
0:00:00.000493

Exception derived from AssertionError raised by testing functions when an assertion fails

## test_algo(expected: Ex, throw_on_fail: bool)

Decorator to aid defining unit tests for algorithms.
This adds the boilerplate code and adds an assert for the test.
@test_algo($a + b + c$) def sort_sum_test01(): ex := b + a + c. return sort_sum(ex) sort_sum_test01() @test_algo($a + b$) def sort_sum_test02(): ex := c + a. return sort_sum(ex) sort_sum_test02() @test_algo($a + b$, throw_on_fail=True) def sort_sum_test03(): ex := c + a. return sort_sum(ex) try: sort_sum_test03() raise AssertionError("CadabraTestError not raised!") except CadabraTestError: print("Raised CadabraTestError")
sort_sum_test01 passed
sort_sum_test02 FAILED
Expected: a + b
Produced: a + c
sort_sum_test03 FAILED
Expected: a + b
Produced: a + c

Move up stack frames until one which defines the __cdbkernel__ variable is located and return it. If no Kernel object is found then None is returned