Fix typo in display-html-help
[maxima.git] / src / spgcd.lisp
blobdca1026a8fa619028ad095b561f72e92d515f925
1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
4 ;;; ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; *****************************************************************
9 ;;; ***** SPGCD ******* Sparse polynomial routines for GCD **********
10 ;;; *****************************************************************
11 ;;; ** (C) COPYRIGHT 1982 MASSACHUSETTS INSTITUTE OF TECHNOLOGY *****
12 ;;; ****** THIS IS A READ-ONLY FILE! (ALL WRITES RESERVED) **********
13 ;;; *****************************************************************
15 (in-package :maxima)
17 (macsyma-module spgcd)
19 (declare-top (special *which-factor*))
21 (load-macsyma-macros ratmac)
23 (defmvar $pointbound *alpha)
25 (defmacro len (lobj) `(cadr ,lobj))
27 (defmacro skel (lobj) `(caddr ,lobj))
29 (defmacro matrix (lobj) `(cadddr ,lobj))
31 (defmacro currow (lobj) `(cadr (cdddr ,lobj)))
33 (defmacro badrows (lobj) `(cadr (cddddr ,lobj)))
35 (defun pinterp (x pts vals)
36 (do ((u (car vals)
37 (pplus u (ptimes
38 (pctimes (crecip (pcsubstz (car xk) qk))
39 qk)
40 (pdifference (car uk)
41 (pcsubstz (car xk) u)))))
42 (uk (cdr vals) (cdr uk))
43 (qk (list x 1 1 0 (cminus (car pts)))
44 (ptimes qk (list x 1 1 0 (cminus (car xk)))))
45 (xk (cdr pts) (cdr xk)))
46 ((null xk) u)))
48 (defun pcsubstz (val p)
49 (if (pcoefp p) p
50 (do ((l (p-terms p) (pt-red l))
51 (ans 0
52 (ctimes
53 (cplus ans (pt-lc l))
54 (cexpt val (- (pt-le l) (pt-le (pt-red l)))))))
55 ((null (pt-red l))
56 (ctimes
57 (cplus ans (pt-lc l))
58 (if (zerop (pt-le l))
60 (cexpt val (pt-le l))))))))
62 ;;skeletons consist of list of exponent vectors
63 ;;lifting structures are as follows:
65 ;;(matrix <n> ;length of <skel>
66 ;; <skel> ;skeleton of poly being lifted
67 ;; <matrix> ; partially diagonalized matrix used to obtain sol.
68 ;; <row> ;we have diagonalized this far
69 ;; <bad rows>) ; after we get
71 (defun eval-mon (mon pt)
72 (do ((l mon (cdr l))
73 (ll pt (cdr ll))
74 (ans 1 (ctimes
75 (if (zerop (car l)) 1
76 (cexpt (car ll) (car l)))
77 ans)))
78 ((null l) ans)))
80 ;; ONE-STEP causes the (row,col) element of mat to be made to be zero. It is
81 ;; assumed that row > col, and that the matrix is diagonal above the row.
82 ;; n indicates how wide the rows are suppoded to be.
84 (defun one-step (mat row col n)
85 (do ((i col (1+ i))
86 (c (aref mat row col))) ;Got to save this away before it is zeroed
87 ((> i n) mat)
88 (setf (aref mat row i)
89 (cdifference (aref mat row i)
90 (ctimes c (aref mat col i))))))
92 ;; MONICIZE-ROW assumes the (row,row) element is non-zero and that this element
93 ;; is to be made equal to one. It merely divides the entire row by the
94 ;; (row,row) element. It is assumed that the (row,n) elements with n < row are
95 ;; already zero. Again n is the width of the rows.
97 (defun monicize-row (mat row n)
98 (do ((i n (1- i))
99 (c (crecip (aref mat row row))))
100 ((= i row)
101 (setf (aref mat row row) 1)) ;Don't bother doing the division on the diagonal
102 (setf (aref mat row i) (ctimes (aref mat row i) c))))
104 ;; FILL-ROW is given a point and the value of the skeleton at the point and
105 ;; fills the appropriate row in the matrix. with the values of the monomials
106 ;; n is the length of the skeleton
108 (defun fill-row (skel mat n row pt val)
109 (do ((i 0 (1+ i))
110 (l skel (cdr l)))
111 ((= i n) ;l should be nil now,
112 (if (not (null l)) ;but lets check just to make sure
113 (merror "FILL-ROW: skeleton too long: ~S"
114 (list n '= skel)))
115 (setf (aref mat row n) val)) ;The value is stored in the last column
116 (setf (aref mat row i)
117 (eval-mon (car l) pt)))) ;Should think about a better
118 ;way to do this evaluation.
120 #-allegro
121 (defun swap-rows (mat m n) ;Interchange row m and n
122 (do ((k 0 (1+ k))
123 (l (array-dimension mat 0)))
124 ((> k l) mat)
125 (setf (aref mat m k)
126 (prog1
127 (aref mat n k)
128 (setf (aref mat n k) (aref mat m k))))))
130 ;; PARTIAL-DIAG fills in one more row of the matrix and tries to diagonalize
131 ;; what it has so far. If the row which has just been introduced has a
132 ;; zero element on the diagonal then it is scanned for its first non-zero
133 ;; element and it is placed in the matrix so that the non-zero element is
134 ;; on the diagonal. The rows which are missing are added to the list in
135 ;; badrows. The current row pointer may skip a couple of numbers here, so
136 ;; when it is equal to n, the only empty rows to add thing to are on the
137 ;; bad rows list.
139 (defun partial-diag (lobj pt val) ; Does one step of diagonalization of
140 (let ((n (len lobj)) ;the matrix in lobj
141 (skel (skel lobj))
142 (mat (matrix lobj)) ;The matrix, obviously
143 (row (currow lobj)) ;This is the row which is to be
144 ;introduced
145 (badrows (badrows lobj)) ;Rows which could not be used when
146 ;their time came, and will be used later
147 crow)
148 (cond ((= row n) ;All rows already done, must start
149 ;using the rows in badrows.
150 (fill-row skel mat n (setq crow (car badrows)) pt val))
151 ((fill-row skel mat n row pt val) ;Fill up the data
152 (setq crow row)))
154 ;; This loop kills all the elements in the row up to the diagonal.
156 (do ((i 0 (1+ i)) ;runs over the columns of mat
157 (l (setq badrows (cons nil badrows)))
158 (flag))
159 ((= i crow)
160 (setq badrows (cdr badrows))) ;update badrows
161 (if (cdr l) ;Any bad rows around?
162 (if (= (cadr l) i) ;No one for this row,
163 (if (and (null flag) ;So if this guy can fill in
164 (not (zerop (aref mat crow i))))
165 (progn
166 (swap-rows mat crow i) ;Put this guy in the right spot
167 (rplacd l (cddr l))
168 (setq flag t crow i)) ; and make him a winner
169 (setq l (cdr l))) ;At any rate this guy isn't
170 ;used any more.
171 (one-step mat crow i n))
172 (one-step mat crow i n)))
174 (if (zerop (aref mat crow crow)) ;diagonal is zero?
175 (setq badrows (cons crow badrows))
176 (progn
177 (monicize-row mat crow n) ;Monicize the diagonal element on this
178 ;row
179 (do ((j 0 (1+ j))) ;For each element in the rows above
180 ;this one zero the the crow column
181 ((= j crow)) ;Don't zero the diagonal element
182 (one-step mat j crow n))))
183 (cond ((and (= (1+ row) n)
184 (progn (setq row (1- row)) t) ;Decrement it just in case
185 (null (cdr badrows)))
186 (do ((l nil (cons (aref mat i n) l))
187 (i (1- n) (1- i)))
188 ((< i 0)
189 (list 'solution n skel mat l))))
190 (t (list 'matrix n skel mat (1+ row) badrows)))))
192 (defun gen-point (vars)
193 (do ((l vars (cdr l))
194 (ans nil (cons (cmod (random $pointbound)) ans)))
195 ((null l) ans)))
197 ;; PDIAG-ALL introduces a new row in each matrix in the list of l-lobjs.
198 ;; The RHS of the equations is stripped off of poly.
200 (defun pdiag-all (l-lobjs poly pt)
201 (do ((l l-lobjs (cdr l))
202 (lp (p-terms poly))
203 (solved t) (c))
204 ((null l)
205 (if solved (cons 'solved l-lobjs)
206 l-lobjs))
207 (if (and lp (= (caar l) (pt-le lp))) ;This term corresponds to the
208 ;current lobj, set c to the coefficient
209 (setq c (pt-lc lp) lp (pt-red lp))
210 (setq c 0))
211 ;;FIXTHIS ;Should put it some check for extra
212 ;terms in the polynomial
213 (if (not (eq (cadar l) 'solution))
214 (progn (rplacd (car l)
215 (partial-diag (cdar l) pt c))
216 (and solved (null (eq (cadar l) 'solution))
217 (setq solved nil))))))
219 ;; not currently called
220 ;; (defun CREATE-INTVECT (h)
221 ;; (do ((l (cdr h) (cddr l))
222 ;; (ans nil (cons (list (car l) (cadr l))
223 ;; ans)))
224 ;; ((null l)
225 ;; (nreverse ans))))
227 ;; (defun MERGE-INTVECT (iv h)
228 ;; (do ((l iv (cdr l))
229 ;; (h (cdr h)))
230 ;; ((null l) iv)
231 ;; (cond ((or (null h) (> (caar l) (car h)))
232 ;; (rplacd (car l) (cons 0 (cdar l))))
233 ;; ((= (caar l) (car h))
234 ;; (rplacd (car l) (cons (cadr h) (cdar l)))
235 ;; (setq h (cddr h)))
236 ;; (t (error '|Bad Evaluation point - MERGE-INTVECT|)))))
239 (defun merge-skel (mon poly)
240 (cond ((pcoefp poly)
241 (list (cons 0 mon)))
242 ((do ((l (cdr poly) (cddr l))
243 (ans nil
244 (cons (cons (car l) mon) ans)))
245 ((null l) ans)))))
247 (defun new-skel (skel polys)
248 (list
249 (mapcan #'(lambda (mon poly) (merge-skel mon poly)) skel polys)
250 (mapcan #'(lambda (q)
251 (cond ((pcoefp q) (list q))
252 ((do ((l (cdr q) (cddr l))
253 (ans nil (cons (cadr l) ans)))
254 ((null l) ans)))))
255 polys)))
257 (defun create-lobjs (prev-lift)
258 (mapcar #'(lambda (q)
259 (let ((n (length (cadr q))))
260 (cons (car q)
261 (list 'matrix n (cadr q)
262 (make-array (list n (1+ n)) :initial-element 0 :element-type 'fixnum)
263 0 nil))))
264 prev-lift))
266 (defun clear-lobjs (lobjs)
267 (mapcar #'(lambda (q)
268 (cons (car q)
269 (list 'matrix (caddr q) (cadddr q)
270 (caddr (cddr q)) 0 nil)))
271 lobjs))
273 (defun sparse-lift (c f g l-lobjs vars)
274 (do ((pt (gen-point vars) (gen-point vars))
275 (gcd))
276 ((eq (car l-lobjs) 'solved)
277 (cdr l-lobjs))
278 (setq gcd (lifting-factors-image
279 (pcsub c pt vars) (pcsub f pt vars) (pcsub g pt vars)))
280 (if (or (pcoefp gcd)
281 (not (= (pt-le (p-terms gcd)) (caar l-lobjs))))
282 (throw 'bad-point nil)
283 (setq l-lobjs (pdiag-all l-lobjs gcd pt)))))
285 (defun lifting-factors-image (c f g)
286 (let ((gcd (pgcdu f g)))
287 (case *which-factor*
288 (1 (pctimes c gcd))
289 (2 (pquotient f gcd))
290 (3 (pquotient g gcd)))))
292 (defun zgcd-lift* (c f g vars degb)
293 (do ((vals (gen-point vars) (gen-point vars))
294 (ans))
295 ((not (null ans))
296 ans)
297 (setq ans
298 (catch 'bad-point
299 (zgcd-lift c f g vars vals degb)))))
301 ;; ZGCD-LIFT returns objects called lifts. These have the the following
302 ;; structure
303 ;; ((n <skel> <poly>) ... )
304 ;; n corresponds to the degree in the main variable to which this guy
305 ;; corresponds.
307 (defun zgcd-lift (c f g vars vals degb)
308 (cond ((null vars) ;No variables left, just the main one
309 (let ((p (lifting-factors-image c f g))) ;Compute factor and
310 ;enforce leading coefficient
311 (if (pcoefp p) (throw 'relprime 1) ;if the GCD is 1 quit
312 (do ((l (p-terms p) (pt-red l)) ;otherwise march
313 ;though the polynomial
314 (ans nil ;constructing a lift for each term.
315 (cons (list (pt-le l) '(nil) (list (pt-lc l)))
316 ans)))
317 ((null l)
318 (nreverse ans))))))
319 ((let ((prev-lift ;Recurse if possible
320 (zgcd-lift (pcsubsty (car vals) (car vars) c)
321 (pcsubsty (car vals) (car vars) f)
322 (pcsubsty (car vals) (car vars) g)
323 (cdr vars) (cdr vals) (cdr degb))))
324 (do ((i 0 (1+ i)) ;counts to the degree bound
325 (lobjs (create-lobjs prev-lift) ;need to create
326 ;the appropriate matrices
327 (clear-lobjs lobjs)) ;but reuse them at each
328 ;step
329 (pts (add-point (list (car vals))) ;List of random
330 (add-point pts)) ;points
331 (linsols (mapcar 'make-linsols prev-lift)
332 (merge-sol-lin
333 linsols
334 (sparse-lift
335 (pcsubsty (car pts) (car vars) c)
336 (pcsubsty (car pts) (car vars) f)
337 (pcsubsty (car pts) (car vars) g)
338 lobjs (cdr vars)))))
339 ((= i (car degb))
340 (interp-polys linsols (cdr pts) (car vars))))))))
342 (defun make-linsols (prev-lift)
343 (list (car prev-lift)
344 (cadr prev-lift)
345 (mapcan #'(lambda (q)
346 (cond ((pcoefp q) (list (list q)))
347 (t (do ((l (p-terms q) (pt-red l))
348 (ans nil (cons (list (pt-lc l)) ans)))
349 ((null l) ans)))))
350 (caddr prev-lift))))
352 (defun add-point (l)
353 (do ((try (cmod (random $pointbound))
354 (cmod (random $pointbound))))
355 ((null (member try l :test #'equal))
356 (cons try l))))
358 (defun merge-sol-lin (l1 l2)
359 (do ((l l1 (cdr l))
360 (l2 l2 (cdr l2)))
361 ((null l) l1)
362 (cond ((= (caar l) (caar l2))
363 (rplaca (cddar l)
364 (mapcar 'cons (cadddr (cddar l2)) (caddar l)))))))
366 (defun interp-polys (l pts var)
367 (mapcar #'(lambda (q)
368 (cons (car q)
369 (new-skel
370 (cadr q)
371 (mapcar #'(lambda (r) (pinterp var pts r))
372 (caddr q)))))
375 (defun zgcd (f g &aux $algebraic algfac*)
376 (let ((f (oldcontent f)) ;This is a good spot to
377 (g (oldcontent g)) ;initialize random
378 (gcd) (mon)
379 (*which-factor*))
380 ;; *WHICH-FACTOR* is interpreted as follows. It is set fairly deep in the
381 ;; algorithm, inside ZGCD1.
382 ;; 1 -> Lift the GCD
383 ;; 2 -> Lift the cofactor of F
384 ;; 3 -> Lift the cofactor of G
387 (setq mon (pgcd (car f) (car g)) ;compute GCD of content
388 f (cadr f) g (cadr g)) ;f and g are now primitive
389 (if (or (pcoefp f) (pcoefp g)
390 (not (eq (car f) (car g))))
391 (merror "ZGCD: incorrect arguments."))
392 (ptimes mon
393 (do ((test))
394 (nil)
395 (setq gcd (catch 'relprime (zgcd1 f g)))
396 (setq test
397 (case *which-factor*
398 (1 (testdivide f gcd))
399 (2 (testdivide f gcd))
400 (3 (testdivide g gcd))))
401 (cond ((not (null test))
402 (return
403 (cond ((equal *which-factor* 1)
404 gcd)
405 (t test))))
406 ((not (null modulus))
407 (return (let (($gcd '$red))
408 (pgcd f g)))))))))
410 (defun zgcd1 (f g)
411 (let* ((modulus modulus)
412 first-lift
413 h degb c
414 (vars (sort (union1 (listovars f) (listovars g))
415 #'pointergp))
416 (genvar (reverse vars))
418 ;; the elements of the following degree vectors all correspond to the
419 ;; contents of var. Thus there may be variables missing that are in
420 ;;GENVAR.
421 ;; (f-degv (zpdegreevector f vars)) ;;WHY NOT JUST PUT GENVAR THE REVERSE
422 ;; (g-degv (zpdegreevector g vars)) ;;THE REVERSE OF VARS AND JUST USE PDEGREEVECTOR--wfs
423 (f-degv (pdegreevector f))
424 (g-degv (pdegreevector g))
425 (gcd-degv (gcd-degree-vector f g vars)))
427 ;; First we try to decide which of the gcd and the cofactors of f and g
428 ;; is smallest. The result of this decision is indicated by *which-factor*.
429 ;; Then the leading coefficient that is to be enforced is changed if a
430 ;; cofactor has been chosen.
431 (case (setq *which-factor*
432 (determine-lifting-factor f-degv g-degv gcd-degv))
433 (1 (setq c (pgcd (p-lc f) (p-lc g))))
434 (2 (setq c (p-lc f)))
435 (3 (setq c (p-lc g))))
437 ;; Next a degree bound must be chosen.
438 (setq degb
439 (reverse
440 (mapcar #'+
441 (case *which-factor*
442 (1 gcd-degv)
443 (2 (mapcar #'- f-degv gcd-degv))
444 (3 (mapcar #'- g-degv gcd-degv)))
445 (zpdegreevector c vars))))
447 (cond ((not (null modulus))
448 (lobj->poly (car vars) (cdr vars)
449 (zgcd-lift* c f g (cdr vars) (cdr degb))))
451 (setq h (* (maxcoefficient f)
452 (maxcoefficient g)))
453 (setq modulus *alpha) ;Really should randomize
454 (setq first-lift
455 (zgcd-lift* (pmod c) (pmod f) (pmod g)
456 (cdr vars) (cdr degb)))
457 (do ((linsols (mapcar #'(lambda (q)
458 (cons (car q)
459 (new-skel (cadr q) (caddr q))))
460 first-lift)
461 (merge-sol-lin-z linsols
462 (sparse-lift cm fm gm lobjs (cdr vars))
463 (* coef-bound
464 (crecip (cmod coef-bound)))
465 (* modulus coef-bound)))
466 (lobjs (create-lobjs first-lift)
467 (clear-lobjs lobjs))
468 (coef-bound *alpha (* modulus coef-bound))
469 (cm) (fm) (gm))
470 ((> coef-bound h)
471 (setq modulus nil)
472 (lobj->poly (car vars) (cdr vars) linsols))
473 (setq modulus (newprime modulus))
474 (setq cm (pmod c)
475 fm (pmod f)
476 gm (pmod g)))))))
478 (defun lobj->poly (var vars lobj)
479 (primpart
480 (cons var
481 (mapcan
482 #'(lambda (q)
483 (list (car q)
484 (do ((x (cadr q) (cdr x))
485 (y (caddr q) (cdr y))
486 (ans 0
487 (pplus ans
488 (disrep-monom (cdar x) (car y)
489 vars))))
490 ((null x) ans))))
491 lobj))))
493 (defun disrep-monom (monom c vars)
494 (cond ((null monom) c)
495 ((equal 0 (car monom))
496 (disrep-monom (cdr monom) c (cdr vars)))
497 ((list (car vars) (car monom)
498 (disrep-monom (cdr monom) c (cdr vars))))))
500 (defun merge-sol-lin-z (l1 l2 c new-coef-bound)
501 (do ((l l1 (cdr l))
502 (l2 l2 (cdr l2))
503 (modulus new-coef-bound)
504 (n))
505 ((null l) l1)
506 (cond ((= (caar l) (caar l2))
507 (rplaca (cddar l)
508 (mapcar
509 #'(lambda (a b)
510 (cond ((> (abs (setq n (cplus b (ctimes c (cdifference a b)))))
511 new-coef-bound)
512 (throw 'relprime 1))
513 (n)))
514 (cadddr (cddar l2)) (caddar l)))))))
516 ;; The following function tries to determine the degree of gcd(f, g) in each
517 ;; variable. This is done in the following manner: All but one of the
518 ;; variables in f and g are replaced by randomly chosen integers. The
519 ;; resulting polynomials are called f* and g*. The degree of gcd(f*, g*) is
520 ;; used as the degree of gcd(f, g) in that variable.
522 ;; The univariate gcd's are computed with modulus=*alpha.
524 (defun gcd-degree-vector (f g vars)
525 (let ((modulus *alpha))
526 (setq f (pmod f) g (pmod g))
527 (do ((vn (cdr vars) (cdr vn))
528 (vs (delete (car vars) (copy-list vars) :test #'equal)
529 (delete (car vn) (copy-list vars) :test #'equal))
530 (l) (f*) (g*) (gcd*) (rand))
531 (nil)
532 (setq rand (gen-point vs))
533 (setq f* (pcsub f rand vs)
534 g* (pcsub g rand vs))
535 (cond ((or (pcoefp f*) (pcoefp g*)
536 (pcoefp (setq gcd* (pgcdu f* g*))))
537 (push 0 l))
538 (t (push (pt-le (p-terms gcd*)) l)))
539 (cond ((null vn)
540 (return l)))))) ;No reverse needed here
542 ;; DETERMINE-LIFTING-FACTOR returns a number indicating which factor of f or g
543 ;; to which to lift
545 (defun dlf-mumblify (a b)
546 (loop for x in a for y in b sum (- x y)))
548 (defun determine-lifting-factor (f-degv g-degv gcd-degv)
549 (let* ((fv (dlf-mumblify f-degv gcd-degv))
550 (gv (dlf-mumblify g-degv gcd-degv))
551 (gcdv (apply '+ gcd-degv)))
552 (if (< fv gcdv)
553 (if (< fv gv) 2 3)
554 (if (< gv gcdv) 3 1))))
556 (defun excise-extra-variables (degv vars)
557 (do ((l (reverse degv) (cdr l))
558 (lv (reverse genvar) (cdr lv))
559 (ndegv))
560 ((null l)
561 ndegv)
562 (cond ((eq (car lv) (car vars))
563 (push (car l) ndegv)
564 (setq vars (cdr vars))))))
566 (defun zpdegreevector (p vars)
567 (excise-extra-variables (pdegreevector p) vars))
569 ;; Local Modes:
570 ;; Mode:LISP
571 ;; Fill Column:76
572 ;; Auto Fill Mode:1
573 ;; Comment Column:40
574 ;; END: