1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
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 ;;; *****************************************************************
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
)
38 (pctimes (crecip (pcsubstz (car xk
) qk
))
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
)))
48 (defun pcsubstz (val p
)
50 (do ((l (p-terms p
) (pt-red l
))
54 (cexpt val
(- (pt-le l
) (pt-le (pt-red 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
)
76 (cexpt (car ll
) (car l
)))
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
)
86 (c (aref mat row col
))) ;Got to save this away before it is zeroed
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
)
99 (c (crecip (aref mat row 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
)
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"
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.
121 (defun swap-rows (mat m n
) ;Interchange row m and n
123 (l (array-dimension mat
0)))
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
139 (defun partial-diag (lobj pt val
) ; Does one step of diagonalization of
140 (let ((n (len lobj
)) ;the matrix in lobj
142 (mat (matrix lobj
)) ;The matrix, obviously
143 (row (currow lobj
)) ;This is the row which is to be
145 (badrows (badrows lobj
)) ;Rows which could not be used when
146 ;their time came, and will be used later
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
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
)))
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
))))
166 (swap-rows mat crow i
) ;Put this guy in the right spot
168 (setq flag t crow i
)) ; and make him a winner
169 (setq l
(cdr l
))) ;At any rate this guy isn't
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
))
177 (monicize-row mat crow n
) ;Monicize the diagonal element on this
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
))
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
)))
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
))
205 (if solved
(cons 'solved 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
))
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))
227 ;; (defun MERGE-INTVECT (iv h)
228 ;; (do ((l iv (cdr l))
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
)
242 ((do ((l (cdr poly
) (cddr l
))
244 (cons (cons (car l
) mon
) ans
)))
247 (defun new-skel (skel polys
)
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
)))
257 (defun create-lobjs (prev-lift)
258 (mapcar #'(lambda (q)
259 (let ((n (length (cadr q
))))
261 (list 'matrix n
(cadr q
)
262 (make-array (list n
(1+ n
)) :initial-element
0 :element-type
'fixnum
)
266 (defun clear-lobjs (lobjs)
267 (mapcar #'(lambda (q)
269 (list 'matrix
(caddr q
) (cadddr q
)
270 (caddr (cddr q
)) 0 nil
)))
273 (defun sparse-lift (c f g l-lobjs vars
)
274 (do ((pt (gen-point vars
) (gen-point vars
))
276 ((eq (car l-lobjs
) 'solved
)
278 (setq gcd
(lifting-factors-image
279 (pcsub c pt vars
) (pcsub f pt vars
) (pcsub g pt vars
)))
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
)))
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
))
299 (zgcd-lift c f g vars vals degb
)))))
301 ;; ZGCD-LIFT returns objects called lifts. These have the the following
303 ;; ((n <skel> <poly>) ... )
304 ;; n corresponds to the degree in the main variable to which this guy
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
)))
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
329 (pts (add-point (list (car vals
))) ;List of random
330 (add-point pts
)) ;points
331 (linsols (mapcar 'make-linsols prev-lift
)
335 (pcsubsty (car pts
) (car vars
) c
)
336 (pcsubsty (car pts
) (car vars
) f
)
337 (pcsubsty (car pts
) (car vars
) g
)
340 (interp-polys linsols
(cdr pts
) (car vars
))))))))
342 (defun make-linsols (prev-lift)
343 (list (car 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
)))
353 (do ((try (cmod (random $pointbound
))
354 (cmod (random $pointbound
))))
355 ((null (member try l
:test
#'equal
))
358 (defun merge-sol-lin (l1 l2
)
362 (cond ((= (caar l
) (caar l2
))
364 (mapcar 'cons
(cadddr (cddar l2
)) (caddar l
)))))))
366 (defun interp-polys (l pts var
)
367 (mapcar #'(lambda (q)
371 (mapcar #'(lambda (r) (pinterp var pts r
))
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
380 ;; *WHICH-FACTOR* is interpreted as follows. It is set fairly deep in the
381 ;; algorithm, inside ZGCD1.
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."))
395 (setq gcd
(catch 'relprime
(zgcd1 f g
)))
398 (1 (testdivide f gcd
))
399 (2 (testdivide f gcd
))
400 (3 (testdivide g gcd
))))
401 (cond ((not (null test
))
403 (cond ((equal *which-factor
* 1)
406 ((not (null modulus
))
407 (return (let (($gcd
'$red
))
411 (let* ((modulus modulus
)
414 (vars (sort (union1 (listovars f
) (listovars g
))
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
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.
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
)
453 (setq modulus
*alpha
) ;Really should randomize
455 (zgcd-lift* (pmod c
) (pmod f
) (pmod g
)
456 (cdr vars
) (cdr degb
)))
457 (do ((linsols (mapcar #'(lambda (q)
459 (new-skel (cadr q
) (caddr q
))))
461 (merge-sol-lin-z linsols
462 (sparse-lift cm fm gm lobjs
(cdr vars
))
464 (crecip (cmod coef-bound
)))
465 (* modulus coef-bound
)))
466 (lobjs (create-lobjs first-lift
)
468 (coef-bound *alpha
(* modulus coef-bound
))
472 (lobj->poly
(car vars
) (cdr vars
) linsols
))
473 (setq modulus
(newprime modulus
))
478 (defun lobj->poly
(var vars lobj
)
484 (do ((x (cadr q
) (cdr x
))
485 (y (caddr q
) (cdr y
))
488 (disrep-monom (cdar x
) (car y
)
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
)
503 (modulus new-coef-bound
)
506 (cond ((= (caar l
) (caar l2
))
510 (cond ((> (abs (setq n
(cplus b
(ctimes c
(cdifference a b
)))))
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))
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
*))))
538 (t (push (pt-le (p-terms gcd
*)) l
)))
540 (return l
)))))) ;No reverse needed here
542 ;; DETERMINE-LIFTING-FACTOR returns a number indicating which factor of f or g
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
)))
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
))
562 (cond ((eq (car lv
) (car vars
))
564 (setq vars
(cdr vars
))))))
566 (defun zpdegreevector (p vars
)
567 (excise-extra-variables (pdegreevector p
) vars
))