1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancments. ;;;;;
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 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
)
39 (pctimes (crecip (pcsubstz (car xk
) qk
))
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
)))
49 (defun pcsubstz (val p
)
51 (do ((l (p-terms p
) (pt-red l
))
55 (cexpt val
(- (pt-le l
) (pt-le (pt-red 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
)
77 (cexpt (car ll
) (car l
)))
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
)
87 (c (aref mat row col
))) ;Got to save this away before it is zeroed
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
)
100 (c (crecip (aref mat row 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
)
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"
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.
122 (defun swap-rows (mat m n
) ;Interchange row m and n
124 (l (array-dimension mat
0)))
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
140 (defun partial-diag (lobj pt val
) ; Does one step of diagonalization of
141 (let ((n (len lobj
)) ;the matrix in lobj
143 (mat (matrix lobj
)) ;The matrix, obviously
144 (row (currow lobj
)) ;This is the row which is to be
146 (badrows (badrows lobj
)) ;Rows which could not be used when
147 ;their time came, and will be used later
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
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
)))
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
))))
167 (swap-rows mat crow i
) ;Put this guy in the right spot
169 (setq flag t crow i
)) ; and make him a winner
170 (setq l
(cdr l
))) ;At any rate this guy isn't
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
))
178 (monicize-row mat crow n
) ;Monicize the diagonal element on this
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
))
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
)))
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
))
206 (if solved
(cons 'solved 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
))
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))
228 ;; (defun MERGE-INTVECT (iv h)
229 ;; (do ((l iv (cdr l))
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
)
243 ((do ((l (cdr poly
) (cddr l
))
245 (cons (cons (car l
) mon
) ans
)))
248 (defun new-skel (skel polys
)
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
)))
258 (defun create-lobjs (prev-lift)
259 (mapcar #'(lambda (q)
260 (let ((n (length (cadr q
))))
262 (list 'matrix n
(cadr q
)
263 (make-array (list n
(1+ n
)) :initial-element
0 :element-type
'fixnum
)
267 (defun clear-lobjs (lobjs)
268 (mapcar #'(lambda (q)
270 (list 'matrix
(caddr q
) (cadddr q
)
271 (caddr (cddr q
)) 0 nil
)))
274 (defun sparse-lift (c f g l-lobjs vars
)
275 (do ((pt (gen-point vars
) (gen-point vars
))
277 ((eq (car l-lobjs
) 'solved
)
279 (setq gcd
(lifting-factors-image
280 (pcsub c pt vars
) (pcsub f pt vars
) (pcsub g pt vars
)))
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
)))
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
))
300 (zgcd-lift c f g vars vals degb
)))))
302 ;; ZGCD-LIFT returns objects called lifts. These have the the following
304 ;; ((n <skel> <poly>) ... )
305 ;; n corresponds to the degree in the main variable to which this guy
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
)))
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
330 (pts (add-point (list (car vals
))) ;List of random
331 (add-point pts
)) ;points
332 (linsols (mapcar 'make-linsols prev-lift
)
336 (pcsubsty (car pts
) (car vars
) c
)
337 (pcsubsty (car pts
) (car vars
) f
)
338 (pcsubsty (car pts
) (car vars
) g
)
341 (interp-polys linsols
(cdr pts
) (car vars
))))))))
343 (defun make-linsols (prev-lift)
344 (list (car 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
)))
354 (do ((try (cmod (random $pointbound
))
355 (cmod (random $pointbound
))))
356 ((null (member try l
:test
#'equal
))
359 (defun merge-sol-lin (l1 l2
)
363 (cond ((= (caar l
) (caar l2
))
365 (mapcar 'cons
(cadddr (cddar l2
)) (caddar l
)))))))
367 (defun interp-polys (l pts var
)
368 (mapcar #'(lambda (q)
372 (mapcar #'(lambda (r) (pinterp var pts r
))
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
381 ;; *WHICH-FACTOR* is interpreted as follows. It is set fairly deep in the
382 ;; algorithm, inside ZGCD1.
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."))
396 (setq gcd
(catch 'relprime
(zgcd1 f g
)))
399 (1 (testdivide f gcd
))
400 (2 (testdivide f gcd
))
401 (3 (testdivide g gcd
))))
402 (cond ((not (null test
))
404 (cond ((equal *which-factor
* 1)
407 ((not (null modulus
))
408 (return (let (($gcd
'$red
))
412 (let* ((modulus modulus
)
415 (vars (sort (union1 (listovars f
) (listovars g
))
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
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.
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
)
454 (setq modulus
*alpha
) ;Really should randomize
456 (zgcd-lift* (pmod c
) (pmod f
) (pmod g
)
457 (cdr vars
) (cdr degb
)))
458 (do ((linsols (mapcar #'(lambda (q)
460 (new-skel (cadr q
) (caddr q
))))
462 (merge-sol-lin-z linsols
463 (sparse-lift cm fm gm lobjs
(cdr vars
))
465 (crecip (cmod coef-bound
)))
466 (* modulus coef-bound
)))
467 (lobjs (create-lobjs first-lift
)
469 (coef-bound *alpha
(* modulus coef-bound
))
473 (lobj->poly
(car vars
) (cdr vars
) linsols
))
474 (setq modulus
(newprime modulus
))
479 (defun lobj->poly
(var vars lobj
)
485 (do ((x (cadr q
) (cdr x
))
486 (y (caddr q
) (cdr y
))
489 (disrep-monom (cdar x
) (car y
)
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
)
504 (modulus new-coef-bound
)
507 (cond ((= (caar l
) (caar l2
))
511 (cond ((> (abs (setq n
(cplus b
(ctimes c
(cdifference a b
)))))
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))
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
*))))
539 (t (push (pt-le (p-terms gcd
*)) l
)))
541 (return l
)))))) ;No reverse needed here
543 ;; DETERMINE-LIFTING-FACTOR returns a number indicating which factor of f or g
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
)))
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
))
563 (cond ((eq (car lv
) (car vars
))
565 (setq vars
(cdr vars
))))))
567 (defun zpdegreevector (p vars
)
568 (excise-extra-variables (pdegreevector p
) vars
))