I'm not sure if this is quite right. I can't follow all the code but I think I get the gist.
What I'm getting at is essentially this: One starts with a free monoid. This is effectively just a matrix representation
M[i,j] = a[i] . a[j]
where a is a list of operators/symbols.
this monoid tells us how to multiply our symbols. Of course, since it is free it is relatively useless but we can use it to reduce strings to binary products. e.g., a.a.a.a
It's just a "mutiplication" table. In this case we cannot reduce anything because there is no relationships between elements.
But if we have some rules/relations between elements or classes of elements then we might be able to simplify:
if a.a = a and a[3k].a[17k] = a[k]
Then allows us to reduce our string to a.a.a.a = a.a
Of course we might want the associativity law to apply which it might not in my example but.
So starting with a sort of "infinite table" which is an arbitrary "multiplication table" we can introduce "known products" and we would want the table to simplify using those products. It should simplify to the simplest representation(and maybe even check for consistency).
It seems what you have done is simply enable an arbitrary operator(such as cat, modp, matrix mul, etc).
But what I want is to be able to simplify the table given known relationships between elements.
E.g., suppose we have an indentity(which we should in monoid.
Then if a = id, M[1,j] = id.a[j] = a[j] and M[i,j] = a[i]
Then when we display the matrix our first column and row will not show a.a[j] but just a[j] and same for first row.
I do like the ability to supply arbitrary operators but that isn't really what I'm afte.
E.g., I might want M[i,i] = a[i].a[i+1]
I currently do this with something like:
matrix(n,n,(i,j)->piecewise(i = 1, a[j], j = 1, a[i], i = 3 and j = 3, a, etc....))
and this lets me fill in some of the simplifications but it's messy and I have to do all the work by hand. I also use subs and it works to some degree but it doesn't actually simplify things algorithmically by applying the rules as one would want so I might have to repeat the process many times and figure out what the rules need to be to get a total simplication of the table(e.g., closure, that is, no entry actually is a product because it's all been simplified).
i = 3 and j = 3, a,
says that a.a = a^2 = a which says that a is an idempotent. So if the 3rd element is an idempotent I can specify it this way.
In some ways I probably just just specify the generating rules and create a proc that generates all the possible combinations of rules and basically build the table from the ground up.