4 (nvars 0 :type integer
)
5 (ncosets 0 :type integer
)
8 (subgroup-generators nil
)
11 (defvar $todd_coxeter_state
)
13 ;; To turn on debug printing set to T
16 ;; When *debug* is not nil, this holds the multiplications for
18 (defvar *this-row
* nil
)
20 (deftype coset nil
'integer
)
22 ;; The data type we use to enumerate cosets.
24 (defvar *todo
* (make-array 10 :element-type
'coset
:fill-pointer
0 :adjustable t
:initial-element
0))
26 (defmacro with-multiply-table
(&body body
)
27 `(let ((nvars (tc-state-nvars $todd_coxeter_state
))
28 (multiply-table (tc-state-multiply-table $todd_coxeter_state
)))
29 (declare (type (vector t
) multiply-table
))
35 ;; Multiply coset K times variable R
36 (defmacro tc-mult
(k r
)
37 `(the coset
(aref (table ,r
) ,k
)))
39 ;; Force k . r = s and k = s . r^-1
40 (defmacro define-tc-mult
(k r s
)
42 (setf (tc-mult ,k
,r
) ,s
)
43 (setf (tc-mult ,s
(- ,r
)) ,k
)))
45 ;; cosets M < N are to be made equal
46 (defmacro push-todo
(m n
)
48 (vector-push-extend ,m
*todo
*)
49 (vector-push-extend ,n
*todo
*)))
51 ;; The multiplication table for variable i
52 ;; (can only be used inside with-multiply-table)
54 `(the (vector (coset)) (aref multiply-table
(+ ,i nvars
))))
57 ;; NVARS is the number of of variables. It should be the maximum
58 ;; of the absolute values of the entries in the relations RELS.
59 ;; The format of the relations is variables X,Y,.. correspond to
60 ;; numbers 1,2,.. and X^^-1, Y^^-1 .. are -1,-2,... RELS is
61 ;; a list of lists in these variables.
62 ;; Thus rels = '((1 -2 -1) (2 2 3) ..) (ie [x1.x2^^-1 . x1^^-1, x2.x2.x3,.. ))
63 ;; SUBGP is also a list of lists.
64 ;; Returns order of G/H, where G is Free/(rels), and H is
65 ;; This is the main entry point at lisp level.
66 ;; Example: (TODD-COXETER 2 '((1 1) (1 2 1 2 1 2) (2 2)))
67 ;; returns 6. In (tc-state-multiply-table $todd_coxeter_state) we find the current
68 ;; state of the action of the variables on the cosets G/H.
69 ;; For computing the symmetric group using the relations
70 ;; p(i,j) :=concat(x,i).concat(x,j);
71 ;; symet(n):=create_list(if (j - i) = 1 then (p(i,j))^^3 else
72 ;; if (not i = j) then (p(i,j))^^2 else p(i,i) , j,1,n-1,i,1,j);
73 ;; todd_coxeter(symet(n)) == n!
74 ;; the running time of the first version of this code is observed to be quadratic
75 ;; in the number of cosets. On a rios it is approx 5*10^-5 * (ncosets)^2.
77 (defun todd-coxeter (nvars rels subgp
&aux
(i 1) (c 0))
78 (set-up nvars rels subgp
)
79 (loop while
(>= (tc-state-ncosets $todd_coxeter_state
) i
)
80 do
(incf c
) ;; count how many row tries..
81 (cond ((doing-row i
) ;; row still being done
82 (replace-coset-in-multiply-table))
83 ((> (fill-pointer *todo
*) 0) ;; row finished but there is work to do
85 (replace-coset-in-multiply-table))
86 (t ;; row finished -- no work
88 (format t
"~%Rows tried ~d~%" c
)
89 (tc-state-ncosets $todd_coxeter_state
))
91 ;; Store the data in $todd_coxeter_state, and build multiply-table.
92 (defun set-up (nvars rels subgp
)
93 (setf (fill-pointer *todo
*) 0)
94 (setf $todd_coxeter_state
(make-tc-state :nvars nvars
97 :subgroup-generators subgp
98 :row1-relations
(append subgp rels
)
99 :multiply-table
(make-array (1+ (* 2 nvars
)))))
102 (loop for rel in
(tc-state-row1-relations $todd_coxeter_state
) do
104 do
(unless (<= 1 (abs v
) nvars
)
105 (error "Vars must be integers with absolute value between 1 and ~d" nvars
))))
106 (loop for i from
(- nvars
) to nvars
108 do
(setf (table i
) (make-array 10 :adjustable t
:element-type
'coset
:initial-element
0)))))
110 ;; Starts multiplying coset i times the relations. Basic fact is i . rel = i.
111 ;; This gives a condition on the multiplication table. Once we have made it all
112 ;; the way through the relations for a given coset i, and NOT had any
113 ;; inconsistency in our current multiplication table, then we go on the the next
114 ;; coset. The coset 1 denotes H. so for generators h of H we we have 1 . h = 1.
115 ;; So when we do row 1, we add to the relations the generators of H.
117 ;; When we do find an inconsistency eg: 7 . y = 1 and 4 . y = 1 or 7 = 1 . y^^-1
118 ;; and 4 . y = 1, then we would know that 4 and 7 represent the same coset, and
119 ;; so we put 4 and 7 in the *todo* vector and return t so that
120 ;; replace-coset-in-multiply-table will identify them. While we are running
121 ;; inside doing-row, the multiply-table is accurate, up to our current state of
122 ;; knowledge. Note that once we find such a nonpermutation action of y, we could
123 ;; not maintain the consistency of (table i) and (table -i). We exit doing-row
124 ;; with value t, to indicate replacements should be done, and that we must
125 ;; return to complete row i. (Actually we return t even in the case we were
126 ;; finished the row and found the duplicate in the last step).
128 (defun doing-row (i &aux
(j 0) (k 0) (r 0)(s 0) *this-row
* relations
)
129 (setf relations
(if (eql i
1)
130 (tc-state-row1-relations $todd_coxeter_state
)
131 (tc-state-relations $todd_coxeter_state
)))
133 (loop for rel in relations
140 (setq s
(tc-mult k r
))
143 (setq s
(next-coset))
144 (define-tc-mult k r s
))
145 (t (setq s
(tc-mult i
(- r
)))
146 (cond ((undef s
) (define-tc-mult k r i
))
147 ((< k s
) (push-todo k s
)(return-from doing-row
(cdr v
)))
148 ((> k s
) (push-todo s k
)(return-from doing-row
(cdr v
))))
150 (cond ((setq rel
(cdr rel
))
153 (my-print (reverse *this-row
*) i
))
157 (push-todo i s
) (return-from doing-row
(cdr v
)))
159 (push-todo s i
) (return-from doing-row
(cdr v
)))
160 (t ;rel is exhausted and it matched
164 (my-print (reverse *this-row
*) i
))
167 ;; FILL-IN-INVERSES not only completes the (table i) for i < 0
168 ;; but at the same time checks that (table i) for i > 0
169 ;; does not have repeats. eg if 5 . y = 3 and 7 . y = 3,
170 ;; then this would show up when we go to build the inverse.
171 ;; if it does we add 5 and 7 to the *todo* vector.
173 (defun fill-in-inverses (&aux
(s 0) (sp 0))
175 (loop for i from
1 to nvars
176 do
(let ((ta1 (table i
))
178 (declare (type (vector (coset)) ta1 ta2
))
179 (loop for j from
1 to
(tc-state-ncosets $todd_coxeter_state
) do
180 (setf (aref ta2 j
) 0))
181 (loop for j from
1 to
(tc-state-ncosets $todd_coxeter_state
) do
182 (setf s
(aref ta1 j
))
185 (setf sp
(aref ta2 s
))
186 (cond ((eql 0 sp
) (setf (aref ta2 s
) j
))
187 (t ;; there's a duplicate!
189 (return-from fill-in-inverses t
))))))))
191 ;; set n (vector-pop *todo*) , m (vector-pop *todo*)
192 ;; and replace n by m in multiply-table and in *todo*.
193 ;; The replacement is done carefully so as not to lose ANY
194 ;; information from multiply-table, without recording it in
195 ;; *todo*. It finishes by doing FILL-IN-INVERSES which may
196 ;; in turn cause entries to be added to *todo*.
198 (defun replace-coset-in-multiply-table (&aux
(m 0) (n 0) (s 0) (s2 0) )
202 (setf n
(vector-pop *todo
*))
203 (setf m
(vector-pop *todo
*))
206 (when *debug
* (format t
" ~a --> ~a " n m
))
208 (loop for i from
1 to nvars
210 (let ((ta (table i
)))
211 (declare (type (vector (coset)) ta
))
212 (setq s2
(tc-mult n i
))
214 (setq s
(tc-mult m i
))
215 (cond ((undef s
) (setf (tc-mult m i
) s2
))
216 ((< s s2
) (push-todo s s2
))
217 ((> s s2
)(push-todo s2 s
))))
218 (loop for j downfrom
(1- n
) to
1
219 do
(setq s
(aref ta j
))
220 (cond ((> s n
) (setf (aref ta j
) (1- s
)))
221 ((eql s n
) (setf (aref ta j
) m
) )))
222 (loop for j from n below
(tc-state-ncosets $todd_coxeter_state
)
223 do
(setq s
(aref ta
(1+ j
)))
224 (cond ((> s n
) (setf (aref ta j
) (1- s
)))
225 ((eql s n
) (setf (aref ta j
) m
) )
226 (t (setf (aref ta j
) s
))))))
228 (loop for i downfrom
(1- (fill-pointer *todo
*)) to
0
229 do
(setf s
(aref *todo
* i
))
230 (cond ((> s n
) (setf (aref *todo
* i
) (1- s
)))
231 ((eql s n
)(setf (aref *todo
* i
) m
))))
232 (decf (tc-state-ncosets $todd_coxeter_state
))
235 (when (> (fill-pointer *todo
*) 0)
237 ;;(format t "~%There are now ~a cosets" (tc-state-ncosets $todd_coxeter_state))
238 ;; check for new duplicates introduced!!
239 (when (fill-in-inverses)
242 ;; Get the next coset number, making sure the multiply-table will
243 ;; have room for it, and is appropriately cleaned up.
245 (let* ((n (1+ (tc-state-ncosets $todd_coxeter_state
)))
248 (let ((ta (table 1)))
249 (unless (> (array-total-size ta
) (1+ n
))
250 (setf m
(+ n
(ash n -
1)))
251 (loop for i from
(- nvars
) to nvars
253 do
(setf ta
(table i
))
254 (setf (table i
) (adjust-array ta m
))))
255 (loop for i from
1 to nvars
256 do
(setf (aref (table i
) n
) 0)
257 (setf (aref (table (- i
)) n
) 0))))
258 (setf (tc-state-ncosets $todd_coxeter_state
) n
)))
262 ;; $todd_coxeter parses maxima args
263 ;; todd_coxeter(rels, subgrp) computes the
264 ;; order of G/H where G = Free(listofvars(rels))/subgp_generated(rels));
265 ;; and H is generated by subgp. Subgp defaults to [].
266 ;; todd_coxeter([x^^3,y.x.y^^-1 . x^^-1],[]) gives 6 the order of the symmetric group
268 ;; todd_coxeter([a^^8,b^^7,a.b.a.b,(a^^-1 . b)^^3],[a^^2, a^^-1 . b]); gives 448
270 (defmfun $todd_coxeter
(rels &optional
(subgp '((mlist))))
271 (let ((vars ($sort
($listofvars rels
)))
275 (mapcar #'(lambda (rel) (coerce-rel neg vars rel
)) (cdr rels
))
276 (mapcar #'(lambda (rel) (coerce-rel neg vars rel
)) (cdr subgp
)))))
278 (defun coerce-rel (neg vars rel
)
280 (list (* neg
(position rel vars
)))
282 (mnctimes (apply #'append
(mapcar #'(lambda (rel) (coerce-rel neg vars rel
)) (cdr rel
))))
283 (mncexpt (let* ((n (meval* (third rel
)))
285 (v (coerce-rel neg vars
(second rel
))))
286 (loop for i below
(abs (third rel
))
288 (otherwise (error "bad rel")))))
290 ;; The following functions are for debugging purposes, and
291 ;; for displaying the rows as they are computed.
293 (defvar *names
* '(nil x y z
))
295 (defun my-print (ro i
&aux relations
)
298 (format t
"Row ~a " i
)
299 (setq relations
(if (eql i
1)
300 (tc-state-row1-relations $todd_coxeter_state
)
301 (tc-state-relations $todd_coxeter_state
)))
302 (loop for rel in relations do
303 (loop for v on rel do
304 (format t
(if (> (car v
) 0) "~a" "~(~a~)")
305 (nth (abs (car v
)) *names
*))
306 (when (null ro
) (return-from my-print
))
309 (format t
"~a | ~a" i i
))))))
311 (defun has-repeat (ar &aux
(j (1+ (tc-state-ncosets $todd_coxeter_state
))) ans tem
)
312 (loop for k from
1 to
(tc-state-ncosets $todd_coxeter_state
) do
313 (setq tem
(aref ar k
))
314 (when (and (not (eql tem
0))
315 (find tem ar
:start
(1+ k
) :end j
))
319 (defun dcheck-tables (&aux tem
)
322 (loop for i from
1 to nvars
323 do
(if (setq tem
(has-repeat (table i
)))
324 (format t
"~%Table ~a has repeat ~a " i tem
))))))
326 (defun dprint-state ()
329 (format t
"~%Ncosets = ~a, *todo* = ~a" (tc-state-ncosets $todd_coxeter_state
) *todo
*)
330 (loop for i from
1 to nvars do
331 (format t
"~%~a:~a" (nth i
*names
*) (subseq (table i
) 1 (1+ (tc-state-ncosets $todd_coxeter_state
)))))
332 (my-print (reverse *this-row
*) 0))))