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 algorithmsimport 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
CadabraTestError
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:
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 Raised CadabraTestError
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