File: cgrind.lisp

package info (click to toggle)
maxima-sage 5.45.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid, trixie
  • size: 113,788 kB
  • sloc: lisp: 440,833; fortran: 14,665; perl: 14,369; tcl: 10,997; sh: 4,475; makefile: 2,520; ansic: 447; python: 262; xml: 59; awk: 37; sed: 17
file content (138 lines) | stat: -rw-r--r-- 5,000 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
(in-package :maxima)

(macsyma-module cgrind)

;; This is heavily lifted from grind.lisp and fortra.lisp, and should have the
;; same features as the fortran command. In order to work it needs to be compiled and
;; then loaded, as in (for the case of cmucl):
;; :lisp (compile-file "cgrind.lisp"), copy cgrind.sse2f to ~/.maxima
;; load(cgrind)
;; Then one can run cgrind(expression) or cgrind(matrix).
;; M. Talon (2011)


(declare-top (special $loadprint)) ;If NIL, no load message gets printed.

;; This function is called from Macsyma toplevel.  First the arguments is
;; checked to be a single expression. Then if the argument is a
;; symbol, and the symbol is bound to a matrix, the matrix is printed
;; using an array assignment notation.

(defmspec $cgrind (l)
  (setq l (fexprcheck l))
  (let ((value (strmeval l)))
    (cond ((msetqp l) (setq value `((mequal) ,(cadr l) ,(meval l)))))
    (cond ((and (symbolp l) ($matrixp value))
	   ($cgrindmx l value))
	  ((and (not (atom value)) (eq (caar value) 'mequal)
		(symbolp (cadr value)) ($matrixp (caddr value)))
	   ($cgrindmx (cadr value) (caddr value)))
	  (t (c-print value)))))

(defun c-print (x &optional (stream *standard-output*))
  ;; Restructure the expression for displaying. 
  ;; Mainly sanitizes exponentials, notably exp(2/3) becomes
  ;; exp(2.0/3.0)

  (setq x (scanforc x))

  ;; Protects the modifications to mexpt from creeping out.

  (unwind-protect

    (progn     
    (defprop mexpt msz-cmexpt grind)

    ;; This means basic printing for atoms, grind does fancy things.
    (setq *fortran-print* t)

    ;; Prints using the usual grind mechanisms
    (mgrind x stream)(write-char #\; stream)(write-char #\Newline stream))
       
    ;; Restore usual mexpt property etc. before exiting this frame.
    (defprop mexpt msz-mexpt grind)
    (setq *fortran-print* nil))
  '$done)


;; The only modification to grind, converts a^b to pow(a,b), but taking
;; care of appropriate bracketing. The argument l to the left of (MEXPT)
;; has to be composed backwards. Finally a^-b has special treatment.

(defun msz-cmexpt (x l r)
  (setq l (msize (cadr x) (revappend '(#\p #\o #\w #\() l) (list #\,) 'mparen 'mparen)
        r (if (mmminusp (setq x (nformat (caddr x))))
              (msize (cadr x) (list #\-) (cons #\) r) 'mexpt rop)
              (msize x nil  (cons #\) r ) 'mparen 'mparen)))
  (list (+ (car l) (car r)) l r))


  
  

;; Takes a name and a matrix and prints a sequence of C assignment
;; statements of the form
;; NAME[I][J] = <corresponding matrix element>
;; This requires some formatting work unnecessary for the fortran case.

(defmfun $cgrindmx (name mat &optional (stream *standard-output*) &aux ($loadprint nil))
  (cond ((not (symbolp name))
	 (merror (intl:gettext "cgrindmx: first argument must be a symbol; found: ~M") name))
	((not ($matrixp mat))
	 (merror (intl:gettext "cgrindmx: second argument must be a matrix; found: ~M") mat)))
  (do ((mat (cdr mat) (cdr mat)) (i 1 (1+ i)))
      ((null mat))
    (do ((m (cdar mat) (cdr m)) (j 1 (1+ j)))
	((null m))
       (format stream "~a[~a][~a] = " (string-left-trim "$" name) (1- i) (1- j) )
       (c-print (car m) stream)))
  '$done)





;; This C scanning function is similar to fortscan.  Prepare an expression
;; for printing by converting x^(1/2) to sqrt(x), etc.   Since C has no
;; support for complex numbers, contrary to Fortran, ban them.

(defun scanforc (e)
  (cond ((atom e) (cond ((eq e '$%i) ;; ban complex numbers
			 (merror (intl:gettext "Take real and imaginary parts")))
			(t e)))	 
	;; %e^a -> exp(a)
	((and (eq (caar e) 'mexpt) (eq (cadr e) '$%e))
	 (list '(%exp simp) (scanforc (caddr e))))
	;; a^1/2 -> sqrt(a)  1//2 is defined as ((rat simp) 1 2)
	((and (eq (caar e) 'mexpt) (alike1 (caddr e) 1//2))
	 (list '(%sqrt simp) (scanforc (cadr e))))
	;; a^-1/2 -> 1/sqrt(a)
	((and (eq (caar e) 'mexpt) (alike1 (caddr e) -1//2))
	 (list '(mquotient simp) 1 (list '(%sqrt simp) (scanforc (cadr e)))))
	;; (1/3)*b -> b/3.0 and (-1/3)*b -> -b/3.0
	((and (eq (caar e) 'mtimes) (ratnump (cadr e))
	      (member (cadadr e) '(1 -1) :test #'equal))
	 (cond ((equal (cadadr e) 1) (scanforc-mtimes e))
	       (t (list '(mminus simp) (scanforc-mtimes e)))))
	;; 1/3 -> 1.0/3.0
	((eq (caar e) 'rat)
	 (list '(mquotient simp) (float (cadr e)) (float (caddr e))))
	;; rat(a/b) -> a/b via ratdisrep
	((eq (caar e) 'mrat) (scanforc (ratdisrep e)))
	;; ban complex numbers
	((and (member (caar e) '(mtimes mplus) :test #'eq)
	      (let ((a (simplify ($bothcoef e '$%i)))) 
		(and (numberp (cadr a))
		     (numberp (caddr a))
		     (not (zerop1 (cadr a)))
		     (merror (intl:gettext "Take real and imaginary parts"))))))
	;; in general do nothing, recurse
	(t (cons (car e) (mapcar 'scanforc (cdr e))))))

;; This is used above 1/3*b*c -> b*c/3.0
(defun scanforc-mtimes (e)
  (list '(mquotient simp)
	(cond ((null (cdddr e)) (scanforc (caddr e)))
	      (t (cons (car e) (mapcar 'scanforc (cddr e)))))
	(float (caddr (cadr e)))))