The freegroup package

This package provides some functionality for manipulating the free group on a finite list of symbols.

There is an excellent wikipedia page.

Basically, the free group~\(\left(X,\circ\right)\) on a set~\(S=\{a,b,c,...,z\}\) is the set~\(X\) of words that are things like \(c^{-4}bb^2aa^{-1}ca\). Usually one works only with words that are in reduced form, which has successive powers of the same symbol combined, in this case it would be \(c^{-4}b^3ca\); see how the \(a\) term in the middle has vanished.

The group operation is juxtaposition; thus, for example \(a^2b^{-3}c^2\circ c^{-2}ba =a^2b^{-3}c^2c^-2ba =a^2b^{-2}ba\).

A word is represented by a two-row integer matrix: The top row is the integer representation of the symbol and the second row is the corresponding power. For example, say we want to represent~\(a^2b^{-3}ac^2a^{-2}\) we would identify \(a\) as 1, \(b\) as 2, etc and write

(M <- rbind(c(1,2,3,3,1),c(2,-3,2,3,-2)))
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    2    3    3    1
#> [2,]    2   -3    2    3   -2

(see how negative entries in the second row correspond to negative powers). Then to convert to a more useful form we would have

library(freegroup)
(x <- free(M))
#> [1] a^2.b^-3.c^5.a^-2

The representation for R object x is still a two-row matrix, but the print method is active and uses a more visually appealing scheme.

We can coerce strings to free objects:

(y <- as.free("aabbbcccc"))
#> [1] a^2.b^3.c^4

The free group operation is simply justaposition, represented here by the plus symbol, “+”:

x+y
#> [1] a^2.b^-3.c^5.b^3.c^4

See how the \(a\) “cancels out” in the juxtaposition. Note that concatenation is associative. Multiplication is also defined. Suppose we want to concatenate 5 copies of x:

x*5
#> [1] a^2.b^-3.c^5.b^-3.c^5.b^-3.c^5.b^-3.c^5.b^-3.c^5.a^-2

The package is vectorized:

x*(0:3)
#> [1] 0                                   a^2.b^-3.c^5.a^-2                  
#> [3] a^2.b^-3.c^5.b^-3.c^5.a^-2          a^2.b^-3.c^5.b^-3.c^5.b^-3.c^5.a^-2

There are a few methods for creating free objects:

abc(1:9)
#> [1] a                 a.b               a.b.c             a.b.c.d          
#> [5] a.b.c.d.e         a.b.c.d.e.f       a.b.c.d.e.f.g     a.b.c.d.e.f.g.h  
#> [9] a.b.c.d.e.f.g.h.i
rfree(10,4)
#>  [1] b^-2.c^-3.b^4     b^-3.a^-3.c^-1    d^-3.c^-3         a^-3.d^-4.a^4    
#>  [5] b^2.a^2.d^-2      c^-4.b^-2         c^2               a^-3             
#>  [9] a.d^4.c^6         d^-1.b^4.d^-3.a^4

Inverses are calculated using unary or binary minus:

(u <- rfree(10,4))
#>  [1] a^3.d^2.c^-1.a^-2  d.a^4.c^-4.d^4     d.c^-2.d^-3        d^-1.c^-2.a^3.d   
#>  [5] a^-2.c^-1.a^-1.d^2 b^3.d^-3           b^-4.a^2.b^-4      b^-3.c.d^-1.a^-2  
#>  [9] c^3.a              b^-3.a^2.d^-2.c^3
-u
#>  [1] a^2.c.d^-2.a^-3    d^-4.c^4.a^-4.d^-1 d^3.c^2.d^-1       d^-1.a^-3.c^2.d   
#>  [5] d^-2.a.c.a^2       d^3.b^-3           b^4.a^-2.b^4       a^2.d.c^-1.b^3    
#>  [9] a^-1.c^-3          c^-3.d^2.a^-2.b^3
u-u
#>  [1] 0 0 0 0 0 0 0 0 0 0

We can take the “sum” of a vector of free objects simply by juxtaposing the elements:

sum(u)
#> [1] a^3.d^2.c^-1.a^-2.d.a^4.c^-4.d^5.c^-2.d^-4.c^-2.a^3.d.a^-2.c^-1.a^-1.d^2.b^3.d^-3.b^-4.a^2.b^-7.c.d^-1.a^-2.c^3.a.b^-3.a^2.d^-2.c^3

Powers are defined as per group conjugation: x^y == y^{-1}xy (or, written in additive notation, -y+x+y):

u
#>  [1] a^3.d^2.c^-1.a^-2  d.a^4.c^-4.d^4     d.c^-2.d^-3        d^-1.c^-2.a^3.d   
#>  [5] a^-2.c^-1.a^-1.d^2 b^3.d^-3           b^-4.a^2.b^-4      b^-3.c.d^-1.a^-2  
#>  [9] c^3.a              b^-3.a^2.d^-2.c^3
z <- alpha(26)
u^z
#>  [1] z^-1.a^3.d^2.c^-1.a^-2.z  z^-1.d.a^4.c^-4.d^4.z    
#>  [3] z^-1.d.c^-2.d^-3.z        z^-1.d^-1.c^-2.a^3.d.z   
#>  [5] z^-1.a^-2.c^-1.a^-1.d^2.z z^-1.b^3.d^-3.z          
#>  [7] z^-1.b^-4.a^2.b^-4.z      z^-1.b^-3.c.d^-1.a^-2.z  
#>  [9] z^-1.c^3.a.z              z^-1.b^-3.a^2.d^-2.c^3.z

Thus:

sum(u^z) == sum(u^z)
#> [1] TRUE

If we have more than 26 symbols the print method runs out of letters:

free(rbind(1:30,1))
#> [1] a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.NA.NA.NA.NA

If this is a problem (it might not be: the print method might not be important) it is possible to override the default symbol set:

options(symbols = state.abb)
free(rbind(1:50,1))
#> [1] AL.AK.AZ.AR.CA.CO.CT.DE.FL.GA.HI.ID.IL.IN.IA.KS.KY.LA.ME.MD.MA.MI.MN.MS.MO.MT.NE.NV.NH.NJ.NM.NY.NC.ND.OH.OK.OR.PA.RI.SC.SD.TN.TX.UT.VT.VA.WA.WV.WI.WY