a field-theory motivated approach to computer algebra


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): 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}$);
A^{\mu} + B^{\mu}
@algo(pre_order=True, pred=lambda node: == 'x') def x_to_y(node): = 'y' return result_t.changed x_to_y($x + y$);
@algo class a_to_b(Algorithm): def __init__(self, ex, a, b): Algorithm.__init__(self, ex) self.a, self.b = a, b def can_apply(self, node): return == self.a.head() def apply(self, node): = self.b.head() return result_t.changed a_to_b($s_{\mu} + t_{\mu}$, $s_{\mu}$, $t_{\mu}$);
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: == 'x') class does_not_need_pred(Algorithm): def can_apply(self,node): return =='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$);


Exception derived from AssertionError raised by testing functions when an assertion fails
class CadabraTestError(AssertionError): pass

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: pass
sort_sum_test01 passed
sort_sum_test02 FAILED
  Expected: a + b
  Produced: a + c
sort_sum_test03 FAILED
  Expected: a + b
  Produced: a + c

inherit_kernel() -> Kernel

Find a kernel in the stack.
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
Copyright © 2001-2021 Kasper Peeters