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/monom.lisp@ 3605

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

* empty log message *

File size: 20.5 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 "MONOM"
23 (:use :cl :copy)
24 (:export "MONOM"
25 "TERM"
26 "EXPONENT"
27 "MONOM-DIMENSION"
28 "MONOM-EXPONENTS"
29 "UNIVERSAL-EQUALP"
30 "MONOM-ELT"
31 "TOTAL-DEGREE"
32 "SUGAR"
33 "MULTIPLY-BY"
34 "DIVIDE-BY"
35 "DIVIDE"
36 "MULTIPLY-2"
37 "MULTIPLY"
38 "DIVIDES-P"
39 "DIVIDES-LCM-P"
40 "LCM-DIVIDES-LCM-P"
41 "LCM-EQUAL-LCM-P"
42 "DIVISIBLE-BY-P"
43 "REL-PRIME-P"
44 "UNIVERSAL-LCM"
45 "UNIVERSAL-GCD"
46 "DEPENDS-P"
47 "LEFT-TENSOR-PRODUCT-BY"
48 "RIGHT-TENSOR-PRODUCT-BY"
49 "LEFT-CONTRACT"
50 "MAKE-MONOM-VARIABLE"
51 "MONOM->LIST"
52 "LEX>"
53 "GRLEX>"
54 "REVLEX>"
55 "GREVLEX>"
56 "INVLEX>"
57 "REVERSE-MONOMIAL-ORDER"
58 "MAKE-ELIMINATION-ORDER-FACTORY")
59 (:documentation
60 "This package implements basic operations on monomials, including
61various monomial orders.
62
63DATA STRUCTURES: Conceptually, monomials can be represented as lists:
64
65 monom: (n1 n2 ... nk) where ni are non-negative integers
66
67However, lists may be implemented as other sequence types, so the
68flexibility to change the representation should be maintained in the
69code to use general operations on sequences whenever possible. The
70optimization for the actual representation should be left to
71declarations and the compiler.
72
73EXAMPLES: Suppose that variables are x and y. Then
74
75 Monom x*y^2 ---> (1 2) "))
76
77(in-package :monom)
78
79(proclaim '(optimize (speed 3) (space 0) (safety 0) (debug 0)))
80
81(deftype exponent ()
82 "Type of exponent in a monomial."
83 'fixnum)
84
85(defclass monom ()
86 ((exponents :initarg :exponents :accessor monom-exponents
87 :documentation "The powers of the variables."))
88 ;; default-initargs are not needed, they are handled by SHARED-INITIALIZE
89 ;;(:default-initargs :dimension 'foo :exponents 'bar :exponent 'baz)
90 (:documentation
91 "Implements a monomial, i.e. a product of powers
92of variables, like X*Y^2."))
93
94(defmethod print-object ((self monom) stream)
95 (print-unreadable-object (self stream :type t :identity t)
96 (with-accessors ((exponents monom-exponents))
97 self
98 (format stream "EXPONENTS=~A"
99 exponents))))
100
101(defmethod initialize-instance :after ((self monom)
102 &key
103 (dimension 0 dimension-supplied-p)
104 (exponents nil exponents-supplied-p)
105 (exponent 0)
106 &allow-other-keys
107 )
108 "The following INITIALIZE-INSTANCE method allows instance initialization
109of a MONOM in a style similar to MAKE-ARRAY, e.g.:
110
111 (MAKE-INSTANCE :EXPONENTS '(1 2 3)) --> #<MONOM EXPONENTS=#(1 2 3)>
112 (MAKE-INSTANCE :DIMENSION 3) --> #<MONOM EXPONENTS=#(0 0 0)>
113 (MAKE-INSTANCE :DIMENSION 3 :EXPONENT 7) --> #<MONOM EXPONENTS=#(7 7 7)>
114
115If both DIMENSION and EXPONENTS are supplied, they must be compatible,
116i.e. the length of EXPONENTS must be equal DIMENSION. If EXPONENTS
117is not supplied, a monom with repeated value EXPONENT is created.
118By default EXPONENT is 0, which results in a constant monomial.
119"
120 (cond
121 (exponents-supplied-p
122 (when (and dimension-supplied-p
123 (/= dimension (length exponents)))
124 (error "EXPONENTS (~A) must have supplied length DIMENSION (~A)"
125 exponents dimension))
126 (let ((dim (length exponents)))
127 (setf (slot-value self 'exponents) (make-array dim :initial-contents exponents))))
128 (dimension-supplied-p
129 ;; when all exponents are to be identical
130 (setf (slot-value self 'exponents) (make-array (list dimension)
131 :initial-element exponent
132 :element-type 'exponent)))
133 (t
134 (error "Initarg DIMENSION or EXPONENTS must be supplied."))))
135
136(defgeneric monom-dimension (m)
137 (:method ((m monom))
138 (length (monom-exponents m))))
139
140(defgeneric universal-equalp (object1 object2)
141 (:documentation "Returns T iff OBJECT1 and OBJECT2 are equal.")
142 (:method ((m1 monom) (m2 monom))
143 "Returns T iff monomials M1 and M2 have identical EXPONENTS."
144 (equalp (monom-exponents m1) (monom-exponents m2))))
145
146(defgeneric monom-elt (m index)
147 (:documentation "Return the power in the monomial M of variable number INDEX.")
148 (:method ((m monom) index)
149 "Return the power in the monomial M of variable number INDEX."
150 (with-slots (exponents)
151 m
152 (elt exponents index))))
153
154(defgeneric (setf monom-elt) (new-value m index)
155 (:documentation "Set the power in the monomial M of variable number INDEX.")
156 (:method (new-value (m monom) index)
157 (with-slots (exponents)
158 m
159 (setf (elt exponents index) new-value))))
160
161(defgeneric total-degree (m &optional start end)
162 (:documentation "Return the total degree of a monomoal M. Optinally, a range
163of variables may be specified with arguments START and END.")
164 (:method ((m monom) &optional (start 0) (end (monom-dimension m)))
165 (declare (type fixnum start end))
166 (with-slots (exponents)
167 m
168 (reduce #'+ exponents :start start :end end))))
169
170(defgeneric sugar (m &optional start end)
171 (:documentation "Return the sugar of a monomial M. Optinally, a range
172of variables may be specified with arguments START and END.")
173 (:method ((m monom) &optional (start 0) (end (monom-dimension m)))
174 (declare (type fixnum start end))
175 (total-degree m start end)))
176
177(defgeneric multiply-by (self other)
178 (:documentation "Multiply SELF by OTHER, return SELF.")
179 (:method ((self monom) (other monom))
180 (with-slots ((exponents1 exponents))
181 self
182 (with-slots ((exponents2 exponents))
183 other
184 (unless (= (length exponents1) (length exponents2))
185 (error "Incompatible dimensions"))
186 (map-into exponents1 #'+ exponents1 exponents2)))
187 self))
188
189(defgeneric divide-by (self other)
190 (:documentation "Divide SELF by OTHER, return SELF.")
191 (:method ((self monom) (other monom))
192 (with-slots ((exponents1 exponents))
193 self
194 (with-slots ((exponents2 exponents))
195 other
196 (unless (= (length exponents1) (length exponents2))
197 (error "divide-by: Incompatible dimensions."))
198 (unless (every #'>= exponents1 exponents2)
199 (error "divide-by: Negative power would result."))
200 (map-into exponents1 #'- exponents1 exponents2)))
201 self))
202
203(defmethod copy-instance :around ((object monom) &rest initargs &key &allow-other-keys)
204 "An :AROUND method of COPY-INSTANCE. It replaces
205exponents with a fresh copy of the sequence."
206 (declare (ignore object initargs))
207 (let ((copy (call-next-method)))
208 (setf (monom-exponents copy) (copy-seq (monom-exponents copy)))
209 copy))
210
211(defun multiply-2 (object1 object2)
212 "Multiply OBJECT1 by OBJECT2"
213 (multiply-by (copy-instance object1) (copy-instance object2)))
214
215(defun multiply (&rest factors)
216 "Non-destructively multiply list FACTORS."
217 (reduce #'multiply-2 factors))
218
219(defun divide (numerator &rest denominators)
220 "Non-destructively divide object NUMERATOR by product of DENOMINATORS."
221 (divide-by (copy-instance numerator) (multiply denominators)))
222
223(defgeneric divides-p (object1 object2)
224 (:documentation "Returns T if OBJECT1 divides OBJECT2.")
225 (:method ((m1 monom) (m2 monom))
226 "Returns T if monomial M1 divides monomial M2, NIL otherwise."
227 (with-slots ((exponents1 exponents))
228 m1
229 (with-slots ((exponents2 exponents))
230 m2
231 (every #'<= exponents1 exponents2)))))
232
233(defgeneric divides-lcm-p (object1 object2 object3)
234 (:documentation "Returns T if OBJECT1 divides LCM(OBJECT2,OBJECT3), NIL otherwise.")
235 (:method ((m1 monom) (m2 monom) (m3 monom))
236 "Returns T if monomial M1 divides LCM(M2,M3), NIL otherwise."
237 (with-slots ((exponents1 exponents))
238 m1
239 (with-slots ((exponents2 exponents))
240 m2
241 (with-slots ((exponents3 exponents))
242 m3
243 (every #'(lambda (x y z) (<= x (max y z)))
244 exponents1 exponents2 exponents3))))))
245
246(defgeneric lcm-divides-lcm-p (object1 object2 object3 object4)
247 (:method ((m1 monom) (m2 monom) (m3 monom) (m4 monom))
248 "Returns T if monomial LCM(M1,M2) divides LCM(M3,M4), NIL otherwise."
249 (with-slots ((exponents1 exponents))
250 m1
251 (with-slots ((exponents2 exponents))
252 m2
253 (with-slots ((exponents3 exponents))
254 m3
255 (with-slots ((exponents4 exponents))
256 m4
257 (every #'(lambda (x y z w) (<= (max x y) (max z w)))
258 exponents1 exponents2 exponents3 exponents4)))))))
259
260(defgeneric monom-lcm-equal-lcm-p (object1 object2 object3 object4)
261 (:method ((m1 monom) (m2 monom) (m3 monom) (m4 monom))
262 "Returns T if monomial LCM(M1,M2) equals LCM(M3,M4), NIL otherwise."
263 (with-slots ((exponents1 exponents))
264 m1
265 (with-slots ((exponents2 exponents))
266 m2
267 (with-slots ((exponents3 exponents))
268 m3
269 (with-slots ((exponents4 exponents))
270 m4
271 (every
272 #'(lambda (x y z w) (= (max x y) (max z w)))
273 exponents1 exponents2 exponents3 exponents4)))))))
274
275(defgeneric divisible-by-p (object1 object2)
276 (:documentation "Return T if OBJECT1 is divisible by OBJECT2.")
277 (:method ((m1 monom) (m2 monom))
278 "Returns T if monomial M1 is divisible by monomial M2, NIL otherwise."
279 (with-slots ((exponents1 exponents))
280 m1
281 (with-slots ((exponents2 exponents))
282 m2
283 (every #'>= exponents1 exponents2)))))
284
285(defgeneric rel-prime-p (object1 object2)
286 (:documentation "Returns T if objects OBJECT1 and OBJECT2 are relatively prime.")
287 (:method ((m1 monom) (m2 monom))
288 "Returns T if two monomials M1 and M2 are relatively prime (disjoint)."
289 (with-slots ((exponents1 exponents))
290 m1
291 (with-slots ((exponents2 exponents))
292 m2
293 (every #'(lambda (x y) (zerop (min x y))) exponents1 exponents2)))))
294
295(defgeneric universal-lcm (object1 object2)
296 (:documentation "Returns the multiple of objects OBJECT1 and OBJECT2.")
297 (:method ((m1 monom) (m2 monom))
298 "Returns least common multiple of monomials M1 and M2."
299 (with-slots ((exponents1 exponents))
300 m1
301 (with-slots ((exponents2 exponents))
302 m2
303 (let* ((exponents (copy-seq exponents1)))
304 (map-into exponents #'max exponents1 exponents2)
305 (make-instance 'monom :exponents exponents))))))
306
307
308(defgeneric universal-gcd (object1 object2)
309 (:documentation "Returns GCD of objects OBJECT1 and OBJECT2")
310 (:method ((m1 monom) (m2 monom))
311 "Returns greatest common divisor of monomials M1 and M2."
312 (with-slots ((exponents1 exponents))
313 m1
314 (with-slots ((exponents2 exponents))
315 m2
316 (let* ((exponents (copy-seq exponents1)))
317 (map-into exponents #'min exponents1 exponents2)
318 (make-instance 'monom :exponents exponents))))))
319
320(defgeneric depends-p (object k)
321 (:documentation "Returns T iff object OBJECT depends on variable K.")
322 (:method ((m monom) k)
323 "Return T if the monomial M depends on variable number K."
324 (declare (type fixnum k))
325 (with-slots (exponents)
326 m
327 (plusp (elt exponents k)))))
328
329(defgeneric left-tensor-product-by (self other)
330 (:documentation "Returns a tensor product SELF by OTHER, stored into
331 SELF. Return SELF.")
332 (:method ((self monom) (other monom))
333 (with-slots ((exponents1 exponents))
334 self
335 (with-slots ((exponents2 exponents))
336 other
337 (setf exponents1 (concatenate 'vector exponents2 exponents1))))
338 self))
339
340(defgeneric right-tensor-product-by (self other)
341 (:documentation "Returns a tensor product of OTHER by SELF, stored
342 into SELF. Returns SELF.")
343 (:method ((self monom) (other monom))
344 (with-slots ((exponents1 exponents))
345 self
346 (with-slots ((exponents2 exponents))
347 other
348 (setf exponents1 (concatenate 'vector exponents1 exponents2))))
349 self))
350
351(defgeneric left-contract (self k)
352 (:documentation "Drop the first K variables in object SELF.")
353 (:method ((self monom) k)
354 "Drop the first K variables in monomial M."
355 (declare (fixnum k))
356 (with-slots (exponents)
357 self
358 (setf exponents (subseq exponents k)))
359 self))
360
361(defun make-monom-variable (nvars pos &optional (power 1)
362 &aux (m (make-instance 'monom :dimension nvars)))
363 "Construct a monomial in the polynomial ring
364RING[X[0],X[1],X[2],...X[NVARS-1]] over the (unspecified) ring RING
365which represents a single variable. It assumes number of variables
366NVARS and the variable is at position POS. Optionally, the variable
367may appear raised to power POWER. "
368 (declare (type fixnum nvars pos power) (type monom m))
369 (with-slots (exponents)
370 m
371 (setf (elt exponents pos) power)
372 m))
373
374(defgeneric ->list (object)
375 (:method ((m monom))
376 "A human-readable representation of a monomial M as a list of exponents."
377 (coerce (monom-exponents m) 'list)))
378
379;; pure lexicographic
380(defgeneric lex> (p q &optional start end)
381 (:documentation "Return T if P>Q with respect to lexicographic
382order, otherwise NIL. The second returned value is T if P=Q,
383otherwise it is NIL.")
384 (:method ((p monom) (q monom) &optional (start 0) (end (monom-dimension p)))
385 (declare (type fixnum start end))
386 (do ((i start (1+ i)))
387 ((>= i end) (values nil t))
388 (cond
389 ((> (monom-elt p i) (monom-elt q i))
390 (return-from lex> (values t nil)))
391 ((< (monom-elt p i) (monom-elt q i))
392 (return-from lex> (values nil nil)))))))
393
394;; total degree order, ties broken by lexicographic
395(defgeneric grlex> (p q &optional start end)
396 (:documentation "Return T if P>Q with respect to graded
397lexicographic order, otherwise NIL. The second returned value is T if
398P=Q, otherwise it is NIL.")
399 (:method ((p monom) (q monom) &optional (start 0) (end (monom-dimension p)))
400 (declare (type monom p q) (type fixnum start end))
401 (let ((d1 (total-degree p start end))
402 (d2 (total-degree q start end)))
403 (declare (type fixnum d1 d2))
404 (cond
405 ((> d1 d2) (values t nil))
406 ((< d1 d2) (values nil nil))
407 (t
408 (lex> p q start end))))))
409
410;; reverse lexicographic
411(defgeneric revlex> (p q &optional start end)
412 (:documentation "Return T if P>Q with respect to reverse
413lexicographic order, NIL otherwise. The second returned value is T if
414P=Q, otherwise it is NIL. This is not and admissible monomial order
415because some sets do not have a minimal element. This order is useful
416in constructing other orders.")
417 (:method ((p monom) (q monom) &optional (start 0) (end (monom-dimension p)))
418 (declare (type fixnum start end))
419 (do ((i (1- end) (1- i)))
420 ((< i start) (values nil t))
421 (declare (type fixnum i))
422 (cond
423 ((< (monom-elt p i) (monom-elt q i))
424 (return-from revlex> (values t nil)))
425 ((> (monom-elt p i) (monom-elt q i))
426 (return-from revlex> (values nil nil)))))))
427
428
429;; total degree, ties broken by reverse lexicographic
430(defgeneric grevlex> (p q &optional start end)
431 (:documentation "Return T if P>Q with respect to graded reverse
432lexicographic order, NIL otherwise. The second returned value is T if
433P=Q, otherwise it is NIL.")
434 (:method ((p monom) (q monom) &optional (start 0) (end (monom-dimension p)))
435 (declare (type fixnum start end))
436 (let ((d1 (total-degree p start end))
437 (d2 (total-degree q start end)))
438 (declare (type fixnum d1 d2))
439 (cond
440 ((> d1 d2) (values t nil))
441 ((< d1 d2) (values nil nil))
442 (t
443 (revlex> p q start end))))))
444
445(defgeneric invlex> (p q &optional start end)
446 (:documentation "Return T if P>Q with respect to inverse
447lexicographic order, NIL otherwise The second returned value is T if
448P=Q, otherwise it is NIL.")
449 (:method ((p monom) (q monom) &optional (start 0) (end (monom-dimension p)))
450 (declare (type fixnum start end))
451 (do ((i (1- end) (1- i)))
452 ((< i start) (values nil t))
453 (declare (type fixnum i))
454 (cond
455 ((> (monom-elt p i) (monom-elt q i))
456 (return-from invlex> (values t nil)))
457 ((< (monom-elt p i) (monom-elt q i))
458 (return-from invlex> (values nil nil)))))))
459
460(defun reverse-monomial-order (order)
461 "Create the inverse monomial order to the given monomial order ORDER."
462 #'(lambda (p q &optional (start 0) (end (monom-dimension q)))
463 (declare (type monom p q) (type fixnum start end))
464 (funcall order q p start end)))
465
466;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
467;;
468;; Order making functions
469;;
470;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
471
472;; This returns a closure with the same signature
473;; as all orders such as #'LEX>.
474(defun make-elimination-order-factory-1 (&optional (secondary-elimination-order #'lex>))
475 "It constructs an elimination order used for the 1-st elimination ideal,
476i.e. for eliminating the first variable. Thus, the order compares the degrees of the
477first variable in P and Q first, with ties broken by SECONDARY-ELIMINATION-ORDER."
478 #'(lambda (p q &optional (start 0) (end (monom-dimension p)))
479 (declare (type monom p q) (type fixnum start end))
480 (cond
481 ((> (monom-elt p start) (monom-elt q start))
482 (values t nil))
483 ((< (monom-elt p start) (monom-elt q start))
484 (values nil nil))
485 (t
486 (funcall secondary-elimination-order p q (1+ start) end)))))
487
488;; This returns a closure which is called with an integer argument.
489;; The result is *another closure* with the same signature as all
490;; orders such as #'LEX>.
491(defun make-elimination-order-factory (&optional
492 (primary-elimination-order #'lex>)
493 (secondary-elimination-order #'lex>))
494 "Return a function with a single integer argument K. This should be
495the number of initial K variables X[0],X[1],...,X[K-1], which precede
496remaining variables. The call to the closure creates a predicate
497which compares monomials according to the K-th elimination order. The
498monomial orders PRIMARY-ELIMINATION-ORDER and
499SECONDARY-ELIMINATION-ORDER are used to compare the first K and the
500remaining variables, respectively, with ties broken by lexicographical
501order. That is, if PRIMARY-ELIMINATION-ORDER yields (VALUES NIL T),
502which indicates that the first K variables appear with identical
503powers, then the result is that of a call to
504SECONDARY-ELIMINATION-ORDER applied to the remaining variables
505X[K],X[K+1],..."
506 #'(lambda (k)
507 (cond
508 ((<= k 0)
509 (error "K must be at least 1"))
510 ((= k 1)
511 (make-elimination-order-factory-1 secondary-elimination-order))
512 (t
513 #'(lambda (p q &optional (start 0) (end (monom-dimension p)))
514 (declare (type monom p q) (type fixnum start end))
515 (multiple-value-bind (primary equal)
516 (funcall primary-elimination-order p q start k)
517 (if equal
518 (funcall secondary-elimination-order p q k end)
519 (values primary nil))))))))
520
521(defclass term (monom)
522 ((coeff :initarg :coeff :accessor term-coeff))
523 (:default-initargs :coeff nil)
524 (:documentation "Implements a term, i.e. a product of a scalar
525and powers of some variables, such as 5*X^2*Y^3."))
526
527(defmethod print-object ((self term) stream)
528 (print-unreadable-object (self stream :type t :identity t)
529 (with-accessors ((exponents monom-exponents)
530 (coeff term-coeff))
531 self
532 (format stream "EXPONENTS=~A COEFF=~A"
533 exponents coeff))))
534
535(defmethod universal-equalp ((term1 term) (term2 term))
536 "Returns T if TERM1 and TERM2 are equal as MONOM, and coefficients
537are UNIVERSAL-EQUALP."
538 (and (call-next-method)
539 (universal-equalp (term-coeff term1) (term-coeff term2))))
540
541(defmethod update-instance-for-different-class :after ((old monom) (new term) &key)
542 (setf (term-coeff new) 1))
543
544(defmethod multiply-by :before ((self term) (other term))
545 "Destructively multiply terms SELF and OTHER and store the result into SELF.
546It returns SELF."
547 (setf (term-coeff self) (multiply-by (term-coeff self) (term-coeff other))))
548
549(defmethod left-tensor-product-by :before ((self term) (other term))
550 (setf (term-coeff self) (multiply-by (term-coeff self) (term-coeff other))))
551
552(defmethod right-tensor-product-by :before ((self term) (other term))
553 (setf (term-coeff self) (multiply-by (term-coeff self) (term-coeff other))))
554
555(defmethod divide-by :before ((self term) (other term))
556 (setf (term-coeff self) (divide-by (term-coeff self) (term-coeff other))))
557
558(defgeneric unary-minus (self)
559 (:method ((self term))
560 (setf (term-coeff self) (unary-minus (term-coeff self)))
561 self))
562
563(defgeneric universal-zerop (self)
564 (:method ((self term))
565 (universal-zerop (term-coeff self))))
Note: See TracBrowser for help on using the repository browser.