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/parse.lisp@ 646

Last change on this file since 646 was 646, checked in by Marek Rychlik, 10 years ago

* empty log message *

File size: 12.6 KB
Line 
1;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: Grobner; Base: 10 -*-
2
3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4;;;
5;;; Copyright (C) 1999, 2002, 2009, 2015 Marek Rychlik <rychlik@u.arizona.edu>
6;;;
7;;; This program is free software; you can redistribute it and/or modify
8;;; it under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 2 of the License, or
10;;; (at your option) any later version.
11;;;
12;;; This program is distributed in the hope that it will be useful,
13;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with this program; if not, write to the Free Software
19;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20;;;
21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
22
23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24;;
25;; Parser of infix notation.
26;;
27;; NOTE: This package is adapted from CGBLisp.
28;;
29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30
31(defpackage "PARSE"
32 (:use :cl :order :poly :ring)
33 (:export "PARSE PARSE-TO-ALIST" "PARSE-STRING-TO-ALIST"
34 "PARSE-TO-SORTED-ALIST" "PARSE-STRING-TO-SORTED-ALIST" "^" "["
35 "POLY-EVAL"))
36
37(in-package "PARSE")
38
39(proclaim '(optimize (speed 0) (space 0) (safety 3) (debug 3)))
40
41;; The function PARSE yields the representations as above. The two functions
42;; PARSE-TO-ALIST and PARSE-STRING-TO-ALIST parse polynomials to the alist
43;; representations. For example
44;;
45;; >(parse)x^2-y^2+(-4/3)*u^2*w^3-5 --->
46;; (+ (* 1 (^ X 2)) (* -1 (^ Y 2)) (* -4/3 (^ U 2) (^ W 3)) (* -5))
47;;
48;; >(parse-to-alist '(x y u w))x^2-y^2+(-4/3)*u^2*w^3-5 --->
49;; (((0 0 2 3) . -4/3) ((0 2 0 0) . -1) ((2 0 0 0) . 1) ((0 0 0 0) . -5))
50;;
51;; >(parse-string-to-alist "x^2-y^2+(-4/3)*u^2*w^3-5" '(x y u w)) --->
52;; (((0 0 2 3) . -4/3) ((0 2 0 0) . -1) ((2 0 0 0) . 1) ((0 0 0 0) . -5))
53;;
54;; >(parse-string-to-alist "[x^2-y^2+(-4/3)*u^2*w^3-5,y]" '(x y u w))
55;; ([ (((0 0 2 3) . -4/3) ((0 2 0 0) . -1) ((2 0 0 0) . 1)
56;; ((0 0 0 0) . -5))
57;; (((0 1 0 0) . 1)))
58;; The functions PARSE-TO-SORTED-ALIST and PARSE-STRING-TO-SORTED-ALIST
59;; in addition sort terms by the predicate defined in the ORDER package
60;; For instance:
61;; >(parse-to-sorted-alist '(x y u w))x^2-y^2+(-4/3)*u^2*w^3-5
62;; (((2 0 0 0) . 1) ((0 2 0 0) . -1) ((0 0 2 3) . -4/3) ((0 0 0 0) . -5))
63;; >(parse-to-sorted-alist '(x y u w) t #'grlex>)x^2-y^2+(-4/3)*u^2*w^3-5
64;; (((0 0 2 3) . -4/3) ((2 0 0 0) . 1) ((0 2 0 0) . -1) ((0 0 0 0) . -5))
65
66;;(eval-when (compile)
67;; (proclaim '(optimize safety)))
68
69(defun convert-number (number-or-poly n)
70 "Returns NUMBER-OR-POLY, if it is a polynomial. If it is a number,
71it converts it to the constant monomial in N variables. If the result
72is a number then convert it to a polynomial in N variables."
73 (if (numberp number-or-poly)
74 (list (cons (make-list n :initial-element 0) number-or-poly))
75 number-or-poly))
76
77(defun $poly+ (p q n order ring)
78 "Add two polynomials P and Q, where each polynomial is either a
79numeric constant or a polynomial in internal representation. If the
80result is a number then convert it to a polynomial in N variables."
81 (poly+ (convert-number p n) (convert-number q n) order ring))
82
83(defun $poly- (p q n order ring)
84 "Subtract two polynomials P and Q, where each polynomial is either a
85numeric constant or a polynomial in internal representation. If the
86result is a number then convert it to a polynomial in N variables."
87 (poly- (convert-number p n) (convert-number q n) order ring))
88
89(defun $minus-poly (p n ring)
90 "Negation of P is a polynomial is either a numeric constant or a
91polynomial in internal representation. If the result is a number then
92convert it to a polynomial in N variables."
93 (minus-poly (convert-number p n) ring))
94
95(defun $poly* (p q n order ring)
96 "Multiply two polynomials P and Q, where each polynomial is either a
97numeric constant or a polynomial in internal representation. If the
98result is a number then convert it to a polynomial in N variables."
99 (poly* (convert-number p n) (convert-number q n) order ring))
100
101(defun $poly/ (p q ring)
102 "Divide a polynomials P which is either a numeric constant or a
103polynomial in internal representation, by a number Q."
104 (if (numberp p)
105 (common-lisp:/ p q)
106 (scalar-times-poly (common-lisp:/ q) p ring)))
107
108(defun $poly-expt (p l n order ring)
109 "Raise polynomial P, which is a polynomial in internal
110representation or a numeric constant, to power L. If P is a number,
111convert the result to a polynomial in N variables."
112 (poly-expt (convert-number p n) l order ring))
113
114(defun parse (&optional stream)
115 "Parser of infis expressions with integer/rational coefficients
116The parser will recognize two kinds of polynomial expressions:
117
118- polynomials in fully expanded forms with coefficients
119 written in front of symbolic expressions; constants can be optionally
120 enclosed in (); for example, the infix form
121 X^2-Y^2+(-4/3)*U^2*W^3-5
122 parses to
123 (+ (- (EXPT X 2) (EXPT Y 2)) (* (- (/ 4 3)) (EXPT U 2) (EXPT W 3)) (- 5))
124
125- lists of polynomials; for example
126 [X-Y, X^2+3*Z]
127 parses to
128 (:[ (- X Y) (+ (EXPT X 2) (* 3 Z)))
129 where the first symbol [ marks a list of polynomials.
130
131-other infix expressions, for example
132 [(X-Y)*(X+Y)/Z,(X+1)^2]
133parses to:
134 (:[ (/ (* (- X Y) (+ X Y)) Z) (EXPT (+ X 1) 2))
135Currently this function is implemented using M. Kantrowitz's INFIX package."
136 (read-from-string
137 (concatenate 'string
138 "#I("
139 (with-output-to-string (s)
140 (loop
141 (multiple-value-bind (line eof)
142 (read-line stream t)
143 (format s "~A" line)
144 (when eof (return)))))
145 ")")))
146
147;; Translate output from parse to a pure list form
148;; assuming variables are VARS
149
150(defun alist-form (plist vars)
151 "Translates an expression PLIST, which should be a list of polynomials
152in variables VARS, to an alist representation of a polynomial.
153It returns the alist. See also PARSE-TO-ALIST."
154 (cond
155 ((endp plist) nil)
156 ((eql (first plist) '[)
157 (cons '[ (mapcar #'(lambda (x) (alist-form x vars)) (rest plist))))
158 (t
159 (assert (eql (car plist) '+))
160 (alist-form-1 (rest plist) vars))))
161
162(defun alist-form-1 (p vars
163 &aux (ht (make-hash-table
164 :test #'equal :size 16))
165 stack)
166 (dolist (term p)
167 (assert (eql (car term) '*))
168 (incf (gethash (powers (cddr term) vars) ht 0) (second term)))
169 (maphash #'(lambda (key value) (unless (zerop value)
170 (push (cons key value) stack))) ht)
171 stack)
172
173(defun powers (monom vars
174 &aux (tab (pairlis vars (make-list (length vars)
175 :initial-element 0))))
176 (dolist (e monom (mapcar #'(lambda (v) (cdr (assoc v tab))) vars))
177 (assert (equal (first e) '^))
178 (assert (integerp (third e)))
179 (assert (= (length e) 3))
180 (let ((x (assoc (second e) tab)))
181 (if (null x) (error "Variable ~a not in the list of variables."
182 (second e))
183 (incf (cdr x) (third e))))))
184
185
186;; New implementation based on the INFIX package of Mark Kantorowitz
187(defun parse-to-alist (vars &optional stream)
188 "Parse an expression already in prefix form to an association list form
189according to the internal CGBlisp polynomial syntax: a polynomial is an
190alist of pairs (MONOM . COEFFICIENT). For example:
191 (WITH-INPUT-FROM-STRING (S \"X^2-Y^2+(-4/3)*U^2*W^3-5\")
192 (PARSE-TO-ALIST '(X Y U W) S))
193evaluates to
194(((0 0 2 3) . -4/3) ((0 2 0 0) . -1) ((2 0 0 0) . 1) ((0 0 0 0) . -5))"
195 (poly-eval (parse stream) vars))
196
197
198(defun parse-string-to-alist (str vars)
199 "Parse string STR and return a polynomial as a sorted association
200list of pairs (MONOM . COEFFICIENT). For example:
201(parse-string-to-alist \"[x^2-y^2+(-4/3)*u^2*w^3-5,y]\" '(x y u w))
202 ([ (((0 0 2 3) . -4/3) ((0 2 0 0) . -1) ((2 0 0 0) . 1)
203 ((0 0 0 0) . -5))
204 (((0 1 0 0) . 1)))
205The functions PARSE-TO-SORTED-ALIST and PARSE-STRING-TO-SORTED-ALIST
206sort terms by the predicate defined in the ORDER package."
207 (with-input-from-string (stream str)
208 (parse-to-alist vars stream)))
209
210
211(defun parse-to-sorted-alist (vars &optional (order #'lex>) (stream t))
212 "Parses streasm STREAM and returns a polynomial represented as
213a sorted alist. For example:
214(WITH-INPUT-FROM-STRING (S \"X^2-Y^2+(-4/3)*U^2*W^3-5\")
215 (PARSE-TO-SORTED-ALIST '(X Y U W) S))
216returns
217(((2 0 0 0) . 1) ((0 2 0 0) . -1) ((0 0 2 3) . -4/3) ((0 0 0 0) . -5))
218and
219(WITH-INPUT-FROM-STRING (S \"X^2-Y^2+(-4/3)*U^2*W^3-5\")
220 (PARSE-TO-SORTED-ALIST '(X Y U W) T #'GRLEX>) S)
221returns
222(((0 0 2 3) . -4/3) ((2 0 0 0) . 1) ((0 2 0 0) . -1) ((0 0 0 0) . -5))"
223 (sort-poly (parse-to-alist vars stream) order))
224
225(defun parse-string-to-sorted-alist (str vars &optional (order #'lex>))
226 "Parse a string to a sorted alist form, the internal representation
227of polynomials used by our system."
228 (with-input-from-string (stream str)
229 (parse-to-sorted-alist vars order stream)))
230
231(defun sort-poly-1 (p order)
232 "Sort the terms of a single polynomial P using an admissible monomial order ORDER.
233Returns the sorted polynomial. Destructively modifies P."
234 (sort p order :key #'first))
235
236;; Sort a polynomial or polynomial list
237(defun sort-poly (poly-or-poly-list &optional (order #'lex>))
238 "Sort POLY-OR-POLY-LIST, which could be either a single polynomial
239or a list of polynomials in internal alist representation, using
240admissible monomial order ORDER. Each polynomial is sorted using
241SORT-POLY-1."
242 (cond
243 ((eql poly-or-poly-list :syntax-error) nil)
244 ((null poly-or-poly-list) nil)
245 ((eql (car poly-or-poly-list) '[)
246 (cons '[ (mapcar #'(lambda (p) (sort-poly-1 p order))
247 (rest poly-or-poly-list))))
248 (t (sort-poly-1 poly-or-poly-list order))))
249
250(defun poly-eval-1 (expr vars order ring
251 &aux
252 (n (length vars))
253 (basis (monom-basis (length vars))))
254 "Evaluate an expression EXPR as polynomial by substituting operators
255+ - * expt with corresponding polynomial operators and variables VARS
256with monomials (1 0 ... 0), (0 1 ... 0) etc. We use special versions
257of binary operators $poly+, $poly-, $minus-poly, $poly* and $poly-expt
258which work like the corresponding functions in the POLY package, but
259accept scalars as arguments as well."
260 (cond
261 ((numberp expr)
262 (cond
263 ((zerop expr) NIL)
264 (t (list (cons (make-list n :initial-element 0) expr)))))
265 ((symbolp expr)
266 (nth (position expr vars) basis))
267 ((consp expr)
268 (case (car expr)
269 (expt
270 (if (= (length expr) 3)
271 ($poly-expt (poly-eval-1 (cadr expr) vars order ring)
272 (caddr expr)
273 n order ring)
274 (error "Too many arguments to EXPT")))
275 (/
276 (if (and (= (length expr) 3)
277 (numberp (caddr expr)))
278 ($poly/ (cadr expr) (caddr expr) ring)
279 (error "The second argument to / must be a number")))
280 (otherwise
281 (let ((r (mapcar
282 #'(lambda (e) (poly-eval-1 e vars order ring))
283 (cdr expr))))
284 (ecase (car expr)
285 (+ (reduce #'(lambda (p q) ($poly+ p q n order ring)) r))
286 (-
287 (if (endp (cdr r))
288 ($minus-poly (car r) n ring)
289 ($poly- (car r)
290 (reduce #'(lambda (p q) ($poly+ p q n order ring)) (cdr r))
291 n
292 order ring)))
293 (*
294 (reduce #'(lambda (p q) ($poly* p q n order ring)) r))
295 )))))))
296
297
298(defun poly-eval (expr vars &optional (order #'lex>) (ring *coefficient-ring*))
299 "Evaluate an expression EXPR, which should be a polynomial
300expression or a list of polynomial expressions (a list of expressions
301marked by prepending keyword :[ to it) given in lisp prefix notation,
302in variables VARS, which should be a list of symbols. The result of
303the evaluation is a polynomial or a list of polynomials (marked by
304prepending symbol '[) in the internal alist form. This evaluator is
305used by the PARSE package to convert input from strings directly to
306internal form."
307 (cond
308 ((numberp expr)
309 (unless (zerop expr)
310 (list (cons (make-list (length vars) :initial-element 0) expr))))
311 ((or (symbolp expr) (not (eq (car expr) :[)))
312 (poly-eval-1 expr vars order ring))
313 (t (cons '[ (mapcar #'(lambda (p) (poly-eval-1 p vars order ring)) (rest expr))))))
314
315
316;; Return the standard basis of the monomials in n variables
317(defun monom-basis (n &aux
318 (basis
319 (copy-tree
320 (make-list n :initial-element
321 (list (cons
322 (make-list
323 n
324 :initial-element 0)
325 1))))))
326 "Generate a list of monomials ((1 0 ... 0) (0 1 0 ... 0) ... (0 0 ... 1)
327which correspond to linear monomials X1, X2, ... XN."
328 (dotimes (i n basis)
329 (setf (elt (caar (elt basis i)) i) 1)))
Note: See TracBrowser for help on using the repository browser.