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