modelflowib 2.73__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
model_latex.py ADDED
@@ -0,0 +1,497 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sun Dec 3 19:07:03 2017
4
+
5
+ @author: hanseni
6
+
7
+ Mostly to eat latex models and translate to business logic
8
+
9
+ The routines are specific to a style of latex and should be inspected before use
10
+
11
+ """
12
+ from IPython.display import display, Math, Latex, Markdown , Image
13
+ import re
14
+ from IPython.lib.latextools import latex_to_png
15
+ from pathlib import Path
16
+ import os
17
+
18
+ import modelmanipulation as mp
19
+ import modelclass as mc
20
+ import modelpattern as pt
21
+ def rebank(model):
22
+ ''' All variable names are decorated by a {bank}
23
+ The {bank} is injected as the first dimension '''
24
+
25
+ ypat = re.compile(pt.namepat+pt.lagpat)
26
+ funk = set(pt.funkname) | {'SUM'}
27
+ nobank = set('N S T __{bank} NORM PPF CDF'.split()) # specific variable names not to be decorated
28
+ notouch = funk | nobank # names not to be decorated
29
+ def trans(matchobj):
30
+ ''' The function recieves a matchobj entity. The matching groups can be accesed by matchobj.group()
31
+ it returns a string with a bankname added at the end or at the first __ which marks dimensions '''
32
+ var = matchobj.group(1)
33
+ lag = '(' + matchobj.group(2) + ')'if matchobj.group(2) else ''
34
+ if var.upper() in notouch:
35
+ return var+lag
36
+ else:
37
+ if '__' in var:
38
+ pre,post = var.split('__',1)
39
+ post = ''+post
40
+ else:
41
+ pre = var
42
+ post = ''
43
+
44
+ return (pre+'__{bank}'+post + lag)
45
+
46
+ banked = ypat.sub(trans, model)
47
+ return banked
48
+
49
+ #print(rebank('a+b'))
50
+ #print(rebank('a(-1)+yyy+cc+bb__x'))
51
+
52
+ def txttolatex(model):
53
+ # takes a template model to latex
54
+ tpoc2 = model[:]
55
+ #tpoc2 = re.sub(r'((list)|(do)|(enddo)|(ppppend))[^$]+[$]','',tpoc2)
56
+ tpoc2 = re.sub(r'((list)|(do)|(enddo)|(ppppend))([^$]*) [$]',r' \\mbox{\1 \6} \\\\ \n',tpoc2)
57
+ tpoc2 = re.sub(r'__{bank}',r'^{bank}',tpoc2)
58
+ tpoc2 = re.sub(r'diff()',r'\Delta ',tpoc2)
59
+ tpoc2 = re.sub(r'norm.ppf',r'\Phi ',tpoc2)
60
+ tpoc2 = re.sub(r'norm.cdf',r'\Phi^{-1} ',tpoc2)
61
+ tpoc2 = re.sub(r'__{CrCountry}__{PortSeg}' ,r'_{CrCountry,PortSeg}',tpoc2) # two subscribt indexes
62
+ tpoc2 = re.sub(r'__{CrModelGeo}__{CrModel}',r'_{CrModelGeo,CrModel}',tpoc2) # two subscribt indexes
63
+ tpoc2 = re.sub(r'\$',r' \\\\[10pt] ',tpoc2)
64
+ tpoc2 = re.sub(r'!([^\n]*\n)',r' & \mbox{ ! \1 } & \\\\[10pt]',tpoc2)
65
+ #tpoc2 = re.sub(r'\n',r'',tpoc2) # remove all linebreaks
66
+ tpoc2 = re.sub(r'frml <>','',tpoc2)
67
+ tpoc2 = re.sub(r'sum\(([a-zA-Z{}]+),',r'\\sum_{\mbox{\1 }}',tpoc2)
68
+ tpoc2 = re.sub(r'[(]-1[)]',r'{\small[t-1]}',tpoc2)
69
+ tpoc2 = re.sub(r'=',r' &=',tpoc2)
70
+ tpoc2 = re.sub(r'([a-zA-Z])_([a-zA-Z])',r'\1\_\2',tpoc2)
71
+ tpoc2 = re.sub(r'([a-zA-Z])__([a-zA-Z])',r'\1\_\_\2',tpoc2)
72
+ tpoc2 = r'\begin{align*}'+tpoc2+r'\end{align*}'
73
+ return(tpoc2)
74
+
75
+ def defrack(streng):
76
+ '''
77
+ \frac{xxx}{yyy} = ((xxx)/(yyy))
78
+
79
+ '''
80
+ tstreng = streng[:]
81
+ tfunk = r'\frac{'
82
+ while tfunk in tstreng:
83
+ start = tstreng.find(tfunk)
84
+ # first find the first matching {}
85
+ match = tstreng[start + len(tfunk):] # the rest of the string in which we have to match }
86
+ open = 1 # we already found the first {
87
+ for index1 in range(len(match)):
88
+ if match[index1] in '{}':
89
+ open = (open + 1) if match[index1] == '{' else (open - 1)
90
+ if not open:
91
+ break
92
+ # now find the second matching {}
93
+ match2 = match[index1 + 1+1:] # the string from the location of second { to the end of string
94
+ open=1
95
+ for index2 in range(len(match2)):
96
+ if match2[index2] in '{}':
97
+ open = (open + 1) if match2[index2] == '{' else (open - 1)
98
+ if not open:
99
+ break
100
+ tstreng = tstreng[:start]+ '(('+ match[:index1] +')/('+ match2[:index2]+'))'+match2[index2+1:]
101
+ return tstreng
102
+ def debrace(streng):
103
+ '''
104
+ Eliminates underbrace{xxx}_{yyy} in a string
105
+ underbrace{xxx}_{yyy} => (xxx)
106
+ As there can be nested {} we need to match the braces
107
+
108
+ '''
109
+ tstreng = streng[:]
110
+ tfunk = r'\underbrace{'
111
+ while tfunk in tstreng:
112
+ start = tstreng.find(tfunk)
113
+ match = tstreng[start + len(tfunk):]
114
+ open = 1
115
+ for index1 in range(len(match)):
116
+ if match[index1] in '{}':
117
+ open = (open + 1) if match[index1] == '{' else (open - 1)
118
+ if not open:
119
+ break
120
+ goodstuf = tstreng[:start]+match[index1]
121
+ match2 = match[index1 + 1+2:]
122
+ open=1
123
+ for index2 in range(len(match2)):
124
+ if match2[index2] in '{}':
125
+ open = (open + 1) if match2[index2] == '{' else (open - 1)
126
+ if not open:
127
+ break
128
+ tstreng = tstreng[:start]+ ''+ match[:index1] +''+ match2[index2+1:]
129
+ return tstreng
130
+
131
+ def defunk(funk, subs , streng,startp='{',slutp='}'):
132
+ '''
133
+ \funk{xxx} => subs(xxx)
134
+
135
+ in a string
136
+ '''
137
+ tfunk, tstreng = funk[:] , streng[:]
138
+ tfunk = tfunk + startp
139
+ while tfunk in tstreng:
140
+ start = tstreng.find(tfunk)
141
+ match = tstreng[start + len(tfunk):]
142
+ open = 1
143
+ for index in range(len(match)):
144
+ if match[index] in startp+slutp:
145
+ open = (open + 1) if match[index] == startp else (open - 1)
146
+ if not open:
147
+ break
148
+ tstreng = tstreng[:start]+subs+'(' + match[:index] +')' + match[index + 1:]
149
+ return tstreng
150
+ #print(defunk(r'\sqrt',r'sqrt',r'a=\sqrt{b+ \sqrt{f+y}}'))
151
+ #print(defunk(r'\log',r'log',r'a=\log(b(-1)+ \log({f+y}))',startp='(',slutp=')'))
152
+
153
+ def findindex(ind):
154
+ ''' find the index variables on the left hand side. meaning variables braced by {} '''
155
+ lhs=ind.split('=')[0]
156
+ return re.findall(r'\{([A-Za-z][\w]*)\}',lhs ) # all the index variables
157
+
158
+ def doable(ind,show=False):
159
+ ''' find all dimensions in the left hand side of = and and decorate with the nessecary do .. enddo '''
160
+
161
+ xxy = findindex(ind) # all the index variables
162
+ xxx = ['n_{bank}' if index == 'n' else index for index in xxy]
163
+ if xxx :
164
+ pre = ' $ '.join(['Do '+i for level,i in enumerate(xxx)])+' $ \n '
165
+ post = '\n' + 'enddo $ '*len(xxx)
166
+ out = pre+ind + post
167
+ if show:
168
+ print('Before doable',ind,sep='\n')
169
+ print('After doable',out,sep='\n')
170
+ print()
171
+ else:
172
+ out=ind
173
+ return out
174
+
175
+ def findlists(input):
176
+ '''extracte liste from latex'''
177
+ relevant = re.findall(r'LIST \s*\$[^$]*\$',input.upper())
178
+ temp1 = [l.replace('$','').replace('\\','').replace(',',' ')
179
+ .replace('{','').replace('}','')
180
+ for l in relevant]
181
+ temp2 = [l.split('=')[0]+' = '
182
+ + l.split('=')[0][4:]
183
+ +' : '+ l.split('=')[1]+'$' for l in temp1]
184
+ return temp2
185
+
186
+
187
+ def findlistsnew(input):
188
+ '''extracte list with sublist from latex'''
189
+ # relevant = re.findall(r'LIST \s*\$[^$]*\$',input.upper())
190
+ relevant = re.findall(r'\$LIST\s*\\;\s*[^$]*\$',input.upper())
191
+
192
+ print(f'{relevant=}')
193
+ temp1 = [l.replace('$','').replace('\\','').replace(',',' ')
194
+ .replace('{','').replace('}','').replace('\n','/ \n')
195
+ for l in relevant]
196
+ print(f'{temp1=}')
197
+ temp2 = [l.split('=')[0]+' = '
198
+ + l.split('=')[0][4:]
199
+ +' : '+ l.split('=')[1]+'$' for l in temp1]
200
+ return temp2
201
+
202
+ def findlistsx(input):
203
+ '''extracte list with sublist from latex'''
204
+ relevant = re.findall(r'\$LIST\s*\\;\s*[^$]*\$',input.upper())
205
+ print(f'{relevant=}')
206
+ temp1 = [l.replace('$','').replace('\\','')
207
+ .replace(',',' ').replace(';',' ')
208
+ .replace('{','').replace('}','').replace('\n','/ \n')
209
+ for l in relevant]
210
+ print(f'{temp1=}')
211
+ temp2 = ['LIST ' + l.split('=')[0][4:].strip() +' = '
212
+ + l.split('=')[0][4:]
213
+ +' : '+ l.split('=')[1]+'$' for l in temp1]
214
+
215
+ print(f'{temp2=}')
216
+ return ('\n'.join(temp2)+'\n')
217
+
218
+ if 0:
219
+ listtest=r'''
220
+ $List \; stage=\{s1, s2,s3\} \\
221
+ stagened:\{ 0, 0, 1,\} $
222
+ '''
223
+ print(findlists(listtest))
224
+
225
+
226
+
227
+ def latextotxt(input,dynare=False,bankadd=False):
228
+ '''
229
+ Translates a latex input to a BL output
230
+
231
+ '''
232
+ # breakpoint()
233
+ ex12 = re.findall(r'\\label\{eq:(.*?)\}\n(.*?)\\end\{',input,re.DOTALL) # select the relevant equations
234
+ org = [(name,eq.replace('\n','')) for name,ex in ex12 for eq in ex.split('\\\\')
235
+ if 2 == len(eq.split('='))]
236
+ # ex15 = [('frml '+name+' '+eq) for (name,ex) in ex12 for eq in ex.splitlines()]
237
+ ex15 = [eq.strip() for (name,eq) in org]
238
+ temp = '\n'.join(ex15)
239
+ trans={r'\left':'',
240
+ r'\right':'',
241
+ r'\min':'min',
242
+ r'\max':'max',
243
+ r'\rho':'rho',
244
+ r'\tau':'tau',
245
+ r'\sigma':'sigma',
246
+ r'&':'',
247
+ r'\\':'',
248
+ r'[':'(',
249
+ r']':')',
250
+ r'&':'',
251
+ r'\nonumber' : '',
252
+ r'\_' : '_',
253
+ r'{n}' : '__{n}',
254
+ r'_{t}' : '',
255
+ r'{n,s}' : '__{n}__{s}',
256
+ r'{n,s,t}' : '__{n}__{s}__{t}',
257
+ 'logit^{-1}' : 'logit_inverse',
258
+ r'\{' : '{',
259
+ r'\}' : '}',
260
+
261
+
262
+ }
263
+ ftrans = {
264
+ r'\sqrt':'sqrt',
265
+ r'\Delta':'diff',
266
+ r'\sum_':'sum',
267
+ r'\Phi':'NORM.CDF',
268
+ r'\Phi^{-1}':'NORM.PDF'
269
+ }
270
+ regtrans = {
271
+ r'\\Delta ([A-Za-z_][\w{},\^]*)':r'diff(\1)', # \Delta xy => diff(xy)
272
+ r'_{t-([1-9])}' : r'(-\1)', # _{t-x} => (-x)
273
+
274
+ # r'\^([\w])' : r'_\1', # ^x => _x
275
+ # r'\^\{([\w]+)\}(\w)' : r'_\1_\2', # ^{xx}y => _xx_y
276
+ r'\^\{([\w]+)\}' : r'_{\1}', # ^{xx} => _xx
277
+ r'\^\{([\w]+),([\w]+)\}' : r'_{\1}_{\2}', # ^{xx,yy} => _xx_yy
278
+ r'\s*\\times\s*':'*' ,
279
+ }
280
+ # breakpoint()
281
+ for before,to in ftrans.items():
282
+ temp = defunk(before,to,temp)
283
+ for before,to in trans.items():
284
+ temp = temp.replace(before,to)
285
+ for before,to in regtrans.items():
286
+ temp = re.sub(before,to,temp)
287
+ temp = debrace(temp)
288
+ temp = defrack(temp)
289
+ if bankadd:
290
+ temp = rebank(temp)
291
+
292
+ # breakpoint()
293
+ temp = re.sub(r'sum\(n,s,t\)'+(pt.namepat),r'sum(n_{bank},sum(s,sum(t,\1)))',temp)
294
+ temp = re.sub(r'sum\(n\)sum\(s\)sum\(t\)'+(pt.namepat),r'sum(n_{bank},sum(s,sum(t,\1)))',temp)
295
+ temp = re.sub(r'sum\(n\)'+(pt.namepat),r'sum(n_{bank},\1)',temp)
296
+ temp = re.sub(fr'sum\({pt.namepat}\)\(',r'sum(\1,',temp)
297
+
298
+ ltemp = [b.strip().split('=') for b in temp.splitlines()] # remove blanks in the ends and split each line at =
299
+ # breakpoint()
300
+ ltemp = [lhs + ' = '+ rhs.replace(' ','') for lhs,rhs in ltemp] # change ' ' to * on the rhs.
301
+ ltemp = ['Frml '+fname + ' ' + eq + ' $ 'for eq,(fname,__) in zip(ltemp,org)]
302
+
303
+ ltemp = [doable(l) for l in ltemp]
304
+
305
+
306
+ out = '\n'.join(ltemp+findlists(input))
307
+ return out
308
+
309
+ def latextotxtnew(input):
310
+ '''
311
+ Translates a latex input to a BL output
312
+
313
+ '''
314
+ # breakpoint()
315
+ ex12 = re.findall(r'\\label\{eq:(.*?)\}\n(.*?)\\end\{',input,re.DOTALL) # select the relevant equations
316
+ org = [(name,eq.replace('\n','')) for name,ex in ex12 for eq in ex.split('\\\\')
317
+ if 2 == len(eq.split('='))]
318
+ # ex15 = [('frml '+name+' '+eq) for (name,ex) in ex12 for eq in ex.splitlines()]
319
+ ex15 = [eq.strip() for (name,eq) in org]
320
+ temp = '\n'.join(ex15)
321
+ trans={r'\left':'',
322
+ r'\right':'',
323
+ r'\min':'min',
324
+ r'\max':'max',
325
+ r'\rho':'rho',
326
+ r'&':'',
327
+ r'\\':'',
328
+ r'[':'(',
329
+ r']':')',
330
+ r'&':'',
331
+ r'\nonumber' : '',
332
+ r'\_' : '_',
333
+ r'{n}' : '__{n}',
334
+ r'_{t}' : '',
335
+ r'{n,s}' : '__{n}__{s}',
336
+ r'{n,s,t}' : '__{n}__{s}__{t}',
337
+ 'logit^{-1}' : 'logit_inverse',
338
+ r'\{' : '{',
339
+ r'\}' : '}',
340
+ r'\sigma' :'sigma',
341
+
342
+
343
+ }
344
+ ftrans = {
345
+ r'\sqrt':'sqrt',
346
+ r'\Delta':'diff',
347
+ r'\sum_':'sum',
348
+ r'\Phi':'NORM.CDF',
349
+ r'\Phi^{-1}':'NORM.PDF'
350
+ }
351
+ regtrans = {
352
+ r'\\Delta ([A-Za-z_][\w{},\^]*)':r'diff(\1)', # \Delta xy => diff(xy)
353
+ r'_{t-([1-9])}' : r'(-\1)', # _{t-x} => (-x)
354
+
355
+ # r'\^([\w])' : r'_\1', # ^x => _x
356
+ # r'\^\{([\w]+)\}(\w)' : r'_\1_\2', # ^{xx}y => _xx_y
357
+ r'\^\{([\w]+)\}' : r'_{\1}', # ^{xx} => _xx
358
+ r'\^\{([\w]+),([\w]+)\}' : r'_{\1}_{\2}', # ^{xx,yy} => _xx_yy
359
+ r'\s*\\times\s*':'*' ,
360
+ }
361
+ # breakpoint()
362
+ for before,to in ftrans.items():
363
+ temp = defunk(before,to,temp)
364
+ for before,to in trans.items():
365
+ temp = temp.replace(before,to)
366
+ for before,to in regtrans.items():
367
+ temp = re.sub(before,to,temp)
368
+ temp = debrace(temp)
369
+ temp = defrack(temp)
370
+ if bankadd:
371
+ temp = rebank(temp)
372
+
373
+ # breakpoint()
374
+ temp = re.sub(r'sum\(n,s,t\)'+(pt.namepat),r'sum(n_{bank},sum(s,sum(t,\1)))',temp)
375
+ temp = re.sub(r'sum\(n\)sum\(s\)sum\(t\)'+(pt.namepat),r'sum(n_{bank},sum(s,sum(t,\1)))',temp)
376
+ temp = re.sub(r'sum\(n\)'+(pt.namepat),r'sum(n_{bank},\1)',temp)
377
+ temp = re.sub(fr'sum\({pt.namepat}\)\(',r'sum(\1,',temp)
378
+
379
+ ltemp = [b.strip().split('=') for b in temp.splitlines()] # remove blanks in the ends and split each line at =
380
+ # breakpoint()
381
+ ltemp = [lhs + ' = '+ rhs.replace(' ','') for lhs,rhs in ltemp] # change ' ' to * on the rhs.
382
+ ltemp = ['Frml '+fname + ' ' + eq + ' $ 'for eq,(fname,__) in zip(ltemp,org)]
383
+
384
+ ltemp = [doable(l) for l in ltemp]
385
+
386
+
387
+ out = '\n'.join(ltemp+findlists(input))
388
+ return out
389
+
390
+ def dynlatextotxt(input,show=False):
391
+ '''
392
+ Translates a latex input to a BL output
393
+ The latex input is the latex output of Dynare
394
+
395
+ '''
396
+ with mc.ttimer('Findall',show):
397
+ ex12 = re.findall(r'\\begin{dmath}\n(.*?)\n\\end{dmath}',input,re.DOTALL) # select the relevant equations
398
+
399
+ with mc.ttimer('Split',show):
400
+ org = [' = '.join([side[:] for side in e.split('=',1)]) for e in ex12]
401
+ # ex15 = [('frml '+name+' '+eq) for (name,ex) in ex12 for eq in ex.splitlines()]
402
+ with mc.ttimer('Strip',show):
403
+ ex15 = [defrack(eq.strip()) for eq in org]
404
+ with mc.ttimer('join',show):
405
+ temp = '\n'.join(ex15)
406
+
407
+
408
+ trans={r'\left':'',
409
+ r'\right':'',
410
+ r'\min':'min',
411
+ r'\max':'max',
412
+ r'&':'',
413
+ r'\\':'',
414
+ r'\_':'_',
415
+ r'[':'(',
416
+ r']':')',
417
+ r'&':'',
418
+ r'_{t}': '',
419
+ r'\,': ' *',
420
+ r'\leq': ' <= ',
421
+ r'\neq': ' != ',
422
+ r'\geq': ' >= ',
423
+
424
+ }
425
+ ftrans = { # before{expression} ==> after(expression)
426
+ r'\sqrt':'sqrt',
427
+ r'\Delta':'diff',
428
+ r'^':'^',
429
+ r'\sum_':'sum'
430
+ }
431
+ ftransp = {
432
+ r'\log':'log',
433
+ }
434
+ regtrans = {
435
+ r'\\Delta ([A-Za-z][\w{},\^]*)':r'diff(\1)', # \Delta xy => diff(xy)
436
+ r'_{t-([1-9])}' : r'(-\1)', # _{t-x} => (-x)
437
+ '{'+pt.namepat+'}' : r'\1', #
438
+ '{'+pt.namepat+'(?:\\(([+-][0-9]+)\\))}' : r'\1(\2)',
439
+ }
440
+
441
+ # with mc.ttimer('Defrac',show):
442
+ # temp = defrack(temp)
443
+ for before,to in trans.items():
444
+ with mc.ttimer(f'replace {before}',show):
445
+ temp = temp.replace(before,to)
446
+
447
+ for before,to in ftrans.items():
448
+ with mc.ttimer(f'defunk {before}',show):
449
+ temp = defunk(before,to,temp)
450
+
451
+ for before,to in ftransp.items():
452
+ with mc.ttimer(f'defunk {before}',show):
453
+ temp = defunk(before,to,temp,'(',')')
454
+
455
+ for before,to in regtrans.items():
456
+ with mc.ttimer(f'Regtrans {before}'):
457
+ temp = re.sub(before,to,temp)
458
+ # temp = debrace(temp)
459
+
460
+ if show: print('Translation from Latex to BLL finished')
461
+ # temp = re.sub(r'sum\(n,s,t\)'+(pt.namepat),r'sum(n_{bank},sum(s,sum(t,\1)))',temp)
462
+ # temp = re.sub(r'sum\(n\)sum\(s\)sum\(t\)'+(pt.namepat),r'sum(n_{bank},sum(s,sum(t,\1)))',temp)
463
+ # temp = re.sub(r'sum\(n\)'+(pt.namepat),r'sum(n_{bank},\1)',temp)
464
+
465
+ ltemp = ['Frml <> ' + eq + ' $ 'for eq in temp.splitlines() ]
466
+
467
+ # ltemp = [doable(l) for l in ltemp]
468
+
469
+ out = '\n'.join(ltemp)
470
+ return out
471
+
472
+
473
+ if __name__ == '__main__' :
474
+ if 0:
475
+ test = r'''\
476
+ Loans can be in 3 stages, 1,2 3.
477
+ New loans will be generated and loans will mature.
478
+
479
+
480
+ \begin{equation}
481
+ \label{eq:Norm}
482
+ TR^{stage\_from,stage} = \frac{TR\_U^{stage\_from,stage}}{1+0*\sum_{stage\_from2}(TR\_U^{stage\_from2,stage})}
483
+ \times(1-M^{stage}-WRO^{stage})
484
+ \end{equation}
485
+
486
+ List $stage=\{s1, s2,s3\}$
487
+
488
+ List $stage\_from=\{s1, s2,s3\}$
489
+
490
+ List $stage\_from2=\{s1, s2,s3\}$
491
+
492
+ List $stage\_to=\{s1, s2,s3\}$
493
+
494
+ '''
495
+ res = latextotxt(test)
496
+
497
+