close Warning: Can't synchronize with repository "(default)" (The repository directory has changed, you should resynchronize the repository with: trac-admin $ENV repository resync '(default)'). Look in the Trac log for more information.

source: branches/f4grobner/division.lisp@ 1237

Last change on this file since 1237 was 1237, checked in by Marek Rychlik, 9 years ago

* empty log message *

File size: 8.9 KB
Line 
1;;; -*- Mode: Lisp -*-
2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3;;;
4;;; Copyright (C) 1999, 2002, 2009, 2015 Marek Rychlik <rychlik@u.arizona.edu>
5;;;
6;;; This program is free software; you can redistribute it and/or modify
7;;; it under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 2 of the License, or
9;;; (at your option) any later version.
10;;;
11;;; This program is distributed in the hope that it will be useful,
12;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with this program; if not, write to the Free Software
18;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19;;;
20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21
22(defpackage "DIVISION"
23 (:use :cl :utils :ring :monomial :polynomial :grobner-debug :term :ring-and-order)
24 (:export "$POLY_TOP_REDUCTION_ONLY"
25 "POLY-PSEUDO-DIVIDE"
26 "POLY-EXACT-DIVIDE"
27 "NORMAL-FORM-STEP"
28 "NORMAL-FORM"
29 "POLY-NORMALIZE"
30 "POLY-NORMALIZE-LIST"
31 "BUCHBERGER-CRITERION"
32 ))
33
34(in-package :division)
35
36(defvar $poly_top_reduction_only nil
37 "If not FALSE, use top reduction only whenever possible.
38Top reduction means that division algorithm stops after the first reduction.")
39
40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41;;
42;; An implementation of the division algorithm
43;;
44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
45
46(defun grobner-op (ring-and-order c1 c2 m f g
47 &aux
48 (ring (ro-ring ring-and-order)))
49 "Returns C2*F-C1*M*G, where F and G are polynomials M is a monomial.
50Assume that the leading terms will cancel."
51 (declare (type ring-and-order ring-and-order))
52 #+grobner-check(funcall (ring-zerop ring)
53 (funcall (ring-sub ring)
54 (funcall (ring-mul ring) c2 (poly-lc f))
55 (funcall (ring-mul ring) c1 (poly-lc g))))
56 #+grobner-check(monom-equal-p (poly-lm f) (monom-mul m (poly-lm g)))
57 ;; Note that below we can drop the leading terms of f ang g for the
58 ;; purpose of polynomial arithmetic.
59 ;;
60 ;; TODO: Make sure that the sugar calculation is correct if leading
61 ;; terms are dropped.
62 (poly-sub ring-and-order
63 (scalar-times-poly-1 ring c2 f)
64 (scalar-times-poly-1 ring c1 (monom-times-poly m g))))
65
66(defun check-loop-invariant (ring-and-order c f0 a fl r f
67 &aux
68 (ring (ro-ring ring-and-order))
69 (p-zero (make-poly-zero)))
70 "Loop invariant: c*f0=sum ai*fi+r+f, where f0 is the initial value of f"
71 (flet ((p-add (p q) (poly-add ring-and-order p q))
72 (p-sub (p q) (poly-sub ring-and-order p q))
73 (p-mul (p q) (poly-mul ring-and-order p q)))
74 (poly-zerop
75 (p-sub
76 (scalar-times-poly ring c f0)
77 (reduce #'p-add
78 (list (inner-product a fl p-add p-mul p-zero)
79 r
80 f))))))
81
82
83
84(defun poly-pseudo-divide (ring-and-order f fl
85 &aux
86 (ring (ro-ring ring-and-order)))
87 "Pseudo-divide a polynomial F by the list of polynomials FL. Return
88multiple values. The first value is a list of quotients A. The second
89value is the remainder R. The third argument is a scalar coefficient
90C, such that C*F can be divided by FL within the ring of coefficients,
91which is not necessarily a field. Finally, the fourth value is an
92integer count of the number of reductions performed. The resulting
93objects satisfy the equation: C*F= sum A[i]*FL[i] + R. The sugar of
94the quotients is initialized to default."
95 (declare (type poly f) (list fl))
96 ;; Loop invariant: c*f0=sum ai*fi+r+f, where f0 is the initial value of f
97 (do ((r (make-poly-zero))
98 (c (funcall (ring-unit ring)))
99 (a (make-list (length fl) :initial-element (make-poly-zero)))
100 (division-count 0)
101 (p f))
102 ((poly-zerop p)
103 (debug-cgb "~&~3T~d reduction~:p" division-count)
104 (when (poly-zerop r) (debug-cgb " ---> 0"))
105 ;; We obtained the terms in reverse order, so must fix that
106 (setf a (mapcar #'poly-nreverse a)
107 r (poly-nreverse r))
108 ;; Initialize the sugar of the quotients
109 (mapc #'poly-reset-sugar a)
110 (values a r c division-count))
111 (declare (fixnum division-count))
112 (do ((fl fl (rest fl)) ;scan list of divisors
113 (b a (rest b)))
114 ((cond
115 ((endp fl) ;no division occurred
116 (push (poly-lt p) (poly-termlist r)) ;move lt(p) to remainder
117 (setf (poly-sugar r) (max (poly-sugar r) (term-sugar (poly-lt p))))
118 (pop (poly-termlist p)) ;remove lt(p) from p
119 t)
120 ((monom-divides-p (poly-lm (car fl)) (poly-lm p)) ;division occurred
121 (incf division-count)
122 (multiple-value-bind (gcd c1 c2)
123 (funcall (ring-ezgcd ring) (poly-lc (car fl)) (poly-lc p))
124 (declare (ignore gcd))
125 (let ((m (monom-div (poly-lm p) (poly-lm (car fl)))))
126 ;; Multiply the equation c*f=sum ai*fi+r+p by c1.
127 (mapl #'(lambda (x)
128 (setf (car x) (scalar-times-poly ring c1 (car x))))
129 a)
130 (setf r (scalar-times-poly ring c1 r)
131 c (funcall (ring-mul ring) c c1)
132 p (grobner-op ring-and-order c2 c1 m p (car fl)))
133 (push (make-term m c2) (poly-termlist (car b))))
134 t)))))))
135
136(defun poly-exact-divide (ring f g)
137 "Divide a polynomial F by another polynomial G. Assume that exact division
138with no remainder is possible. Returns the quotient."
139 (declare (type poly f g))
140 (multiple-value-bind (quot rem coeff division-count)
141 (poly-pseudo-divide ring f (list g))
142 (declare (ignore division-count coeff)
143 (list quot)
144 (type poly rem)
145 (type fixnum division-count))
146 (unless (poly-zerop rem) (error "Exact division failed."))
147 (car quot)))
148
149;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
150;;
151;; An implementation of the normal form
152;;
153;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
154
155(defun normal-form-step (ring-and-order fl p r c division-count
156 &aux
157 (ring (ro-ring ring-and-order))
158 (g (find (poly-lm p) fl
159 :test #'monom-divisible-by-p
160 :key #'poly-lm)))
161 (cond
162 (g ;division possible
163 (incf division-count)
164 (multiple-value-bind (gcd cg cp)
165 (funcall (ring-ezgcd ring) (poly-lc g) (poly-lc p))
166 (declare (ignore gcd))
167 (let ((m (monom-div (poly-lm p) (poly-lm g))))
168 ;; Multiply the equation c*f=sum ai*fi+r+p by cg.
169 (setf r (scalar-times-poly ring cg r)
170 c (funcall (ring-mul ring) c cg)
171 ;; p := cg*p-cp*m*g
172 p (grobner-op ring-and-order cp cg m p g))))
173 (debug-cgb "/"))
174 (t ;no division possible
175 (push (poly-lt p) (poly-termlist r)) ;move lt(p) to remainder
176 (setf (poly-sugar r) (max (poly-sugar r) (term-sugar (poly-lt p))))
177 (pop (poly-termlist p)) ;remove lt(p) from p
178 (debug-cgb "+")))
179 (values p r c division-count))
180
181;; Merge it sometime with poly-pseudo-divide
182(defun normal-form (ring-and-order f fl
183 &optional
184 (top-reduction-only $poly_top_reduction_only)
185 (ring (ro-ring ring-and-order)))
186 ;; Loop invariant: c*f0=sum ai*fi+r+f, where f0 is the initial value of f
187 #+grobner-check(when (null fl) (warn "normal-form: empty divisor list."))
188 (do ((r (make-poly-zero))
189 (c (funcall (ring-unit ring)))
190 (division-count 0))
191 ((or (poly-zerop f)
192 ;;(endp fl)
193 (and top-reduction-only (not (poly-zerop r))))
194 (progn
195 (debug-cgb "~&~3T~d reduction~:p" division-count)
196 (when (poly-zerop r)
197 (debug-cgb " ---> 0")))
198 (setf (poly-termlist f) (nreconc (poly-termlist r) (poly-termlist f)))
199 (values f c division-count))
200 (declare (fixnum division-count)
201 (type poly r))
202 (multiple-value-setq (f r c division-count)
203 (normal-form-step ring-and-order fl f r c division-count))))
204
205(defun buchberger-criterion (ring-and-order g)
206 "Returns T if G is a Grobner basis, by using the Buchberger
207criterion: for every two polynomials h1 and h2 in G the S-polynomial
208S(h1,h2) reduces to 0 modulo G."
209 (every #'poly-zerop
210 (makelist (normal-form ring-and-order (spoly ring-and-order (elt g i) (elt g j)) g nil)
211 (i 0 (- (length g) 2))
212 (j (1+ i) (1- (length g))))))
213
214
215(defun poly-normalize (ring p &aux (c (poly-lc p)))
216 "Divide a polynomial by its leading coefficient. It assumes
217that the division is possible, which may not always be the
218case in rings which are not fields. The exact division operator
219is assumed to be provided by the RING structure."
220 (mapc #'(lambda (term)
221 (setf (term-coeff term) (funcall (ring-div ring) (term-coeff term) c)))
222 (poly-termlist p))
223 p)
224
225(defun poly-normalize-list (ring plist)
226 "Divide every polynomial in a list PLIST by its leading coefficient. "
227 (mapcar #'(lambda (x) (poly-normalize ring x)) plist))
Note: See TracBrowser for help on using the repository browser.