Not perfect... but helpful.
A time ago I was trying something related to your question. After a while of looking on the web (and asking... just like you!) I came out with the following: define a circular function.
Let $a$ and $b$ be noncommuting, but distributable
{a,b}::NonCommuting.
{a,b}::Distributable.
def post_process(ex):
expand_power(ex)
distribute(ex)
I define the function
def isCircular(arr1, arr2):
return arr1 in arr2+' '+arr2
and a expression to test
expr := (a + b)**2;
The trick is now to convert this to a string, split it, and compare term by term if they are circular. NOTE: blank spaces before and after the plus sign inside the split
method.
list = str(expr)
list = str(expr).split(' + ')
list;
for i in range(len(list)):
for j in range(i+1,len(list)):
if isCircular( list[i], list[j]):
list[j] = list[i]
list;
Then, convert it back to a cadabra expression to finally simplify. NOTE: the newexpr
is initalised as an empty expression, it is not a single double quote, but double single quotes.
newexpr = '';
for word in list:
newexpr = newexpr + ' + ' + word
;
cab_expr = Ex(newexpr)
collect_terms(cab_expr);
I tried something like expr := (a b + b a)**2;
and the result is almost there! It could be a starting point!!!
Cheers.