source: project/release/4/nemo/trunk/nemo-nmodl.scm @ 27140

Last change on this file since 27140 was 27140, checked in by Ivan Raikov, 9 years ago

nemo: added ability to specify ion valence in decaying pool definitions (nmodl backend)

File size: 38.1 KB
Line 
1;;       
2;;
3;; An extension for translating NEMO models to NMODL descriptions.
4;;
5;; Copyright 2008-2012 Ivan Raikov and the Okinawa Institute of Science and Technology
6;;
7;; This program is free software: you can redistribute it and/or
8;; modify it under the terms of the GNU General Public License as
9;; published by the Free Software Foundation, either version 3 of the
10;; License, or (at your option) any later version.
11;;
12;; This program is distributed in the hope that it will be useful, but
13;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15;; General Public License for more details.
16;;
17;; A full copy of the GPL license can be found at
18;; <http://www.gnu.org/licenses/>.
19;;
20
21(module nemo-nmodl
22
23        (nemo:nmodl-translator)
24
25        (import scheme chicken utils data-structures lolevel srfi-1 srfi-13 srfi-69)
26
27        (require-extension lolevel datatype matchable strictly-pretty 
28                           varsubst datatype 
29                           nemo-core nemo-utils nemo-gate-complex)
30
31(define nmodl-builtin-consts
32  `(celsius diam))
33
34(define nmodl-ops
35  `(+ - * / > < <= >= = ^))
36
37(define builtin-fns
38  `(+ - * / pow neg abs atan asin acos sin cos exp ln
39      sqrt tan cosh sinh tanh hypot gamma lgamma log10 log2 log1p ldexp cube
40      > < <= >= = and or round ceiling floor max min
41      ))
42
43
44(define (nmodl-name s)
45  (let ((cs (string->list (->string s))))
46    (let loop ((lst (list)) (cs cs))
47      (if (null? cs) (string->symbol (list->string (reverse lst)))
48          (let* ((c (car cs))
49                 (c1 (cond ((or (char-alphabetic? c) (char-numeric? c) (char=? c #\_)) c)
50                           (else #\_))))
51            (loop (cons c1 lst) (cdr cs)))))))
52                           
53
54(define (nmodl-state-name n s)
55  (nmodl-name (if n (s+ n s) s)))
56
57(define (rhsvars rhs)
58  (enum-freevars rhs (list) (list)))
59
60(define (rhsexpr/NMODL expr)
61  (match expr 
62         (('if . es)  `(if . ,(map (lambda (x) (rhsexpr/NMODL x)) es)))
63         (('let bnds body) `(let ,(map (lambda (x) (list (car x) (rhsexpr/NMODL (cadr x)))) bnds) ,(rhsexpr/NMODL body)))
64         (('pow x y)  (if (and (integer? y)  (positive? y))
65                          (if (> y 1)  (let ((tmp (gensym "x")))
66                                         `(let ((,tmp ,x)) (* . ,(list-tabulate (inexact->exact y) (lambda (i) tmp)))))
67                              x)
68                          (if (and (number? y) (zero? y)) 1.0 expr)))
69         ((s . es)    (if (symbol? s)   (cons (if (member s builtin-fns) s (nmodl-name s)) (map (lambda (x) (rhsexpr/NMODL x)) es)) expr))
70         (id          (if (symbol? id) (nmodl-name id) id))))
71
72
73(define-syntax pp
74  (syntax-rules ()
75    ((pp indent val ...) (ppf indent (quasiquote val) ...))))
76
77
78(define (letblk/NMODL e1 e2)
79  (cond ((equal? e1 (doc:empty)) (doc:group (doc:nest 2 e2)))
80        ((equal? e2 (doc:empty)) (doc:group (doc:nest 2 e1)))
81        (else (doc:connect (doc:group (doc:nest 2 e1))
82                           (doc:group (doc:nest 2 e2))))))
83       
84(define ifthen/NMODL  (doc:ifthen 0 (doc:text "if") (doc:text "") (doc:text "else")))
85(define group/NMODL   (doc:block 2 (doc:text "(") (doc:text ")")))
86(define block/NMODL   (doc:block 2 (doc:text "{") (doc:text "}")))
87(define binop/NMODL   (doc:binop 2))
88
89(define (format-op/NMODL indent op args)
90  (let ((op1 (doc:text (->string op))))
91    (let ((res
92           (if (null? args) op1
93               (match args
94                      ((x)           (doc:connect op1 x))
95                      ((x y)         (binop/NMODL x op1 y))
96                      ((x y z)       (binop/NMODL x op1 (binop/NMODL y op1 z)))
97                      (lst           (let* ((n   (length lst))
98                                            (n/2 (inexact->exact (round (/ n 2)))))
99                                       (binop/NMODL (format-op/NMODL indent op (take lst n/2 )) op1 
100                                                    (format-op/NMODL indent op (drop lst n/2 )))))))))
101      res)))
102
103
104(define (format-conseq-op/NMODL indent op args)
105  (let ((op1 (doc:text (->string op))))
106    (if (null? args) op1
107        (match args
108               ((x)      (doc:concat (list op1 x)))
109               ((x y)    (doc:concat (intersperse (list x op1 y) (doc:space))))
110               ((x y z)  (doc:concat (intersperse (list x op1 y op1 z) (doc:space))))
111               (lst      (let* ((n   (length lst))
112                                (n/2 (inexact->exact (round (/ n 2)))))
113                           (doc:concat 
114                            (intersperse 
115                             (list (format-conseq-op/NMODL indent op (take lst n/2 )) op1 
116                                   (format-conseq-op/NMODL indent op (drop lst n/2 )))
117                             (doc:space)))))))))
118
119(define (format-fncall/NMODL indent op args)
120  (let ((op1 (doc:text (->string op))))
121    (doc:cons op1 (group/NMODL ((doc:list indent identity (lambda () (doc:text ", "))) args)))))
122
123(define (name-normalize expr)
124  (match expr 
125         (('if c t e)  `(if ,(name-normalize c) ,(name-normalize t) ,(name-normalize e)))
126         (('let bs e)
127          `(let ,(map (lambda (b) `(,(car b) ,(name-normalize (cadr b)))) bs) ,(name-normalize e)))
128         ((f . es) 
129          (cons f (map name-normalize es)))
130         ((? symbol? ) (nmodl-name expr))
131         ((? atom? ) expr)))
132
133
134(define (canonicalize-expr/NMODL expr)
135  (let ((subst-convert  (subst-driver (lambda (x) (and (symbol? x) x)) 
136                                      nemo:binding? identity nemo:bind 
137                                      nemo:subst-term)))
138    (let* ((expr1 (if-convert expr))
139           (expr2 (subst-convert expr1 subst-empty))
140           (expr3 (let-lift expr2))
141           (expr4 (name-normalize expr3)))
142      expr4)))
143
144
145(define (format-expr/NMODL indent expr . rest) 
146  (let-optionals rest ((rv #f))
147   (let ((indent+ (+ 2 indent)))
148    (match expr
149       (('let bindings body)
150        (letblk/NMODL
151         (fold-right 
152           (lambda (x ax)
153             (let ((res
154                    (letblk/NMODL
155                     (match (second x)
156                            (('if c t e)
157                             (ifthen/NMODL
158                              (group/NMODL (format-expr/NMODL indent c))
159                              (block/NMODL (format-expr/NMODL indent t (first x)))
160                              (block/NMODL (format-expr/NMODL indent e (first x)))))
161                            (else
162                             (format-op/NMODL indent+ " = "
163                                              (list (format-expr/NMODL indent (first x) )
164                                                    (format-expr/NMODL indent (second x))))))
165                     ax)))
166               res
167               ))
168           (doc:empty) bindings)
169         (match body
170                (('let _ _) (format-expr/NMODL indent body rv))
171                (else
172                 (let ((body1 (doc:nest indent (format-expr/NMODL indent body))))
173                   (if rv  (format-op/NMODL indent " = " (list (format-expr/NMODL indent+ rv ) body1))
174                       body1))))))
175       
176       (('if . rest) (error 'format-expr/NMODL "invalid if statement " expr))
177
178       ((op . rest) 
179       (let ((op (case op ((pow)  '^) ((abs) 'fabs) ((ln) 'log) (else op))))
180         (let ((fe
181                (if (member op nmodl-ops)
182                    (let ((mdiv?  (any (lambda (x) (match x (('* . _) #t) (('/ . _) #t) (else #f))) rest))
183                          (mul?   (any (lambda (x) (match x (('* . _) #t) (else #f))) rest))
184                          (plmin? (any (lambda (x) (match x (('+ . _) #t) (('- . _) #t) (else #f))) rest)))
185                      (case op
186                        ((/) 
187                         (format-op/NMODL indent op 
188                                          (map (lambda (x) 
189                                                 (let ((fx (format-expr/NMODL indent+ x)))
190                                                   (if (or (symbol? x) (number? x)) fx
191                                                       (if (or mul? plmin?) (group/NMODL fx) fx)))) rest)))
192                        ((*) 
193                         (format-op/NMODL indent op 
194                                          (map (lambda (x) 
195                                                 (let ((fx (format-expr/NMODL indent+ x)))
196                                                   (if (or (symbol? x) (number? x)) fx
197                                                       (if plmin? (group/NMODL fx) fx)))) rest)))
198                       
199                        ((^) 
200                         (format-op/NMODL indent op 
201                                          (map (lambda (x) 
202                                                 (let ((fx (format-expr/NMODL indent+ x)))
203                                                   (if (or (symbol? x)  (number? x)) fx
204                                                       (if (or mdiv? plmin?) (group/NMODL fx) fx)))) rest)))
205                       
206                        (else
207                         (format-op/NMODL indent op 
208                                          (map (lambda (x) 
209                                                 (let ((fx (format-expr/NMODL indent+ x))) fx)) rest)))))
210                   
211                    (let ((op (case op ((neg) '-) (else op))))
212                      (format-fncall/NMODL indent op (map (lambda (x) (format-expr/NMODL indent+ x)) rest))))))
213           (if rv 
214               (format-op/NMODL indent " = " (list (format-expr/NMODL indent+ rv ) fe))
215               fe))))
216     
217      (else  (let ((fe (doc:text (->string expr))))
218               (if rv 
219                   (format-op/NMODL indent " = " (list (format-expr/NMODL indent+ rv ) fe))
220                   fe)))))))
221               
222
223         
224(define (expr->string/NMODL x . rest)
225  (let-optionals rest ((rv #f) (width 72))
226    (sdoc->string (doc:format width (format-expr/NMODL 2 x rv)))))
227 
228
229(define (format-conseq/NMODL indent expr . rest) 
230  (let-optionals rest ((rv #f))
231   (let ((indent+ (+ 2 indent)))
232    (match expr
233       (('let bindings body)
234        (letblk/NMODL
235         (fold-right 
236           (lambda (x ax)
237             (letblk/NMODL
238              (match (second x)
239                     (('if c t e)
240                      (ifthen/NMODL
241                       (group/NMODL (format-conseq/NMODL indent c))
242                       (block/NMODL (format-conseq/NMODL indent t (first x)))
243                       (block/NMODL (format-conseq/NMODL indent e (first x)))))
244                     (else
245                      (format-conseq-op/NMODL indent+ " = "
246                                       (list (format-conseq/NMODL indent (first x) )
247                                             (format-conseq/NMODL indent (second x))))))
248              ax))
249           (doc:empty) bindings)
250         (let ((body1 (doc:nest indent (format-conseq/NMODL indent body))))
251           (if rv  (format-conseq-op/NMODL indent " = " (list (format-conseq/NMODL indent+ rv ) body1))
252               body1))))
253       
254       (('if . rest) (error 'format-conseq/NMODL "invalid if statement " expr))
255
256       ((op . rest) 
257        (let ((op (case op ((pow)  '^) ((abs) 'fabs) (else op))))
258          (let ((fe
259                (if (member op nmodl-ops)
260                    (let ((mdiv?  (any (lambda (x) (match x (('* . _) #t) (('/ . _) #t) (else #f))) rest))
261                          (mul?   (any (lambda (x) (match x (('* . _) #t) (else #f))) rest))
262                          (plmin? (any (lambda (x) (match x (('+ . _) #t) (('- . _) #t) (else #f))) rest)))
263                      (case op
264                        ((/) 
265                         (format-conseq-op/NMODL indent op 
266                                          (map (lambda (x) 
267                                                 (let ((fx (format-conseq/NMODL indent+ x)))
268                                                   (if (or (symbol? x) (number? x)) fx
269                                                       (if (or mul? plmin?) (group/NMODL fx) fx)))) rest)))
270                        ((*) 
271                         (format-conseq-op/NMODL indent op 
272                                          (map (lambda (x) 
273                                                 (let ((fx (format-conseq/NMODL indent+ x)))
274                                                   (if (or (symbol? x) (number? x)) fx
275                                                       (if plmin? (group/NMODL fx) fx)))) rest)))
276                       
277                        ((^) 
278                         (format-conseq-op/NMODL indent op 
279                                          (map (lambda (x) 
280                                                 (let ((fx (format-conseq/NMODL indent+ x)))
281                                                   (if (or (symbol? x)  (number? x)) fx
282                                                       (if (or mdiv? plmin?) (group/NMODL fx) fx)))) rest)))
283                       
284                        (else
285                         (format-conseq-op/NMODL indent op 
286                                          (map (lambda (x) 
287                                                 (let ((fx (format-conseq/NMODL indent+ x))) fx)) rest)))))
288                   
289                    (case op
290                      ((neg) (format-conseq-op/NMODL indent '* (map (lambda (x) (format-conseq/NMODL indent+ x)) 
291                                                             (cons "(-1)" rest))))
292                      (else  (format-fncall/NMODL indent op (map (lambda (x) (format-conseq/NMODL indent+ x)) 
293                                                                 rest)))))))
294
295           (if rv (format-conseq-op/NMODL indent " = " (list (format-conseq/NMODL indent+ rv ) fe)) fe))))
296     
297      (else  (let ((fe (doc:text (->string expr))))
298               (if rv 
299                   (format-conseq-op/NMODL indent " = " (list (format-conseq/NMODL indent+ rv ) fe))
300                   fe)))))))
301               
302
303(define (conserve-conseq->string/NMODL x val . rest)
304  (let-optionals rest ((width 72))
305    (s+ "CONSERVE " (sdoc->string (doc:format width (format-conseq/NMODL 2 x #f))) 
306        " = " (number->string val))))
307 
308
309(define (make-define-fn table? min-v max-v with depend)
310  (lambda (indent n proc)
311    (let ((lst (procedure-data proc))
312          (indent+ (+ 2 indent)))
313      (let ((rt       (lookup-def 'rt lst))
314            (formals  (lookup-def 'formals lst))
315            (vars     (lookup-def 'vars lst))
316            (body     (lookup-def 'body lst)))
317        (pp indent ,nl (FUNCTION ,(nmodl-name n) (,(slp ", " vars)) "{" ))
318        (let* ((body0 (rhsexpr/NMODL body))
319               (body1 (canonicalize-expr/NMODL body0))
320               (lbs   (enum-bnds body1 (list))))
321          (if (not (null? lbs)) (pp indent+ (LOCAL ,(slp ", " lbs))))
322          (if (and table? min-v max-v with)
323              (match vars
324                     (('v)  (pp indent+ (TABLE ,@(if depend `(DEPEND ,depend) `(""))
325                                               FROM ,min-v TO ,max-v WITH ,with)))
326                     (else  (void))))
327          (pp indent+ ,(expr->string/NMODL body1 (nmodl-name n))))
328        (pp indent "}"))) 
329    ))
330
331(define (expeuler dt name rhs)
332  (define (isname? x) (equal? x name))
333  (let ((res 
334         (match rhs
335                ((or ('- A ('* B (and x (? isname?))))
336                     ('+ ('neg ('* B (and x (? isname?)))) A))
337                 (let ((xexp (string->symbol (s+ x 'exp))))
338                   `(let ((,xexp (exp (* (neg ,B) ,dt))))
339                      (+ (* ,x ,xexp)  (* (- 1 ,xexp) (/ ,A ,B))))))
340
341                ((or ('- A ('* (and x (? isname?)) . B))
342                     ('+ ('neg ('* (and x (? isname?)) . B)) A))
343                 (let ((xexp (string->symbol (s+ x 'exp)))
344                       (B1   (if (null? (cdr B)) (car B) `(* ,@B))))
345                   `(let ((,xexp (exp (* (neg ,B1) ,dt))))
346                      (+ (* ,x ,xexp)  (* (- 1 ,xexp) (/ ,A ,B1))))))
347               
348                (('+ ('neg ('* (and x1 (? isname?)) Alpha))
349                     ('* ('- 1 (and x2 (? isname?))) Beta))
350                 (let ((A  Alpha)
351                       (B  `(+ ,Alpha ,Beta)))
352                   (let ((xexp (string->symbol (s+ x1 'exp))))
353                     `(let ((,xexp (exp (* (neg ,B) ,dt))))
354                        (+ (* ,x1 ,xexp) (* (- 1 ,xexp) (/ ,A ,B)))))))
355               
356                (('let bnds body)
357                 `(let ,bnds ,(expeuler dt name body)))
358               
359                (else (nemo:error 'nemo:expeuler: "unable to rewrite equation " rhs 
360                                  "in exponential Euler form")))))
361
362    res))
363 
364
365(define (reaction-transition-eqs n initial open transitions conserve power method)
366  (match-let (((g cnode node-subs)  (transitions-graph n open transitions conserve nmodl-state-name)))
367     (let* ((out-edges  (g 'out-edges))
368            (in-edges   (g 'in-edges))
369            (nodes      ((g 'nodes))))
370       ;; generate differential equations for each state in the transitions system
371      (let ((eqs    (fold (lambda (s ax) 
372                            (if (and cnode (= (first cnode) (first s) )) ax
373                                (let* ((out   (out-edges (first s)))
374                                       (in    (in-edges (first s)))
375                                       (open? (eq? (second s) open))
376                                       (name  (nmodl-name (lookup-def (second s) node-subs))))
377
378                                  (let* ((rhs1  (cond ((and (not (null? out)) (not (null? in)))
379                                                       `(+ (neg ,(sum (map third out)))
380                                                           ,(sum (map third in))))
381                                                      ((and (not (null? out)) (null? in))
382                                                       `(neg ,(sum (map third out))))
383                                                      ((and (null? out) (not (null? in)))
384                                                       (sum (map third in)))))
385
386                                         (fbody0 (rhsexpr/NMODL rhs1))
387                                         (fbody1 (case method
388                                                   ((expeuler) (canonicalize-expr/NMODL (expeuler 'dt name fbody0)))
389                                                   (else       (canonicalize-expr/NMODL fbody0)))))
390                                    (cons (list name fbody1) ax))
391                                  )))
392                          (list) nodes)))
393        eqs))))
394
395
396           
397       
398
399(define (reaction-keqs n initial open transitions power)
400  (let* ((subst-convert  (subst-driver (lambda (x) (and (symbol? x) x)) 
401                                       nemo:binding? identity nemo:bind nemo:subst-term))
402         (state-list     (let loop ((lst (list)) (tlst transitions))
403                           (if (null? tlst)  (delete-duplicates lst eq?)
404                               (match (car tlst) 
405                                      (('-> (and (? symbol?) s0) (and (? symbol?) s1) rate-expr)
406                                       (loop (cons* s0 s1 lst) (cdr tlst)))
407                                      (((and (? symbol?) s0) '-> (and (? symbol? s1)) rate-expr)
408                                       (loop (cons* s0 s1 lst) (cdr tlst)))
409                                      (('<-> (and (? symbol?) s0) (and (? symbol?) s1) rate-expr1 rate-expr2)
410                                       (loop (cons* s0 s1 lst) (cdr tlst)))
411                                      (((and (? symbol?) s0) 'M-> (and (? symbol? s1)) rate-expr1 rate-expr2)
412                                       (loop (cons* s0 s1 lst) (cdr tlst)))
413                                      (else
414                                       (nemo:error 'nemo:nmodl-reaction-keqs: "invalid transition equation " 
415                                                   (car tlst) " in state complex " n))
416                                      (else (loop lst (cdr tlst)))))))
417         (state-subs     (fold (lambda (s ax) (subst-extend s (nmodl-state-name n s) ax)) subst-empty state-list)))
418    ;; generate kinetic equations for each edge in the transitions system
419    (list n 
420          (map
421           (lambda (e) 
422             (match e
423                    (('-> s0 s1 rexpr)
424                     (let ((i  (lookup-def s0 state-subs))
425                           (j  (lookup-def s1 state-subs)))
426                       `(-> ,i ,j ,(canonicalize-expr/NMODL 
427                                    (subst-convert rexpr state-subs)))))
428                   
429                    ((s0 '-> s1 rexpr)
430                     (let ((i  (lookup-def s0 state-subs))
431                           (j  (lookup-def s1 state-subs)))
432                       `(-> ,i ,j ,(canonicalize-expr/NMODL 
433                                    (subst-convert rexpr state-subs)))))
434                   
435                    (('<-> s0 s1 rexpr1 rexpr2)
436                     (let ((i  (lookup-def s0 state-subs))
437                           (j  (lookup-def s1 state-subs)))
438                       `(<-> ,i ,j 
439                             ,(canonicalize-expr/NMODL (subst-convert rexpr1 state-subs))
440                             ,(canonicalize-expr/NMODL (subst-convert rexpr2 state-subs)))))
441                   
442                    ((s0 '<-> s1 rexpr1 rexpr2)
443                     (let ((i  (lookup-def s0 state-subs))
444                           (j  (lookup-def s1 state-subs)))
445                       `(<-> ,i ,j 
446                             ,(canonicalize-expr/NMODL (subst-convert rexpr1 state-subs))
447                             ,(canonicalize-expr/NMODL (subst-convert rexpr2 state-subs)))))
448                   
449                   
450                    (else (nemo:error 'nemo:nmodl-reaction-keqs: "invalid transition equation " 
451                                      e " in state complex " n))))
452           transitions))))
453       
454
455
456(define (state-init n init)
457  (let* ((init  (rhsexpr/NMODL init))
458         (init1 (canonicalize-expr/NMODL init)))
459    (list (nmodl-name n) init1)))
460
461
462(define (asgn-eq n rhs)
463  (let* ((fbody   (rhsexpr/NMODL rhs))
464         (fbody1  (canonicalize-expr/NMODL fbody)))
465    (list (nmodl-name n) fbody1)))
466
467
468(define (reaction-eq n open transitions conserve)
469  (if (symbol? open)
470      (list (nmodl-name n) (nmodl-state-name n open))
471      (list (nmodl-name n) (sum (map (lambda (x) (nmodl-state-name n x)) open)))
472      ))
473
474
475(define (poset->reaction-eq-defs poset sys kinetic)
476  (fold-right
477   (lambda (lst ax)
478     (fold  (lambda (x ax) 
479              (match-let (((i . n)  x))
480                         (let ((en (hash-table-ref sys n)))
481                           (if (nemo:quantity? en)
482                               (cases nemo:quantity en
483                                      (REACTION  (name initial open transitions conserve power) 
484                                                 (cons (reaction-eq name open transitions conserve) ax))
485
486                                      (else  ax))
487                               ax))))
488            ax lst))
489   (list) poset))
490
491
492(define (poset->asgn-eq-defs poset sys)
493  (fold-right
494   (lambda (lst ax)
495     (fold  (lambda (x ax) 
496              (match-let (((i . n)  x))
497                         (let ((en (hash-table-ref sys n)))
498                           (if (nemo:quantity? en)
499                               (cases nemo:quantity en
500                                      (ASGN  (name value rhs) (cons (asgn-eq name rhs) ax))
501                                      (else  ax))
502                               ax))))
503            ax lst))
504   (list) poset))
505
506
507(define (poset->rate-eq-defs poset sys kinetic method)
508  (fold-right
509   (lambda (lst ax)
510     (fold  (lambda (x ax) 
511              (match-let (((i . n)  x))
512                         (let ((en (hash-table-ref sys n)))
513                           (if (and (not (member n kinetic)) (nemo:quantity? en))
514                               (cases nemo:quantity en
515                                      (REACTION  (name initial open transitions conserve power) 
516                                                 (append (reaction-transition-eqs name initial open transitions 
517                                                                                  conserve power method) ax))
518                                     
519                                      (RATE (name initial rhs power)
520                                            (let ((fbody0  (rhsexpr/NMODL rhs))
521                                                  (dy      (nmodl-name name )))
522                                              (case method
523                                                ((expeuler) 
524                                                 (cons (list dy (canonicalize-expr/NMODL (expeuler 'dt name fbody0))) 
525                                                       ax))
526                                                (else
527                                                 (cons (list dy (canonicalize-expr/NMODL fbody0)) ax)))))
528
529                                      (else  ax))
530                               ax))))
531            ax lst))
532   (list) poset))
533
534
535(define (poset->kinetic-eq-defs poset sys kinetic)
536  (fold-right
537   (lambda (lst ax)
538     (fold  (lambda (x ax) 
539              (match-let (((i . n)  x))
540                         (let ((en (hash-table-ref sys n)))
541                           (if (and (member n kinetic) (nemo:quantity? en))
542                               (cases nemo:quantity en
543                                      (REACTION  (name initial open transitions conserve power) 
544                                                 (cons (reaction-keqs name initial open transitions power) ax))
545                                      (else  ax))
546                               ax))))
547            ax lst))
548   (list) poset))
549
550
551(define (poset->state-init-defs poset sys)
552  (fold-right
553   (lambda (lst ax)
554     (fold  (lambda (x ax) 
555              (match-let (((i . n)  x))
556                         (let ((en (hash-table-ref sys n)))
557                           (if (nemo:quantity? en)
558                               (cases nemo:quantity en
559                                      (REACTION  (name initial open transitions conserve power)
560                                                 (if (nemo:rhs? initial)
561                                                     (cons* (state-init name initial) 
562                                                            (state-init (nmodl-state-name name open) name) ax) 
563                                                     ax))
564                                      (RATE  (name initial rhs power)
565                                             (if (nemo:rhs? initial)
566                                                 (cons (state-init name initial) ax) 
567                                                 ax))
568                                      (else  ax))
569                               ax))))
570            ax lst))
571   (list) poset))
572
573(define (poset->state-conserve-eq-defs poset sys)
574  (fold-right
575   (lambda (lst ax)
576     (fold  (lambda (x ax) 
577              (match-let (((i . n)  x))
578                         (let ((en (hash-table-ref sys n)))
579                           (if (nemo:quantity? en)
580                               (cases nemo:quantity en
581                                      (REACTION (name initial open transitions conserve power)
582                                                (if (and (list? conserve) (every nemo:conseq? conserve))
583                                                    (cons (state-conseqs (nmodl-name name) transitions conserve
584                                                                        nmodl-state-name) ax) 
585                                                    ax))
586                                      (else  ax))
587                               ax))))
588            ax lst))
589   (list) poset))
590
591
592(define (find-locals defs)
593  (concatenate (map (lambda (def) 
594                      (match def 
595                             (('let bnds body) 
596                              (let ((bexprs (map second bnds)))
597                                (concatenate (list (map first bnds) 
598                                                   (find-locals bexprs )
599                                                   (find-locals (list body))))))
600                             (('if c t e)      (append (find-locals (list t)) (find-locals (list e))))
601                             ((s . rest)       (find-locals rest))
602                             (else (list)))) 
603                    defs)))
604
605
606(define (rate/reaction-power sys n)
607  (let ((en (hash-table-ref sys n)))
608    (if (nemo:quantity? en)
609        (cases nemo:quantity en
610               (REACTION  (name initial open transitions conserve power) 
611                          power)
612               (RATE    (name initial rhs power)
613                        power)
614               (else  #f)) 
615        #f)))
616
617
618(define (bucket-partition p lst)
619  (let loop ((lst lst) (ax (list)))
620    (if (null? lst) ax
621        (let ((x (car lst)))
622          (let bkt-loop ((old-bkts ax) (new-bkts (list)))
623            (if (null? old-bkts) (loop (cdr lst) (cons (list x) new-bkts))
624                (if (p x (caar old-bkts ))
625                    (loop (cdr lst) (append (cdr old-bkts) (cons (cons x (car old-bkts)) new-bkts)))
626                    (bkt-loop (cdr old-bkts) (cons (car old-bkts) new-bkts)))))))))
627
628
629
630(define (nemo:nmodl-translator sys . rest)
631  (define (cid x)  (second x))
632  (define (cn x)   (first x))
633  (let-optionals rest ((method 'cnexp) (table? #f) (min-v -100) (max-v 100) (step 0.5) 
634                       (depend #f)  (kinetic (list)) (linear? #f))
635  (match-let ((($ nemo:quantity 'DISPATCH  dis) (hash-table-ref sys (nemo-intern 'dispatch))))
636    (let ((eval-const  (let ((eval-const (dis 'eval-const)))
637                         (lambda (x q) (eval-const sys x q))))
638          (imports     ((dis 'imports)  sys))
639          (exports     ((dis 'exports)  sys))
640          )
641      (let* ((indent        0)
642             (indent+       (+ 2 indent ))
643             (table-with    (and table? (inexact->exact (round (/ (abs (- max-v min-v)) step)))))
644             (sysname       (nmodl-name ((dis 'sysname) sys)))
645             (consts        ((dis 'consts)  sys))
646             (asgns         ((dis 'asgns)   sys))
647             (states        ((dis 'states)  sys))
648             (kinetic       (or kinetic '()))
649             (kinetic       (delete-duplicates
650                             (cond ((eq? kinetic 'all) (filter-map first states))
651                                   ((symbol? kinetic) 
652                                    (let ((sk (->string kinetic)))
653                                      (filter-map (lambda (s) (and s (and (string-suffix? sk (->string s)) s)) )
654                                                  (map first states))))
655                                   (else
656                                    (let ((kinetic (map ->string kinetic))
657                                          (ss      (map (compose ->string first) states)))
658                                      (concatenate
659                                       (map (lambda (sk)
660                                              (filter-map (lambda (s) (and (string-suffix? sk s) s))
661                                                          ss))
662                                            kinetic)))))))
663             (reactions     ((dis 'reactions) sys))
664             (rates         ((dis 'rates) sys))
665             (defuns        ((dis 'defuns)  sys))
666             (components    ((dis 'components) sys))
667             (g             (match-let (((state-list asgn-list g) ((dis 'depgraph*) sys))) g))
668             (poset         (vector->list ((dis 'depgraph->bfs-dist-poset) g)))
669             
670             (gate-complex-info    (nemo:gate-complex-query sys))
671             (gate-complexes       (lookup-def 'gate-complexes gate-complex-info))
672             (perm-ions     (map (match-lambda ((comp i e erev) `(,comp ,(nmodl-name i) ,(nmodl-name e) ,erev)))
673                                 (lookup-def 'perm-ions gate-complex-info)))
674             (acc-ions      (map (match-lambda ((comp i in out) `(,comp ,@(map nmodl-name (list i in out)))))
675                                 (lookup-def 'acc-ions gate-complex-info)))
676             (epools        (lookup-def 'pool-ions gate-complex-info))
677
678             (pool-ions     (map (lambda (lst) (map nmodl-name lst)) epools))
679
680             (i-gates       (lookup-def 'i-gates gate-complex-info))
681
682             (has-kinetic?  (or (not (null? (filter (lambda (x) (member (car x) kinetic)) states)))))
683             (has-ode?      (or (not (null? (filter (lambda (x) (not (member (car x) kinetic))) states)))
684                                (not (null? pool-ions))))
685
686             (asgn-eq-defs       (poset->asgn-eq-defs poset sys))
687             (reaction-eq-defs   (poset->reaction-eq-defs poset sys kinetic)) 
688             (rate-eq-defs       (reverse (poset->rate-eq-defs poset sys kinetic method)))
689             (kstate-eq-defs     (poset->kinetic-eq-defs poset sys kinetic))
690             (conserve-eq-defs   (poset->state-conserve-eq-defs poset sys))
691             (state-init-defs    (poset->state-init-defs poset sys))
692
693             )
694
695           (pp indent ,nl (TITLE ,sysname))
696           
697           (pp indent ,nl (NEURON "{"))
698           (let recur ((exports exports))
699             (if (not (null? exports)) 
700                 (begin
701                   (pp indent+ (RANGE ,(slp ", " (map nmodl-name (take exports (min 10 (length exports)))))))
702                   (recur (drop exports (min 10 (length exports)))))))
703
704           (let ((currents (append (map (lambda (gate-complex) (nmodl-name (s+ 'i_ (first gate-complex)))) gate-complexes )
705                                   (map (lambda (i-gate) (nmodl-name (s+ 'i_ (second i-gate)))) i-gates ))))
706             (if (not (null? currents)) (pp indent+ (RANGE ,(slp ", " currents)))))
707
708
709           (for-each (lambda (x)
710                       (case (first x)
711                         ((non-specific) 
712                          (pp indent+ (RANGE ,(third x))
713                              (NONSPECIFIC_CURRENT ,(second x))))
714                         (else
715                          (cond ((fourth x)
716                                 (pp indent+ (RANGE ,(second x))
717                                     (USEION ,(first x) READ ,(third x) WRITE ,(second x))))
718                                (else (pp indent+ (RANGE ,(second x))))))))
719                     (delete-duplicates perm-ions (lambda (x y) (eq? (car x) (car y)))))
720
721           
722           (if (null? acc-ions)
723               (for-each (lambda (pool-ion epool)
724                           (let ((valence (fifth epool)))
725                             (if valence
726                                 (pp indent+ (RANGE ,(slp ", " (list (second pool-ion) (third pool-ion))))
727                                     (USEION ,(fourth pool-ion) 
728                                             READ  ,(slp ", " (list (second pool-ion)))
729                                             WRITE ,(slp ", " (list (third pool-ion )))
730                                             VALENCE ,(inexact->exact (eval-const valence valence))))
731                                 (pp indent+ (RANGE ,(slp ", " (list (second pool-ion) (third pool-ion))))
732                                     (USEION ,(fourth pool-ion) 
733                                             READ  ,(slp ", " (list (second pool-ion)))
734                                             WRITE ,(slp ", " (list (third pool-ion )))))
735                                 )))
736                         pool-ions epools)
737               (for-each (lambda (acc-ion)
738                           (let ((pool-ion (assoc (first acc-ion) pool-ions)))
739                             (if pool-ion
740                                 (pp indent+ (RANGE ,(second acc-ion))
741                                     (USEION ,(first acc-ion) 
742                                             READ  ,(slp ", " (list (third acc-ion) (fourth acc-ion) (second pool-ion)))
743                                             WRITE ,(slp ", " (list (second acc-ion) (third pool-ion )))))
744                                 (pp indent+ (RANGE ,(second acc-ion))
745                                     (USEION ,(first acc-ion) 
746                                             READ ,(slp ", "  (list (third acc-ion) (fourth acc-ion) ))
747                                             WRITE ,(second acc-ion))))))
748                         (delete-duplicates acc-ions (lambda (x y) (eq? (car x) (car y))))))
749
750           (let* ((const-names   (map first consts))
751                  (is-const?     (lambda (x) (member x const-names)))
752                  (range-consts  (delete-duplicates 
753                                  (fold (lambda (def ax) 
754                                          (let* ((rhs   (second def))
755                                                 (vars  (rhsvars rhs)))
756                                            (append (filter is-const? vars) ax)))
757                                        (list) asgn-eq-defs ))))
758             (if (not (null? range-consts)) (pp indent+ (RANGE ,(slp ", " range-consts)))))
759           
760           
761           (pp indent "}")
762           
763           (let* ((define-fn (make-define-fn table? min-v max-v table-with depend)))
764             (for-each (lambda (fndef) 
765                         (if (not (member (car fndef) builtin-fns))
766                             (apply define-fn (cons indent fndef)))) 
767                       defuns))
768
769           (let* ((parameter-defs 
770                   (filter-map
771                    (lambda (nv)
772                      (and (not (member (first nv) nmodl-builtin-consts))
773                           (let ((v1 (canonicalize-expr/NMODL (second nv))))
774                             (list (first nv) v1))))
775                    consts))
776                  (parameter-locals  (find-locals (map second parameter-defs)))
777                  (state-defs 
778                   (append
779                    (map (lambda (st)
780                           (if (pair? st) (nmodl-state-name (first st) (second st)) 
781                               (nmodl-name st)))
782                         states)
783                    (map nmodl-name reactions)))
784                  (assigned-defs
785                   (filter-map
786                    (lambda (x) 
787                      (let ((x1 (nmodl-name x)))
788                        (and (not (or (member x1 state-defs) (assoc x1 parameter-defs)))
789                             x1)))
790                    (delete-duplicates
791                     (append asgns
792                             (map first imports) 
793                             (map second perm-ions) (map third perm-ions)
794                             (map second acc-ions)  (map fourth acc-ions)
795                             (map second pool-ions) (map third pool-ions)
796                             (map (lambda (gate-complex) (nmodl-name (s+ 'i_ (first gate-complex)))) gate-complexes )
797                             (map (lambda (i-gate) (nmodl-name (s+ 'i_ (second i-gate)))) i-gates )
798                             )))))
799             
800             (pp indent ,nl (PARAMETER "{"))
801             (if (not (null? parameter-locals)) (pp indent+ (LOCAL ,(slp ", " parameter-locals))))
802             (for-each (lambda (def)
803                         (let ((n (nmodl-name (first def))) (b (second def)))
804                           (pp indent+ ,(expr->string/NMODL b n)))) parameter-defs)
805             (case method  ((expeuler)  (pp indent+ dt)))
806             (pp indent "}")
807             (pp indent ,nl (STATE "{"))
808             (for-each (lambda (x) (pp indent+ ,x)) state-defs)
809             (pp indent "}")
810             (pp indent ,nl (ASSIGNED "{"))
811             (for-each (lambda (x) (pp indent+ ,x)) assigned-defs)
812             (pp indent "}"))
813
814           (if (not (null? asgns))
815               (begin
816                 (pp indent ,nl (PROCEDURE asgns () "{"))
817                 (let ((locals    (find-locals (map second asgn-eq-defs))) )
818                   (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals)))))
819#|
820                 This seems to cause a segmentation fault in nrnoc:
821
822                 (if (and table? min-v max-v table-with)
823                     (pp indent+ (TABLE ,(slp ", " (map first asgn-eq-defs))
824                                  ,@(if depend `(DEPEND ,depend) `(""))
825                                  FROM ,min-v TO ,max-v WITH ,table-with)))
826|#
827
828                 (for-each (lambda (def)
829                             (let ((n (nmodl-name (first def)) )
830                                   (b (second def)))
831                               (pp indent+ ,(expr->string/NMODL b n)))) asgn-eq-defs)
832                 (pp indent "}")))
833             
834           (if (not (null? reactions))
835               (begin
836                 (pp indent ,nl (PROCEDURE reactions () "{"))
837                 (let ((locals    (find-locals (map second reaction-eq-defs))) )
838                   (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
839                   (for-each (lambda (def)
840                               (let ((n (nmodl-name (first def))) (b (second def)))
841                                 (pp indent+ ,(expr->string/NMODL b n)))) 
842                             reaction-eq-defs))
843                 (pp indent "}")))
844
845           (if (not (null? pool-ions))
846               (begin
847                 (pp indent ,nl (PROCEDURE pools () "{"))
848                 (for-each (lambda (pool-ion)
849                             (pp indent+ (,(third pool-ion) = ,(first pool-ion))))
850                           pool-ions)
851                 (pp indent "}")))
852
853           (pp indent ,nl (BREAKPOINT "{"))
854           (let* ((i-eqs (filter-map
855                          (lambda (gate-complex) 
856
857                            (let* ((label             (first gate-complex))
858                                   (n                 (second gate-complex))
859                                   (subcomps          ((dis 'component-subcomps) sys n))
860                                   (acc               (lookup-def 'accumulating-substance subcomps))
861                                   (perm              (lookup-def 'permeating-ion subcomps))
862                                   (permqs            (and perm ((dis 'component-exports) sys (cid perm))))
863                                   (pore              (lookup-def 'pore subcomps))
864                                   (permeability      (lookup-def 'permeability subcomps))
865                                   (gate              (lookup-def 'gate subcomps))
866                                   (sts               (and gate ((dis 'component-exports) sys (cid gate)))))
867
868                              (if (and pore (null? permqs))
869                                  (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
870                                              "permeating-ion component lacks exported quantities"))
871
872                              (if (null? sts)
873                                  (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
874                                              "gate component lacks exported quantities"))
875
876                              (if (not (or pore permeability))
877                                  (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
878                                              "lacks any pore or permeability components"))
879 
880                              (cond ((and perm permeability gate)
881                                     (let* ((i     (nmodl-name (s+ 'i (cn perm))))
882                                            (pmax  (car ((dis 'component-exports) sys (cid permeability))))
883                                            (pwrs  (map (lambda (n) (rate/reaction-power sys n)) sts))
884                                            (sptms (map (lambda (st pwr) (if pwr `(pow ,st ,pwr) st)) sts pwrs))
885                                            (gion  `(* ,pmax ,@sptms)))
886                                         (list i #f gion (nmodl-name (s+ 'i_ label) ))))
887
888                                    ((and perm pore gate)
889
890                                     (case (cn perm)
891                                       ((non-specific)
892                                        (let* ((i     (nmodl-name 'i))
893                                               (e     (car permqs))
894                                               (gmax  (car ((dis 'component-exports) sys (cid pore))))
895                                               (pwrs  (map (lambda (n) (rate/reaction-power sys n)) sts))
896                                               (sptms (map (lambda (st pwr) (if pwr `(pow ,st ,pwr) st)) sts pwrs))
897                                               (gion  `(* ,gmax ,@sptms)))
898                                          (list i e gion (nmodl-name (s+ 'i_ label) ))))
899
900                                       (else
901                                        (let* ((i     (nmodl-name (s+ 'i (cn perm))))
902                                               (e     (nmodl-name (car permqs)))
903                                               (gmax  (car ((dis 'component-exports) sys (cid pore))))
904                                               (pwrs  (map (lambda (n) (rate/reaction-power sys n)) sts))
905                                               (sptms (map (lambda (st pwr) (if pwr `(pow ,st ,pwr) st)) sts pwrs))
906                                               (gion  `(* ,gmax ,@sptms)))
907
908                                          (list i e gion (nmodl-name (s+ 'i_ label)))))))
909
910                                    ((and perm pore)
911                                     (case (cn perm)
912                                       ((non-specific)
913                                        (let* ((i     (nmodl-name 'i))
914                                               (e     (car permqs))
915                                               (gmax  (car ((dis 'component-exports) sys (cid pore)))))
916                                          (list i e gmax (nmodl-name (s+ 'i_ label)))))
917                                       (else
918                                        (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
919                                                    (s+ "(" n ")")
920                                                    "lacks gate component"))))
921                                   
922                                    ((and acc pore gate)
923                                     (let* ((i     (nmodl-name (s+ 'i (cn acc))))
924                                            (gmax  (car ((dis 'component-exports) sys (cid pore))))
925                                            (pwrs  (map (lambda (n) (rate/reaction-power sys n)) sts))
926                                            (sptms (map (lambda (st pwr) (if pwr `(pow ,st ,pwr) st)) sts pwrs))
927                                            (gion  `(* ,gmax ,@sptms)))
928                                       (list i #f gion (nmodl-name (s+ 'i_ label) ))))
929                                   
930                                    (else (nemo:error 'nemo:nmodl-translator: "invalid ion channel definition " 
931                                                      label))
932                                    )))
933                          gate-complexes))
934
935                  (i-eqs  (fold  (lambda (i-gate ax) 
936                                   (let ((i-gate-var (first i-gate)))
937                                     (cons (list (nmodl-name 'i) #f i-gate-var (s+ 'i_ (second i-gate)) ) ax)))
938                                 i-eqs i-gates))
939
940                  (i-bkts (bucket-partition (lambda (x y) (eq? (car x) (car y))) i-eqs))
941
942                  (i-eqs  (fold (lambda (b ax) 
943                                  (match b 
944                                         ((and ps ((i e gion ii) . rst))
945                                          (let loop ((ps ps) (summands (list)) (eqs (list)))
946                                            (if (null? ps)
947
948                                                (let* ((sum0  (sum summands))
949                                                       (sum1  (rhsexpr/NMODL sum0))
950                                                       (sum2  (canonicalize-expr/NMODL sum1)))
951                                                  (append eqs (list (list i sum2)) ax))
952
953                                                (match-let (((i e gion ii) (car ps)))
954
955                                                  (loop (cdr ps) 
956                                                        (cons ii summands) 
957                                                        (let* ((expr0 (rhsexpr/NMODL (if e `(* ,gion (- v ,e)) gion)))
958                                                               (expr1 (canonicalize-expr/NMODL expr0)))
959                                                          (cons (list ii expr1) eqs)))))))
960                                           
961                                         ((i e gion ii)
962                                          (let* ((expr0  (rhsexpr/NMODL (if e `(* ,gion (- v ,e)) gion)))
963                                                 (expr1  (canonicalize-expr/NMODL expr0)))
964                                            (cons (list i expr1) ax)))
965                                         
966                                         (else ax)))
967                                (list) i-bkts))
968                 
969                  (locals (find-locals (map second i-eqs))))
970
971             (if (not (null? locals))    (pp indent+ (LOCAL ,(slp ", " locals))))
972             (if has-ode?
973                 (case method
974                   ((#f expeuler)  (pp indent+ (SOLVE states)))
975                   (else           (pp indent+ (SOLVE states METHOD ,method)))))
976             (if has-kinetic?   (pp indent+  (SOLVE kstates METHOD sparse)))
977             (if (not (null? reactions))   (pp indent+ (reactions ())))
978             (if (not (null? pool-ions)) (pp indent+ (pools ())))
979             (for-each (lambda (p) (pp indent+ ,(expr->string/NMODL (second p) (first p)))) i-eqs)
980             (pp indent "}"))
981           
982           (if has-ode?
983               (let ((locals   (find-locals (map second rate-eq-defs))))
984                 (case method
985                   ((expeuler) (pp indent ,nl (PROCEDURE states () "{")))
986                   (else       (pp indent ,nl (DERIVATIVE states "{"))))
987                 (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
988                 (if (not (null? asgns))     (pp indent+ (asgns ())))
989                 (let ((prime (case method 
990                                ((expeuler) identity)
991                                (else  (lambda (x) (s+ x "'"))))))
992                   (for-each (lambda (def)
993                               (let ((n (prime (first def)))
994                                     (b (second def)))
995                                 (pp indent+ ,(expr->string/NMODL b n))))
996                             rate-eq-defs))
997                 (pp indent "}")))
998
999           (if has-kinetic?
1000               (begin
1001                 (pp indent ,nl (KINETIC kstates "{"))
1002                 (let* ((exprs             (map second kstate-eq-defs))
1003                        (locals            (concatenate 
1004                                            (find-locals
1005                                             (append (map (lambda (x) (map fourth x)) exprs)
1006                                                     (map (lambda (x) (map fifth x)) exprs))))))
1007                   (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
1008                   (if (not (null? asgns))     (pp indent+ (asgns ())))
1009                   (for-each
1010                    (lambda (def)
1011                      (let* ((n             (first def))
1012                             (eqs           (second def))
1013                             (conserve-eqs  (lookup-def (nmodl-name n) conserve-eq-defs)))
1014                        (for-each
1015                         (lambda (eq)
1016                           (match eq
1017                                  (('-> s0 s1 rexpr) 
1018                                   (pp indent+ (~ ,s0 -> ,s1 (,(expr->string/NMODL rexpr)))))
1019                                  (('<-> s0 s1 rexpr1 rexpr2) 
1020                                   (pp indent+ (~ ,s0 <-> ,s1 (,(expr->string/NMODL rexpr1) #\,
1021                                                               ,(expr->string/NMODL rexpr2) 
1022                                                               ))))
1023                                  ))
1024                           eqs)
1025                        (if conserve-eqs
1026                            (for-each (lambda (eq) 
1027                                        (let ((val  (first eq))
1028                                              (expr (third eq)))
1029                                          (pp indent+ ,(conserve-conseq->string/NMODL expr val))))
1030                                    conserve-eqs))
1031                        ))
1032                    kstate-eq-defs))
1033                 (pp indent "}")))
1034           
1035           
1036           (let ((locals (find-locals (map second state-init-defs)))) 
1037               (pp indent ,nl (INITIAL "{"))
1038               (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
1039               (if (not (null? asgns))  (pp indent+ (asgns ())))
1040               (for-each (lambda (def)
1041                           (let ((n (first def)) (b (second def)))
1042                             (pp indent+ ,(expr->string/NMODL b n)))) state-init-defs)
1043
1044               (if has-kinetic?
1045                   (pp indent+ (SOLVE kstates STEADYSTATE sparse)))
1046               
1047               (pp indent "}")
1048
1049               (pp indent ,nl (PROCEDURE print_state () "{"))
1050
1051               (let ((lst (sort (map (compose ->string first) rate-eq-defs) string<?)))
1052                 (for-each (lambda (x)
1053                             (pp indent+ (printf (,(s+ #\" x " = %g\\n"  #\") ", " ,x ))))
1054                           lst))
1055
1056               (pp indent "}")
1057
1058               ))))))
1059)
Note: See TracBrowser for help on using the repository browser.