Groups defined by generators (and relators) are analogous to polynomial rings (and their quotient rings). In SymPy there are three different ways of representing polynomials: general expressions, and then dense and sparse representations. Expressions are convenient to use as they are essentially self-contained. But they are less efficient in computations while the other two are each best for specific purposes.
There could also be three types of representations for elements of groups.
Expressions would belong to a class GroupExpr
or GrpExpr
. They
could be constructed from GrpSym
's by means of GrpPow
and
GrpMul
together with GrpUnit
. There would also be methods
such as inverse
.
Moreover, group elements could be represented more efficiently as 'words', i.e., lists or tuples, over an alphabet that consists of (representatives of) generating symbols and their inverses. It seems that there are two practical alphabet implementations that are called 'syllables' and 'letters' in GAP. Syllables are essentially pairs of a symbol and an integer exponent. This allows for representing all positive and negative powers of a generator in addition to the inverse. Letters are just integers indexing the generators. Negative integers denote the inverse generators. Both of these could be implemented in SymPy.
In addition, there are different kinds of 'words' in GAP. When there are less than 128 generators, it is possible to pack a 'letter' into a single byte. However, it should be noted that GAP was conceived in a time when memory was an expensive resource. Since shuffling of bytes in a long word is not very efficient, it might be reasonable to be satisfied with standard Python lists and tuples instead of strings in a SymPy implementation.
Objects of class FreeGroup
could be constructed by giving their
generators as GrpSym
s. In mathematics, two free groups are usually
considered equal when they have the same generators. I think that
could be so also in SymPy. Representations of elements should
not be visible on this level. In fact, it seems that a single group
object could allow all three different representations.
A GrpExpr
would be considered as denoting an element of a group
whenever its GrpSym
s are among the generators. The group class
would have methods transforming these into the two other representations.
In addition, the two dense representations could be transformed into
each other.
The dense representations would each belong to their own class. (I have
not thought about their names yet.) Methods of these classes would
include __mul__
, __pow__
and inverse
. As attributes they
should probably have the group and the representation of unit.
Although the external interface to FreeGroup
would not include
representations, I think they would probably have to be taken into
account in the way the generators are given as arguments.
Mathematically, two free groups are considered equal when they have
the same generators as sets. As the 'letter' representation
obviously demands that the generators can be uniquely indexed, we
have to add some order to the generator sets. The easiest way would
probably be to just give a sequence of GrpSym
s as an argument to
FreeGroup
. Another possibility could be defining a global order
among all GrpSym
s.
The above remarks could be easily extended to non-free groups. They
would have two sets of parameters, generator GrpSym
s and
relator GrpExpr
s. The elements of such groups are mathematically
sets (called 'cosets'), but they would be represented by suitably
chosen representatives.