1 | |
---|
2 | ;; TODO: * uniquify state names in state complexes |
---|
3 | ;; * check that open states are valid |
---|
4 | ;; |
---|
5 | ;; |
---|
6 | ;; An extension for translating NeuroML models to NMODL descriptions. |
---|
7 | ;; |
---|
8 | ;; Copyright 2008 Ivan Raikov and the Okinawa Institute of Science and Technology |
---|
9 | ;; |
---|
10 | ;; This program is free software: you can redistribute it and/or |
---|
11 | ;; modify it under the terms of the GNU General Public License as |
---|
12 | ;; published by the Free Software Foundation, either version 3 of the |
---|
13 | ;; License, or (at your option) any later version. |
---|
14 | ;; |
---|
15 | ;; This program is distributed in the hope that it will be useful, but |
---|
16 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
18 | ;; General Public License for more details. |
---|
19 | ;; |
---|
20 | ;; A full copy of the GPL license can be found at |
---|
21 | ;; <http://www.gnu.org/licenses/>. |
---|
22 | ;; |
---|
23 | |
---|
24 | (require-extension syntax-case) |
---|
25 | (require-extension matchable) |
---|
26 | (require-extension strictly-pretty) |
---|
27 | (require-extension environments) |
---|
28 | (require-extension oru-core) |
---|
29 | (require-extension srfi-1) |
---|
30 | (require-extension srfi-4) |
---|
31 | (require-extension srfi-13) |
---|
32 | (require-extension srfi-14) |
---|
33 | (require-extension runcmd) |
---|
34 | (require-extension utils) |
---|
35 | (require-extension lolevel) |
---|
36 | (require-extension varsubst) |
---|
37 | (require-extension digraph) |
---|
38 | (require-extension datatype) |
---|
39 | |
---|
40 | (define (lookup-def k lst . rest) |
---|
41 | (let-optionals rest ((default #f)) |
---|
42 | (let ((kv (assoc k lst))) |
---|
43 | (if (not kv) default |
---|
44 | (match kv ((k v) v) (else (cdr kv))))))) |
---|
45 | |
---|
46 | (define (nmodl-name s) |
---|
47 | (let ((cs (string->list (->string s)))) |
---|
48 | (let loop ((lst (list)) (cs cs)) |
---|
49 | (if (null? cs) (string->symbol (list->string (reverse lst))) |
---|
50 | (let* ((c (car cs)) |
---|
51 | (c1 (cond ((or (char-alphabetic? c) (char-numeric? c) (char=? c #\_)) c) |
---|
52 | (else #\_)))) |
---|
53 | (loop (cons c1 lst) (cdr cs))))))) |
---|
54 | |
---|
55 | |
---|
56 | |
---|
57 | (define (enumprocs expr ax) |
---|
58 | (match expr |
---|
59 | (('if . es) (fold enumprocs ax es)) |
---|
60 | (('let bnds body) (fold enumprocs (fold enumprocs ax (map cadr bnds)) body)) |
---|
61 | ((s . es) (if (symbol? s) (cons s (fold enumprocs ax es)) ax)) |
---|
62 | (else ax))) |
---|
63 | |
---|
64 | (define (enumvars expr ax) |
---|
65 | (match expr |
---|
66 | (('if . es) (fold enumvars ax es)) |
---|
67 | (('let bnds body) (fold enumprocs (fold enumvars ax (map cadr bnds)) body)) |
---|
68 | ((s . es) (if (symbol? s) (fold enumvars ax es) ax)) |
---|
69 | (id (if (symbol? id) (cons id ax) ax)))) |
---|
70 | |
---|
71 | (define (enumbnds expr ax) |
---|
72 | (match expr |
---|
73 | (('if . es) (fold enumbnds ax es)) |
---|
74 | (('let bnds body) (enumbnds body (append (map car bnds) ax))) |
---|
75 | ((s . es) (if (symbol? s) (fold enumbnds ax es) ax)) |
---|
76 | (else ax))) |
---|
77 | |
---|
78 | (define (rhsvars rhs) |
---|
79 | (enumvars rhs (list))) |
---|
80 | |
---|
81 | (define (rhsexpr expr) |
---|
82 | (match expr |
---|
83 | (('if . es) `(if . ,(map (lambda (x) (rhsexpr x)) es))) |
---|
84 | (('pow x y) (if (and (integer? y) (positive? y)) |
---|
85 | (if (> y 1) (let ((tmp (gensym "x"))) |
---|
86 | `(let ((,tmp ,x)) (* . ,(list-tabulate (inexact->exact y) (lambda (i) tmp))))) |
---|
87 | x) |
---|
88 | expr)) |
---|
89 | ((s . es) (if (symbol? s) (cons s (map (lambda (x) (rhsexpr x)) es)) expr)) |
---|
90 | (id id))) |
---|
91 | |
---|
92 | |
---|
93 | ;;; Procedures for string concatenation and pretty-printing |
---|
94 | |
---|
95 | (define (s+ . lst) (string-concatenate (map ->string lst))) |
---|
96 | (define (sw+ lst) (string-intersperse (filter-map (lambda (x) (and x (->string x))) lst) " ")) |
---|
97 | (define (s\ p . lst) (string-intersperse (map ->string lst) p)) |
---|
98 | (define (sl\ p lst) (string-intersperse (map ->string lst) p)) |
---|
99 | (define nl "\n") |
---|
100 | |
---|
101 | (define (spaces n) (list->string (list-tabulate n (lambda (x) #\space)))) |
---|
102 | |
---|
103 | (define (ppf indent . lst) |
---|
104 | (let ((sp (spaces indent))) |
---|
105 | (for-each (lambda (x) |
---|
106 | (and x (match x |
---|
107 | ((i . x1) (if (and (number? i) (positive? i)) |
---|
108 | (for-each (lambda (x) (ppf (+ indent i) x)) x1) |
---|
109 | (print sp (sw+ x)))) |
---|
110 | (else (print sp (if (list? x) (sw+ x) x)))))) |
---|
111 | lst))) |
---|
112 | |
---|
113 | |
---|
114 | (define-syntax pp |
---|
115 | (syntax-rules () |
---|
116 | ((pp indent val ...) (ppf indent (quasiquote val) ...)))) |
---|
117 | |
---|
118 | (define ifthen/NMODL (doc:ifthen 2 (doc:text "if") (doc:text "") (doc:text "else"))) |
---|
119 | (define letblk/NMODL (doc:letblk 2 (doc:empty) (doc:break) (doc:empty))) |
---|
120 | (define group/NMODL (doc:block 2 (doc:text "(") (doc:text ")"))) |
---|
121 | (define block/NMODL (doc:block 2 (doc:text "{") (doc:text "}"))) |
---|
122 | (define binop/NMODL (doc:binop 2)) |
---|
123 | |
---|
124 | (define (format-op/NMODL indent op args) |
---|
125 | (let ((op1 (doc:text (->string op)))) |
---|
126 | (if (null? args) op1 |
---|
127 | (match args |
---|
128 | ((x) (doc:connect op1 x)) |
---|
129 | ((x y) (binop/NMODL x op1 y)) |
---|
130 | ((x y z) (binop/NMODL x op1 (binop/NMODL y op1 z))) |
---|
131 | (lst (let* ((n (length lst)) |
---|
132 | (n/2 (inexact->exact (round (/ n 2))))) |
---|
133 | (binop/NMODL (format-op/NMODL indent op (take lst n/2 )) op1 |
---|
134 | (format-op/NMODL indent op (drop lst n/2 ))))))))) |
---|
135 | |
---|
136 | (define (format-fncall/NMODL indent op args) |
---|
137 | (let ((op1 (doc:text (->string op)))) |
---|
138 | (doc:cons op1 (group/NMODL ((doc:list indent identity (lambda () (doc:text ", "))) args))))) |
---|
139 | |
---|
140 | |
---|
141 | (define nmodl-ops |
---|
142 | `(+ - * / > < <= >= = ^)) |
---|
143 | |
---|
144 | (define builtin-fns |
---|
145 | `(+ - * / pow neg abs atan asin acos sin cos exp ln |
---|
146 | sqrt tan cosh sinh tanh hypot gamma lgamma log10 log2 log1p ldexp cube |
---|
147 | > < <= >= = and or round ceiling floor max min |
---|
148 | fpvector-ref)) |
---|
149 | |
---|
150 | (define (sum lst) |
---|
151 | (if (null? lst) lst |
---|
152 | (match lst |
---|
153 | ((x) x) |
---|
154 | ((x y) `(+ ,x ,y)) |
---|
155 | ((x y . rest) `(+ (+ ,x ,y) ,(sum rest))) |
---|
156 | ((x . rest) `(+ ,x ,(sum rest)))))) |
---|
157 | |
---|
158 | |
---|
159 | (define (subst-term t subst k) |
---|
160 | (match t |
---|
161 | (('if c t e) |
---|
162 | `(if ,(k c subst) ,(k t subst) ,(k e subst))) |
---|
163 | (('let bs e) |
---|
164 | (k `(let ,(map (lambda (b) `(,(car b) ,(k (cadr b) subst))) bs) ,(k e subst)) subst)) |
---|
165 | ((f . es) |
---|
166 | (cons (k f subst) (map (lambda (e) (k e subst)) es))) |
---|
167 | ((? symbol? ) (lookup-def t subst t)) |
---|
168 | ((? atom? ) t))) |
---|
169 | |
---|
170 | (define (binding? t) (and (list? t) (eq? 'let (car t)) (cdr t))) |
---|
171 | |
---|
172 | (define (bind ks vs e) `(let ,(zip ks vs) ,e)) |
---|
173 | |
---|
174 | |
---|
175 | (define (if-convert expr) |
---|
176 | (match expr |
---|
177 | (('if c t e) |
---|
178 | (let ((r (gensym "if"))) |
---|
179 | `(let ((,r (if ,(if-convert c) ,(if-convert t) ,(if-convert e)))) |
---|
180 | ,r))) |
---|
181 | (('let bs e) |
---|
182 | `(let ,(map (lambda (b) `(,(car b) ,(if-convert (cadr b)))) bs) ,(if-convert e))) |
---|
183 | ((f . es) |
---|
184 | (cons f (map if-convert es))) |
---|
185 | ((? atom? ) expr))) |
---|
186 | |
---|
187 | |
---|
188 | (define (let-enum expr ax) |
---|
189 | (match expr |
---|
190 | (('let ((x ('if c t e))) y) |
---|
191 | (let ((ax (fold let-enum ax (list c t e)))) |
---|
192 | (if (eq? x y) (append ax (list (list x `(if ,c ,t ,e)))) ax))) |
---|
193 | |
---|
194 | (('let bnds body) (let-enum body (append ax bnds))) |
---|
195 | |
---|
196 | (('if c t e) (fold let-enum ax (list c t e))) |
---|
197 | |
---|
198 | ((f . es) (fold let-enum ax es)) |
---|
199 | |
---|
200 | (else ax))) |
---|
201 | |
---|
202 | |
---|
203 | (define (let-elim expr) |
---|
204 | (match expr |
---|
205 | (('let ((x ('if c t e))) y) |
---|
206 | (if (eq? x y) y expr)) |
---|
207 | |
---|
208 | (('let bnds body) (let-elim body)) |
---|
209 | |
---|
210 | (('if c t e) `(if . ,(map let-elim (list c t e)))) |
---|
211 | |
---|
212 | ((f . es) `(,f . ,(map let-elim es))) |
---|
213 | |
---|
214 | (else expr))) |
---|
215 | |
---|
216 | |
---|
217 | (define (let-lift expr) |
---|
218 | (let ((bnds (let-enum expr (list)))) |
---|
219 | (if (null? bnds) expr |
---|
220 | `(let ,(map (lambda (b) (list (car b) (let-elim (cadr b)))) bnds) ,(let-elim expr))))) |
---|
221 | |
---|
222 | (define (canonicalize-expr/NMODL expr) |
---|
223 | (let ((subst-convert (subst-driver (lambda (x) (and (symbol? x) x)) binding? identity bind subst-term))) |
---|
224 | (let* ((expr1 (if-convert expr)) |
---|
225 | (expr2 (subst-convert expr1 subst-empty)) |
---|
226 | (expr3 (let-lift expr2))) |
---|
227 | expr3))) |
---|
228 | |
---|
229 | (define (format-expr/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-expr/NMODL indent c)) |
---|
242 | (block/NMODL |
---|
243 | (binop/NMODL (doc:text (->string (first x))) (doc:text " = ") |
---|
244 | (format-expr/NMODL indent+ t))) |
---|
245 | (block/NMODL |
---|
246 | (binop/NMODL (doc:text (->string (first x))) (doc:text " = ") |
---|
247 | (format-expr/NMODL indent+ e) )))) |
---|
248 | |
---|
249 | (else |
---|
250 | (format-op/NMODL indent+ " = " |
---|
251 | (list (format-expr/NMODL indent+ (first x) ) |
---|
252 | (format-expr/NMODL indent+ (second x)))))) |
---|
253 | ax)) |
---|
254 | (doc:empty) bindings) |
---|
255 | (let ((body1 (doc:nest indent (format-expr/NMODL indent body)))) |
---|
256 | (if rv (format-op/NMODL indent " = " (list (format-expr/NMODL indent+ rv ) body1)) |
---|
257 | body1)))) |
---|
258 | |
---|
259 | (('if . rest) (error 'format-expr/NMODL "invalid if statement " expr)) |
---|
260 | |
---|
261 | ((op . rest) |
---|
262 | (let ((op (case op ((pow) '^) (else op)))) |
---|
263 | (let ((fe |
---|
264 | (if (member op nmodl-ops) |
---|
265 | (let ((mdiv? (any (lambda (x) (match x (('* . _) #t) (('/ . _) #t) (else #f))) rest)) |
---|
266 | (mul? (any (lambda (x) (match x (('* . _) #t) (else #f))) rest)) |
---|
267 | (plmin? (any (lambda (x) (match x (('+ . _) #t) (('- . _) #t) (else #f))) rest))) |
---|
268 | (case op |
---|
269 | ((/) |
---|
270 | (format-op/NMODL indent op |
---|
271 | (map (lambda (x) |
---|
272 | (let ((fx (format-expr/NMODL indent+ x))) |
---|
273 | (if (or (symbol? x) (number? x)) fx |
---|
274 | (if (or mul? plmin?) (group/NMODL fx) fx)))) rest))) |
---|
275 | ((*) |
---|
276 | (format-op/NMODL indent op |
---|
277 | (map (lambda (x) |
---|
278 | (let ((fx (format-expr/NMODL indent+ x))) |
---|
279 | (if (or (symbol? x) (number? x)) fx |
---|
280 | (if plmin? (group/NMODL fx) fx)))) rest))) |
---|
281 | |
---|
282 | ((^) |
---|
283 | (format-op/NMODL indent op |
---|
284 | (map (lambda (x) |
---|
285 | (let ((fx (format-expr/NMODL indent+ x))) |
---|
286 | (if (or (symbol? x) (number? x)) fx |
---|
287 | (if (or mdiv? plmin?) (group/NMODL fx) fx)))) rest))) |
---|
288 | |
---|
289 | (else |
---|
290 | (format-op/NMODL indent op |
---|
291 | (map (lambda (x) |
---|
292 | (let ((fx (format-expr/NMODL indent+ x))) fx)) rest))))) |
---|
293 | |
---|
294 | (let ((op (case op ((neg) '-) (else op)))) |
---|
295 | (format-fncall/NMODL indent op (map (lambda (x) (format-expr/NMODL indent+ x)) rest)))))) |
---|
296 | (if rv |
---|
297 | (format-op/NMODL indent " = " (list (format-expr/NMODL indent+ rv ) fe)) |
---|
298 | fe)))) |
---|
299 | |
---|
300 | (else (let ((fe (doc:text (->string expr)))) |
---|
301 | (if rv |
---|
302 | (format-op/NMODL indent " = " (list (format-expr/NMODL indent+ rv ) fe)) |
---|
303 | fe))))))) |
---|
304 | |
---|
305 | |
---|
306 | |
---|
307 | (define (expr->string/NMODL x . rest) |
---|
308 | (let-optionals rest ((rv #f) (width 64)) |
---|
309 | (sdoc->string (doc:format width (format-expr/NMODL 2 x rv))))) |
---|
310 | |
---|
311 | |
---|
312 | (define (define-fn indent n proc) |
---|
313 | (let ((lst (procedure-data proc)) |
---|
314 | (indent+ (+ 2 indent))) |
---|
315 | (let ((rt (lookup-def 'rt lst)) |
---|
316 | (formals (lookup-def 'formals lst)) |
---|
317 | (vars (lookup-def 'vars lst)) |
---|
318 | (body (lookup-def 'body lst))) |
---|
319 | (pp indent ,nl (FUNCTION ,n (,(sl\ ", " vars)) "{" )) |
---|
320 | (let* ((body1 (canonicalize-expr/NMODL body)) |
---|
321 | (lbs (enumbnds body1 (list)))) |
---|
322 | (if (not (null? lbs)) (pp indent (LOCAL ,(sl\ ", " lbs)))) |
---|
323 | (pp indent+ ,(expr->string/NMODL body1 n))) |
---|
324 | (pp indent "}"))) ) |
---|
325 | |
---|
326 | |
---|
327 | (define (define-state indent n) |
---|
328 | (pp indent (,n))) |
---|
329 | |
---|
330 | (define (define-asgn indent n) |
---|
331 | (pp indent (,n))) |
---|
332 | |
---|
333 | (define (define-import indent n) |
---|
334 | (pp indent (,(car n)))) |
---|
335 | |
---|
336 | |
---|
337 | (define (state-eqs n initial open transitions power) |
---|
338 | (let* ((g (make-digraph n (string-append (->string n) " transitions graph"))) |
---|
339 | (add-node! (g 'add-node!)) |
---|
340 | (add-edge! (g 'add-edge!)) |
---|
341 | (out-edges (g 'out-edges)) |
---|
342 | (in-edges (g 'in-edges)) |
---|
343 | (node-info (g 'node-info)) |
---|
344 | (node-list (let loop ((lst (list)) (tlst transitions)) |
---|
345 | (if (null? tlst) (delete-duplicates lst eq?) |
---|
346 | (match (car tlst) |
---|
347 | (('-> s0 s1 rate-expr) |
---|
348 | (loop (cons s0 (cons s1 lst)) (cdr tlst))) |
---|
349 | (('-> _) |
---|
350 | (oru:error 'oru:nmodl-state-eqs ": invalid transition equation " |
---|
351 | (car tlst) " in state complex " n)) |
---|
352 | (else (loop lst (cdr tlst))))))) |
---|
353 | (node-ids (list-tabulate (length node-list) identity)) |
---|
354 | (name->id-map (zip node-list node-ids))) |
---|
355 | ;; insert state nodes in the dependency graph |
---|
356 | (for-each (lambda (i n) (add-node! i n)) node-ids node-list) |
---|
357 | (let* ((nodes ((g 'nodes))) |
---|
358 | (snode (find (lambda (s) (not (eq? (second s) open))) nodes)) |
---|
359 | (snex `(- 1 ,(sum (filter-map (lambda (s) (and (not (= (first s) (first snode))) (second s))) nodes))))) |
---|
360 | ;; create rate edges in the graph |
---|
361 | (for-each (lambda (e) |
---|
362 | (match e (('-> s0 s1 rate-expr) |
---|
363 | (let ((i (car (alist-ref s0 name->id-map))) |
---|
364 | (j (car (alist-ref s1 name->id-map))) |
---|
365 | (x (if (eq? s0 (second snode)) snex s0))) |
---|
366 | (add-edge! (list i j `(* ,x ,rate-expr))))) |
---|
367 | (else (void)))) |
---|
368 | transitions) |
---|
369 | ;; generate differential equations for each state in the transitions system |
---|
370 | (let ((eqs (fold (lambda (s ax) |
---|
371 | (if (= (first snode) (first s) ) ax |
---|
372 | (let* ((out (out-edges (first s))) |
---|
373 | (in (in-edges (first s))) |
---|
374 | (open? (eq? (second s) open)) |
---|
375 | (name (second s))) |
---|
376 | (let* ((rhs1 (cond ((and (not (null? out)) (not (null? in))) |
---|
377 | `(+ (neg ,(sum (map third out))) |
---|
378 | ,(sum (map third in)))) |
---|
379 | ((and (not (null? out)) (null? in)) |
---|
380 | `(neg ,(sum (map third out)))) |
---|
381 | ((and (null? out) (not (null? in))) |
---|
382 | (sum (map third in))))) |
---|
383 | (fbody (rhsexpr rhs1)) |
---|
384 | (fbody1 (canonicalize-expr/NMODL fbody))) |
---|
385 | (cons (list (s+ name "'") fbody1) ax))))) |
---|
386 | (list) nodes))) |
---|
387 | eqs)))) |
---|
388 | |
---|
389 | |
---|
390 | |
---|
391 | |
---|
392 | (define (state-init n init) |
---|
393 | (let* ((init (rhsexpr init)) |
---|
394 | (init1 (canonicalize-expr/NMODL init))) |
---|
395 | (list n init1))) |
---|
396 | |
---|
397 | (define (asgn-eq n rhs) |
---|
398 | (let* ((fbody (rhsexpr rhs)) |
---|
399 | (fbody1 (canonicalize-expr/NMODL fbody))) |
---|
400 | (list n fbody1))) |
---|
401 | |
---|
402 | |
---|
403 | (define (stcomp-eq n open transitions) |
---|
404 | (list n open)) |
---|
405 | |
---|
406 | |
---|
407 | (define (poset->asgn-eq-defs poset sys) |
---|
408 | (fold-right |
---|
409 | (lambda (lst ax) |
---|
410 | (fold (lambda (x ax) |
---|
411 | (match-let (((i . n) x)) |
---|
412 | (let ((en (environment-ref sys n))) |
---|
413 | (if (oru:quantity? en) |
---|
414 | (cases oru:quantity en |
---|
415 | (ASGN (name value rhs) (cons (asgn-eq name rhs) ax)) |
---|
416 | (else ax)) |
---|
417 | ax)))) |
---|
418 | ax lst)) |
---|
419 | (list) poset)) |
---|
420 | |
---|
421 | |
---|
422 | (define (poset->state-eq-defs poset sys) |
---|
423 | (fold-right |
---|
424 | (lambda (lst ax) |
---|
425 | (fold (lambda (x ax) |
---|
426 | (match-let (((i . n) x)) |
---|
427 | (let ((en (environment-ref sys n))) |
---|
428 | (if (oru:quantity? en) |
---|
429 | (cases oru:quantity en |
---|
430 | (TSCOMP (name initial open transitions power) |
---|
431 | (append (state-eqs name initial open transitions power) ax)) |
---|
432 | (else ax)) |
---|
433 | ax)))) |
---|
434 | ax lst)) |
---|
435 | (list) poset)) |
---|
436 | |
---|
437 | |
---|
438 | (define (poset->stcomp-eq-defs poset sys) |
---|
439 | (fold-right |
---|
440 | (lambda (lst ax) |
---|
441 | (fold (lambda (x ax) |
---|
442 | (match-let (((i . n) x)) |
---|
443 | (let ((en (environment-ref sys n))) |
---|
444 | (if (oru:quantity? en) |
---|
445 | (cases oru:quantity en |
---|
446 | (TSCOMP (name initial open transitions power) |
---|
447 | (cons (stcomp-eq name open transitions) ax)) |
---|
448 | (else ax)) |
---|
449 | ax)))) |
---|
450 | ax lst)) |
---|
451 | (list) poset)) |
---|
452 | |
---|
453 | (define (poset->state-init-defs poset sys) |
---|
454 | (fold-right |
---|
455 | (lambda (lst ax) |
---|
456 | (fold (lambda (x ax) |
---|
457 | (match-let (((i . n) x)) |
---|
458 | (let ((en (environment-ref sys n))) |
---|
459 | (if (oru:quantity? en) |
---|
460 | (cases oru:quantity en |
---|
461 | (TSCOMP (name initial open transitions power) |
---|
462 | (cons* (state-init name initial) (state-init open name) ax)) |
---|
463 | (else ax)) |
---|
464 | ax)))) |
---|
465 | ax lst)) |
---|
466 | (list) poset)) |
---|
467 | |
---|
468 | (define (find-locals defs) |
---|
469 | (concatenate |
---|
470 | (map (lambda (def) (match (second def) (('let bnds _) (map first bnds)) (else (list)))) |
---|
471 | defs))) |
---|
472 | |
---|
473 | |
---|
474 | (define (state-power sys n) |
---|
475 | (let ((en (environment-ref sys n))) |
---|
476 | (if (oru:quantity? en) |
---|
477 | (cases oru:quantity en |
---|
478 | (TSCOMP (name initial open transitions power) power) |
---|
479 | (else #f)) #f))) |
---|
480 | |
---|
481 | (define (bucket-partition p lst) |
---|
482 | (let loop ((lst lst) (ax (list))) |
---|
483 | (if (null? lst) ax |
---|
484 | (let ((x (car lst))) |
---|
485 | (let bkt-loop ((old-bkts ax) (new-bkts (list))) |
---|
486 | (if (null? old-bkts) (loop (cdr lst) (cons (list x) new-bkts)) |
---|
487 | (if (p x (caar old-bkts )) |
---|
488 | (loop (cdr lst) (append (cdr old-bkts) (cons (cons x (car old-bkts)) new-bkts))) |
---|
489 | (bkt-loop (cdr old-bkts) (cons (car old-bkts) new-bkts))))))))) |
---|
490 | |
---|
491 | |
---|
492 | (define (oru:nmodl-translator sys) |
---|
493 | (match-let ((($ oru:quantity 'DISPATCH dis) (environment-ref sys (oru-intern 'dispatch)))) |
---|
494 | (let ((imports ((dis 'imports) sys)) |
---|
495 | (exports ((dis 'exports) sys))) |
---|
496 | (let* ((indent 0) |
---|
497 | (indent+ (+ 2 indent )) |
---|
498 | (sysname (nmodl-name ((dis 'sysname) sys))) |
---|
499 | (sfname (string-append (->string sysname) ".mod")) |
---|
500 | (deps* ((dis 'depgraph*) sys)) |
---|
501 | (asgns ((dis 'asgns) sys)) |
---|
502 | (states ((dis 'states) sys)) |
---|
503 | (stcomps ((dis 'stcomps) sys)) |
---|
504 | (components ((dis 'components) sys)) |
---|
505 | (ionchs (filter-map (match-lambda (('ion-channel name) name) (else #f)) components))) |
---|
506 | |
---|
507 | (match-let (((state-list asgn-list g) deps*)) |
---|
508 | (let ((poset (vector->list ((dis 'depgraph->bfs-dist-poset) g))) |
---|
509 | (perm-ions (delete-duplicates |
---|
510 | (fold (lambda (n ax) |
---|
511 | (let* ((subcomps ((dis 'component-subcomps) sys n)) |
---|
512 | (perm (lookup-def 'permeating-substance subcomps)) |
---|
513 | (erev (and perm (car ((dis 'component-exports) sys perm)))) |
---|
514 | (i (and perm (nmodl-name (s+ 'i perm)))) |
---|
515 | (e (and perm (nmodl-name (s+ 'e perm))))) |
---|
516 | (if perm (cons `(,perm ,i ,e ,erev) ax) ax))) |
---|
517 | (list) ionchs) |
---|
518 | (lambda (x y) (eq? (car x) (car y))))) |
---|
519 | (acc-ions (delete-duplicates |
---|
520 | (fold (lambda (n ax) |
---|
521 | (let* ((subcomps ((dis 'component-subcomps) sys n)) |
---|
522 | (acc (lookup-def 'accumulating-substance subcomps)) |
---|
523 | (i (and acc (nmodl-name (s+ 'i acc)))) |
---|
524 | (in (and acc (nmodl-name (s+ acc 'i)))) |
---|
525 | (out (and acc (nmodl-name (s+ acc 'o))))) |
---|
526 | (if acc (cons `(,acc ,i ,in ,out) ax) ax))) |
---|
527 | (list) ionchs) |
---|
528 | (lambda (x y) (eq? (car x) (car y))))) |
---|
529 | ) |
---|
530 | |
---|
531 | (with-output-to-file sfname |
---|
532 | (lambda () |
---|
533 | (pp indent ,nl (TITLE ,sysname)) |
---|
534 | |
---|
535 | (pp indent ,nl (NEURON "{")) |
---|
536 | (if (not (null? exports)) (pp indent+ (RANGE ,(sl\ ", " exports)))) |
---|
537 | (for-each (lambda (x) |
---|
538 | (pp indent+ (RANGE ,(second x)) |
---|
539 | (USEION ,(first x) READ ,(third x) WRITE ,(second x)))) |
---|
540 | perm-ions) |
---|
541 | (for-each (lambda (x) |
---|
542 | (pp indent+ (RANGE ,(second x)) |
---|
543 | (USEION ,(first x) READ ,(third x) ", " ,(fourth x) WRITE ,(second x)))) |
---|
544 | acc-ions) |
---|
545 | |
---|
546 | (pp indent "}") |
---|
547 | |
---|
548 | |
---|
549 | (pp indent ,nl (PARAMETER "{")) |
---|
550 | (let* ((const-defs (map (lambda (nv) |
---|
551 | (let ((v1 (canonicalize-expr/NMODL (second nv)))) |
---|
552 | (list (first nv) v1))) |
---|
553 | ((dis 'consts) sys))) |
---|
554 | (locals (find-locals const-defs))) |
---|
555 | (if (not (null? locals)) (pp indent+ (LOCAL ,(sl\ ", " locals)))) |
---|
556 | (for-each (lambda (def) |
---|
557 | (let ((n (first def)) (b (second def))) |
---|
558 | (pp indent+ ,(expr->string/NMODL b n)))) const-defs)) |
---|
559 | (pp indent "}") |
---|
560 | |
---|
561 | (for-each (lambda (fndef) |
---|
562 | (if (not (member (car fndef) builtin-fns)) |
---|
563 | (apply define-fn (cons indent fndef)))) |
---|
564 | ((dis 'defuns) sys)) |
---|
565 | |
---|
566 | (pp indent ,nl (STATE "{")) |
---|
567 | (for-each (lambda (st) (apply define-state (list indent+ st))) |
---|
568 | states) |
---|
569 | (for-each (lambda (st) (apply define-state (list indent+ st))) |
---|
570 | stcomps) |
---|
571 | (pp indent "}") |
---|
572 | |
---|
573 | (pp indent ,nl (ASSIGNED "{")) |
---|
574 | (for-each (lambda (x) (apply define-asgn (list indent+ x))) |
---|
575 | asgns) |
---|
576 | (for-each (lambda (x) (apply define-import (list indent+ x))) |
---|
577 | imports) |
---|
578 | (for-each (lambda (x) (pp indent+ ,(second x) ,(third x))) perm-ions) |
---|
579 | (for-each (lambda (x) (pp indent+ ,(second x) ,(third x) ,(fourth x))) acc-ions) |
---|
580 | (pp indent "}") |
---|
581 | |
---|
582 | (if (not (null? asgns)) |
---|
583 | (begin |
---|
584 | (pp indent ,nl (PROCEDURE rates () "{")) |
---|
585 | (let* ((eq-defs (poset->asgn-eq-defs poset sys)) |
---|
586 | (locals (find-locals eq-defs))) |
---|
587 | (if (not (null? locals)) (pp indent+ (LOCAL ,(sl\ ", " locals)))) |
---|
588 | (for-each (lambda (def) |
---|
589 | (let ((n (first def)) (b (second def))) |
---|
590 | (pp indent+ ,(expr->string/NMODL b n)))) eq-defs)) |
---|
591 | |
---|
592 | (pp indent "}"))) |
---|
593 | |
---|
594 | (if (not (null? stcomps)) |
---|
595 | (begin |
---|
596 | (pp indent ,nl (PROCEDURE stcomps () "{")) |
---|
597 | (let* ((eq-defs (poset->stcomp-eq-defs poset sys)) |
---|
598 | (locals (find-locals eq-defs))) |
---|
599 | (if (not (null? locals)) (pp indent+ (LOCAL ,(sl\ ", " locals)))) |
---|
600 | (for-each (lambda (def) |
---|
601 | (let ((n (first def)) (b (second def))) |
---|
602 | (pp indent+ ,(expr->string/NMODL b n)))) eq-defs)) |
---|
603 | |
---|
604 | (pp indent "}"))) |
---|
605 | |
---|
606 | (pp indent ,nl (BREAKPOINT "{")) |
---|
607 | (let* ((i-eqs (filter-map |
---|
608 | (lambda (n) |
---|
609 | (let* ((subcomps ((dis 'component-subcomps) sys n)) |
---|
610 | (acc (lookup-def 'accumulating-substance subcomps)) |
---|
611 | (perm (lookup-def 'permeating-substance subcomps)) |
---|
612 | (pore (lookup-def 'pore subcomps)) |
---|
613 | (gate (lookup-def 'gate subcomps)) |
---|
614 | (sts (and gate ((dis 'component-exports) sys gate)))) |
---|
615 | (cond ((and perm pore gate) |
---|
616 | (let* ((i (nmodl-name (s+ 'i perm))) |
---|
617 | (e (nmodl-name (s+ 'e perm))) |
---|
618 | (gmax (car ((dis 'component-exports) sys pore))) |
---|
619 | (pwrs (map (lambda (n) (state-power sys n)) sts)) |
---|
620 | (gion `(* ,gmax . ,(map (lambda (st pwr) `(pow ,st ,pwr)) sts pwrs)))) |
---|
621 | (list i e gion))) |
---|
622 | ((and acc pore gate) |
---|
623 | (let* ((i (nmodl-name (s+ 'i acc))) |
---|
624 | (gmax (car ((dis 'component-exports) sys pore))) |
---|
625 | (pwrs (map (lambda (n) (state-power sys n)) sts)) |
---|
626 | (gion `(* ,gmax . ,(map (lambda (st pwr) `(pow ,st ,pwr)) sts pwrs)))) |
---|
627 | (list i #f gion))) |
---|
628 | (else (oru:error 'oru:nmodl-translator ": invalid ion channel definition " n)) |
---|
629 | ))) |
---|
630 | ionchs)) |
---|
631 | (i-bkts (bucket-partition (lambda (x y) (eq? (car x) (car y))) i-eqs)) |
---|
632 | (i-eqs (fold (lambda (b ax) |
---|
633 | (match b |
---|
634 | ((and ps ((i e gion) . rst)) |
---|
635 | (let* ((sum (if e `(* ,(sum (map third ps)) (- v ,e)) |
---|
636 | (sum (map third ps)))) |
---|
637 | (sum0 (rhsexpr sum)) |
---|
638 | (sum1 (canonicalize-expr/NMODL sum0))) |
---|
639 | (cons (list i sum1) ax))) |
---|
640 | |
---|
641 | ((i e gion) |
---|
642 | (let* ((expr0 (rhsexpr (if e `(* ,gion (- v ,e)) gion))) |
---|
643 | (expr1 (canonicalize-expr/NMODL expr0))) |
---|
644 | (cons (list i expr1) ax))) |
---|
645 | |
---|
646 | |
---|
647 | (else ax))) |
---|
648 | (list) i-bkts)) |
---|
649 | (locals (find-locals i-eqs))) |
---|
650 | (if (not (null? locals)) (pp indent+ (LOCAL ,(sl\ ", " locals)))) |
---|
651 | (if (not (null? asgns)) (pp indent+ (rates ()))) |
---|
652 | (pp indent+ (SOLVE states)) |
---|
653 | (if (not (null? stcomps)) (pp indent+ (stcomps ()))) |
---|
654 | (for-each (lambda (p) (pp indent+ ,(expr->string/NMODL (second p) (first p)))) i-eqs) |
---|
655 | (pp indent "}")) |
---|
656 | |
---|
657 | (if (not (null? states)) |
---|
658 | (begin |
---|
659 | (pp indent ,nl (DERIVATIVE states "{")) |
---|
660 | (let* ((eq-defs (reverse (poset->state-eq-defs poset sys))) |
---|
661 | (locals (find-locals eq-defs))) |
---|
662 | (if (not (null? locals)) (pp indent+ (LOCAL ,(sl\ ", " locals)))) |
---|
663 | (for-each (lambda (def) |
---|
664 | (let ((n (first def)) (b (second def))) |
---|
665 | (pp indent+ ,(expr->string/NMODL b n)))) eq-defs)) |
---|
666 | (pp indent "}"))) |
---|
667 | |
---|
668 | (pp indent ,nl (INITIAL "{")) |
---|
669 | (let* ((init-defs (poset->state-init-defs poset sys)) |
---|
670 | (locals (concatenate (find-locals init-defs)))) |
---|
671 | (if (not (null? locals)) (pp indent+ (LOCAL ,(sl\ ", " locals)))) |
---|
672 | (for-each (lambda (def) |
---|
673 | (let ((n (first def)) (b (second def))) |
---|
674 | (pp indent+ ,(expr->string/NMODL b n)))) init-defs) |
---|
675 | (for-each (lambda (x) (pp indent+ (,(third x) = ,(fourth x)))) |
---|
676 | perm-ions)) |
---|
677 | (pp indent "}") |
---|
678 | |
---|
679 | ))) |
---|
680 | ))))) |
---|