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