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

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

nemo: better diagnostic messages, some reorganization of example models, bug fixes in code generation

File size: 37.3 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 ((imports  ((dis 'imports)  sys))
637          (exports  ((dis 'exports)  sys)))
638      (let* ((indent        0)
639             (indent+       (+ 2 indent ))
640             (table-with    (and table? (inexact->exact (round (/ (abs (- max-v min-v)) step)))))
641             (sysname       (nmodl-name ((dis 'sysname) sys)))
642             (consts        ((dis 'consts)  sys))
643             (asgns         ((dis 'asgns)   sys))
644             (states        ((dis 'states)  sys))
645             (kinetic       (or kinetic '()))
646             (kinetic       (delete-duplicates
647                             (cond ((eq? kinetic 'all) (filter-map first states))
648                                   ((symbol? kinetic) 
649                                    (let ((sk (->string kinetic)))
650                                      (filter-map (lambda (s) (and s (and (string-suffix? sk (->string s)) s)) )
651                                                  (map first states))))
652                                   (else
653                                    (let ((kinetic (map ->string kinetic))
654                                          (ss      (map (compose ->string first) states)))
655                                      (concatenate
656                                       (map (lambda (sk)
657                                              (filter-map (lambda (s) (and (string-suffix? sk s) s))
658                                                          ss))
659                                            kinetic)))))))
660             (reactions     ((dis 'reactions) sys))
661             (rates         ((dis 'rates) sys))
662             (defuns        ((dis 'defuns)  sys))
663             (components    ((dis 'components) sys))
664             (g             (match-let (((state-list asgn-list g) ((dis 'depgraph*) sys))) g))
665             (poset         (vector->list ((dis 'depgraph->bfs-dist-poset) g)))
666             
667             (gate-complex-info    (nemo:gate-complex-query sys))
668             (gate-complexes       (lookup-def 'gate-complexes gate-complex-info))
669             (perm-ions     (map (match-lambda ((comp i e erev) `(,comp ,(nmodl-name i) ,(nmodl-name e) ,erev)))
670                                 (lookup-def 'perm-ions gate-complex-info)))
671             (acc-ions      (map (match-lambda ((comp i in out) `(,comp ,@(map nmodl-name (list i in out)))))
672                                 (lookup-def 'acc-ions gate-complex-info)))
673             (epools        (lookup-def 'pool-ions gate-complex-info))
674             (pool-ions     (map (lambda (lst) (map nmodl-name lst)) epools))
675
676             (i-gates       (lookup-def 'i-gates gate-complex-info))
677
678             (has-kinetic?  (or (not (null? (filter (lambda (x) (member (car x) kinetic)) states)))))
679             (has-ode?      (or (not (null? (filter (lambda (x) (not (member (car x) kinetic))) states)))
680                                (not (null? pool-ions))))
681
682             (asgn-eq-defs       (poset->asgn-eq-defs poset sys))
683             (reaction-eq-defs   (poset->reaction-eq-defs poset sys kinetic)) 
684             (rate-eq-defs       (reverse (poset->rate-eq-defs poset sys kinetic method)))
685             (kstate-eq-defs     (poset->kinetic-eq-defs poset sys kinetic))
686             (conserve-eq-defs   (poset->state-conserve-eq-defs poset sys))
687             (state-init-defs    (poset->state-init-defs poset sys))
688
689             )
690
691           (pp indent ,nl (TITLE ,sysname))
692           
693           (pp indent ,nl (NEURON "{"))
694           (let recur ((exports exports))
695             (if (not (null? exports)) 
696                 (begin
697                   (pp indent+ (RANGE ,(slp ", " (map nmodl-name (take exports (min 10 (length exports)))))))
698                   (recur (drop exports (min 10 (length exports)))))))
699
700           (let ((currents (append (map (lambda (gate-complex) (nmodl-name (s+ 'i_ (first gate-complex)))) gate-complexes )
701                                   (map (lambda (i-gate) (nmodl-name (s+ 'i_ (second i-gate)))) i-gates ))))
702             (if (not (null? currents)) (pp indent+ (RANGE ,(slp ", " currents)))))
703
704
705           (for-each (lambda (x)
706                       (case (first x)
707                         ((non-specific) 
708                          (pp indent+ (RANGE ,(third x))
709                              (NONSPECIFIC_CURRENT ,(second x))))
710                         (else
711                          (cond ((fourth x)
712                                 (pp indent+ (RANGE ,(second x))
713                                     (USEION ,(first x) READ ,(third x) WRITE ,(second x))))
714                                (else (pp indent+ (RANGE ,(second x))))))))
715                     (delete-duplicates perm-ions (lambda (x y) (eq? (car x) (car y)))))
716
717           
718           (if (null? acc-ions)
719               (for-each (lambda (pool-ion)
720                           (pp indent+ (RANGE ,(slp ", " (list (second pool-ion) (third pool-ion))))
721                               (USEION ,(fourth pool-ion) 
722                                       READ  ,(slp ", " (list (second pool-ion)))
723                                       WRITE ,(slp ", " (list (third pool-ion ))))))
724                         pool-ions)
725               (for-each (lambda (acc-ion)
726                           (let ((pool-ion (assoc (first acc-ion) pool-ions)))
727                             (if pool-ion
728                                 (pp indent+ (RANGE ,(second acc-ion))
729                                     (USEION ,(first acc-ion) 
730                                             READ  ,(slp ", " (list (third acc-ion) (fourth acc-ion) (second pool-ion)))
731                                             WRITE ,(slp ", " (list (second acc-ion) (third pool-ion )))))
732                                 (pp indent+ (RANGE ,(second acc-ion))
733                                     (USEION ,(first acc-ion) 
734                                             READ ,(slp ", "  (list (third acc-ion) (fourth acc-ion) ))
735                                             WRITE ,(second acc-ion))))))
736                         (delete-duplicates acc-ions (lambda (x y) (eq? (car x) (car y))))))
737
738           (let* ((const-names   (map first consts))
739                  (is-const?     (lambda (x) (member x const-names)))
740                  (range-consts  (delete-duplicates 
741                                  (fold (lambda (def ax) 
742                                          (let* ((rhs   (second def))
743                                                 (vars  (rhsvars rhs)))
744                                            (append (filter is-const? vars) ax)))
745                                        (list) asgn-eq-defs ))))
746             (if (not (null? range-consts)) (pp indent+ (RANGE ,(slp ", " range-consts)))))
747           
748           
749           (pp indent "}")
750           
751           (let* ((define-fn (make-define-fn table? min-v max-v table-with depend)))
752             (for-each (lambda (fndef) 
753                         (if (not (member (car fndef) builtin-fns))
754                             (apply define-fn (cons indent fndef)))) 
755                       defuns))
756
757           (let* ((parameter-defs 
758                   (filter-map
759                    (lambda (nv)
760                      (and (not (member (first nv) nmodl-builtin-consts))
761                           (let ((v1 (canonicalize-expr/NMODL (second nv))))
762                             (list (first nv) v1))))
763                    consts))
764                  (parameter-locals  (find-locals (map second parameter-defs)))
765                  (state-defs 
766                   (append
767                    (map (lambda (st)
768                           (if (pair? st) (nmodl-state-name (first st) (second st)) 
769                               (nmodl-name st)))
770                         states)
771                    (map nmodl-name reactions)))
772                  (assigned-defs
773                   (filter-map
774                    (lambda (x) 
775                      (let ((x1 (nmodl-name x)))
776                        (and (not (or (member x1 state-defs) (assoc x1 parameter-defs)))
777                             x1)))
778                    (delete-duplicates
779                     (append asgns
780                             (map first imports) 
781                             (map second perm-ions) (map third perm-ions)
782                             (map second acc-ions)  (map fourth acc-ions)
783                             (map second pool-ions) (map third pool-ions)
784                             (map (lambda (gate-complex) (nmodl-name (s+ 'i_ (first gate-complex)))) gate-complexes )
785                             (map (lambda (i-gate) (nmodl-name (s+ 'i_ (second i-gate)))) i-gates )
786                             )))))
787             
788             (pp indent ,nl (PARAMETER "{"))
789             (if (not (null? parameter-locals)) (pp indent+ (LOCAL ,(slp ", " parameter-locals))))
790             (for-each (lambda (def)
791                         (let ((n (nmodl-name (first def))) (b (second def)))
792                           (pp indent+ ,(expr->string/NMODL b n)))) parameter-defs)
793             (case method  ((expeuler)  (pp indent+ dt)))
794             (pp indent "}")
795             (pp indent ,nl (STATE "{"))
796             (for-each (lambda (x) (pp indent+ ,x)) state-defs)
797             (pp indent "}")
798             (pp indent ,nl (ASSIGNED "{"))
799             (for-each (lambda (x) (pp indent+ ,x)) assigned-defs)
800             (pp indent "}"))
801
802           (if (not (null? asgns))
803               (begin
804                 (pp indent ,nl (PROCEDURE asgns () "{"))
805                 (let ((locals    (find-locals (map second asgn-eq-defs))) )
806                   (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals)))))
807#|
808                 This seems to cause a segmentation fault in nrnoc:
809
810                 (if (and table? min-v max-v table-with)
811                     (pp indent+ (TABLE ,(slp ", " (map first asgn-eq-defs))
812                                  ,@(if depend `(DEPEND ,depend) `(""))
813                                  FROM ,min-v TO ,max-v WITH ,table-with)))
814|#
815
816                 (for-each (lambda (def)
817                             (let ((n (nmodl-name (first def)) )
818                                   (b (second def)))
819                               (pp indent+ ,(expr->string/NMODL b n)))) asgn-eq-defs)
820                 (pp indent "}")))
821             
822           (if (not (null? reactions))
823               (begin
824                 (pp indent ,nl (PROCEDURE reactions () "{"))
825                 (let ((locals    (find-locals (map second reaction-eq-defs))) )
826                   (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
827                   (for-each (lambda (def)
828                               (let ((n (nmodl-name (first def))) (b (second def)))
829                                 (pp indent+ ,(expr->string/NMODL b n)))) 
830                             reaction-eq-defs))
831                 (pp indent "}")))
832
833           (if (not (null? pool-ions))
834               (begin
835                 (pp indent ,nl (PROCEDURE pools () "{"))
836                 (for-each (lambda (pool-ion)
837                             (pp indent+ (,(third pool-ion) = ,(first pool-ion))))
838                           pool-ions)
839                 (pp indent "}")))
840
841           (pp indent ,nl (BREAKPOINT "{"))
842           (let* ((i-eqs (filter-map
843                          (lambda (gate-complex) 
844
845                            (let* ((label             (first gate-complex))
846                                   (n                 (second gate-complex))
847                                   (subcomps          ((dis 'component-subcomps) sys n))
848                                   (acc               (lookup-def 'accumulating-substance subcomps))
849                                   (perm              (lookup-def 'permeating-ion subcomps))
850                                   (permqs            (and perm ((dis 'component-exports) sys (cid perm))))
851                                   (pore              (lookup-def 'pore subcomps))
852                                   (permeability      (lookup-def 'permeability subcomps))
853                                   (gate              (lookup-def 'gate subcomps))
854                                   (sts               (and gate ((dis 'component-exports) sys (cid gate)))))
855
856                              (if (and pore (null? permqs))
857                                  (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
858                                              "permeating-ion component lacks exported quantities"))
859
860                              (if (null? sts)
861                                  (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
862                                              "gate component lacks exported quantities"))
863
864                              (if (not (or pore permeability))
865                                  (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
866                                              "lacks any pore or permeability components"))
867 
868                              (cond ((and perm permeability gate)
869                                     (let* ((i     (nmodl-name (s+ 'i (cn perm))))
870                                            (pmax  (car ((dis 'component-exports) sys (cid permeability))))
871                                            (pwrs  (map (lambda (n) (rate/reaction-power sys n)) sts))
872                                            (sptms (map (lambda (st pwr) (if pwr `(pow ,st ,pwr) st)) sts pwrs))
873                                            (gion  `(* ,pmax ,@sptms)))
874                                         (list i #f gion (nmodl-name (s+ 'i_ label) ))))
875
876                                    ((and perm pore gate)
877
878                                     (case (cn perm)
879                                       ((non-specific)
880                                        (let* ((i     (nmodl-name 'i))
881                                               (e     (car permqs))
882                                               (gmax  (car ((dis 'component-exports) sys (cid pore))))
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  `(* ,gmax ,@sptms)))
886                                          (list i e gion (nmodl-name (s+ 'i_ label) ))))
887
888                                       (else
889                                        (let* ((i     (nmodl-name (s+ 'i (cn perm))))
890                                               (e     (nmodl-name (car permqs)))
891                                               (gmax  (car ((dis 'component-exports) sys (cid pore))))
892                                               (pwrs  (map (lambda (n) (rate/reaction-power sys n)) sts))
893                                               (sptms (map (lambda (st pwr) (if pwr `(pow ,st ,pwr) st)) sts pwrs))
894                                               (gion  `(* ,gmax ,@sptms)))
895
896                                          (list i e gion (nmodl-name (s+ 'i_ label)))))))
897
898                                    ((and perm pore)
899                                     (case (cn perm)
900                                       ((non-specific)
901                                        (let* ((i     (nmodl-name 'i))
902                                               (e     (car permqs))
903                                               (gmax  (car ((dis 'component-exports) sys (cid pore)))))
904                                          (list i e gmax (nmodl-name (s+ 'i_ label)))))
905                                       (else
906                                        (nemo:error 'nemo:nmodl-translator: "ion channel definition " label
907                                                    (s+ "(" n ")")
908                                                    "lacks gate component"))))
909                                   
910                                    ((and acc pore gate)
911                                     (let* ((i     (nmodl-name (s+ 'i (cn acc))))
912                                            (gmax  (car ((dis 'component-exports) sys (cid pore))))
913                                            (pwrs  (map (lambda (n) (rate/reaction-power sys n)) sts))
914                                            (sptms (map (lambda (st pwr) (if pwr `(pow ,st ,pwr) st)) sts pwrs))
915                                            (gion  `(* ,gmax ,@sptms)))
916                                       (list i #f gion (nmodl-name (s+ 'i_ label) ))))
917                                   
918                                    (else (nemo:error 'nemo:nmodl-translator: "invalid ion channel definition " 
919                                                      label))
920                                    )))
921                          gate-complexes))
922
923                  (i-eqs  (fold  (lambda (i-gate ax) 
924                                   (let ((i-gate-var (first i-gate)))
925                                     (cons (list (nmodl-name 'i) #f i-gate-var (s+ 'i_ (second i-gate)) ) ax)))
926                                 i-eqs i-gates))
927
928                  (i-bkts (bucket-partition (lambda (x y) (eq? (car x) (car y))) i-eqs))
929
930                  (i-eqs  (fold (lambda (b ax) 
931                                  (match b 
932                                         ((and ps ((i e gion ii) . rst))
933                                          (let loop ((ps ps) (summands (list)) (eqs (list)))
934                                            (if (null? ps)
935
936                                                (let* ((sum0  (sum summands))
937                                                       (sum1  (rhsexpr/NMODL sum0))
938                                                       (sum2  (canonicalize-expr/NMODL sum1)))
939                                                  (append eqs (list (list i sum2)) ax))
940
941                                                (match-let (((i e gion ii) (car ps)))
942
943                                                  (loop (cdr ps) 
944                                                        (cons ii summands) 
945                                                        (let* ((expr0 (rhsexpr/NMODL (if e `(* ,gion (- v ,e)) gion)))
946                                                               (expr1 (canonicalize-expr/NMODL expr0)))
947                                                          (cons (list ii expr1) eqs)))))))
948                                           
949                                         ((i e gion ii)
950                                          (let* ((expr0  (rhsexpr/NMODL (if e `(* ,gion (- v ,e)) gion)))
951                                                 (expr1  (canonicalize-expr/NMODL expr0)))
952                                            (cons (list i expr1) ax)))
953                                         
954                                         (else ax)))
955                                (list) i-bkts))
956                 
957                  (locals (find-locals (map second i-eqs))))
958
959             (if (not (null? locals))    (pp indent+ (LOCAL ,(slp ", " locals))))
960             (if has-ode?
961                 (case method
962                   ((#f expeuler)  (pp indent+ (SOLVE states)))
963                   (else           (pp indent+ (SOLVE states METHOD ,method)))))
964             (if has-kinetic?   (pp indent+  (SOLVE kstates METHOD sparse)))
965             (if (not (null? reactions))   (pp indent+ (reactions ())))
966             (if (not (null? pool-ions)) (pp indent+ (pools ())))
967             (for-each (lambda (p) (pp indent+ ,(expr->string/NMODL (second p) (first p)))) i-eqs)
968             (pp indent "}"))
969           
970           (if has-ode?
971               (let ((locals   (find-locals (map second rate-eq-defs))))
972                 (case method
973                   ((expeuler) (pp indent ,nl (PROCEDURE states () "{")))
974                   (else       (pp indent ,nl (DERIVATIVE states "{"))))
975                 (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
976                 (if (not (null? asgns))     (pp indent+ (asgns ())))
977                 (let ((prime (case method 
978                                ((expeuler) identity)
979                                (else  (lambda (x) (s+ x "'"))))))
980                   (for-each (lambda (def)
981                               (let ((n (prime (first def)))
982                                     (b (second def)))
983                                 (pp indent+ ,(expr->string/NMODL b n))))
984                             rate-eq-defs))
985                 (pp indent "}")))
986
987           (if has-kinetic?
988               (begin
989                 (pp indent ,nl (KINETIC kstates "{"))
990                 (let* ((exprs             (map second kstate-eq-defs))
991                        (locals            (concatenate 
992                                            (find-locals
993                                             (append (map (lambda (x) (map fourth x)) exprs)
994                                                     (map (lambda (x) (map fifth x)) exprs))))))
995                   (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
996                   (if (not (null? asgns))     (pp indent+ (asgns ())))
997                   (for-each
998                    (lambda (def)
999                      (let* ((n             (first def))
1000                             (eqs           (second def))
1001                             (conserve-eqs  (lookup-def (nmodl-name n) conserve-eq-defs)))
1002                        (for-each
1003                         (lambda (eq)
1004                           (match eq
1005                                  (('-> s0 s1 rexpr) 
1006                                   (pp indent+ (~ ,s0 -> ,s1 (,(expr->string/NMODL rexpr)))))
1007                                  (('<-> s0 s1 rexpr1 rexpr2) 
1008                                   (pp indent+ (~ ,s0 <-> ,s1 (,(expr->string/NMODL rexpr1) #\,
1009                                                               ,(expr->string/NMODL rexpr2) 
1010                                                               ))))
1011                                  ))
1012                           eqs)
1013                        (if conserve-eqs
1014                            (for-each (lambda (eq) 
1015                                        (let ((val  (first eq))
1016                                              (expr (third eq)))
1017                                          (pp indent+ ,(conserve-conseq->string/NMODL expr val))))
1018                                    conserve-eqs))
1019                        ))
1020                    kstate-eq-defs))
1021                 (pp indent "}")))
1022           
1023           
1024           (let ((locals (find-locals (map second state-init-defs)))) 
1025               (pp indent ,nl (INITIAL "{"))
1026               (if (not (null? locals)) (pp indent+ (LOCAL ,(slp ", " locals))))
1027               (if (not (null? asgns))  (pp indent+ (asgns ())))
1028               (for-each (lambda (def)
1029                           (let ((n (first def)) (b (second def)))
1030                             (pp indent+ ,(expr->string/NMODL b n)))) state-init-defs)
1031
1032               (if has-kinetic?
1033                   (pp indent+ (SOLVE kstates STEADYSTATE sparse)))
1034               
1035               (pp indent "}")
1036
1037               (pp indent ,nl (PROCEDURE print_state () "{"))
1038
1039               (let ((lst (sort (map (compose ->string first) rate-eq-defs) string<?)))
1040                 (for-each (lambda (x)
1041                             (pp indent+ (printf (,(s+ #\" x " = %g\\n"  #\") ", " ,x ))))
1042                           lst))
1043
1044               (pp indent "}")
1045
1046               ))))))
1047)
Note: See TracBrowser for help on using the repository browser.