source: sage/interfaces/lie.py @ 5957:ef12725028b9

Revision 5957:ef12725028b9, 21.9 KB checked in by William Stein <wstein@…>, 6 years ago (diff)

Lie interface work.

Line 
1r"""
2Interface to LiE
3
4LiE is a software package under development at CWI since
5January 1988.  Its purpose is to enable mathematicians and
6physicists to obtain on-line information as well as to
7interactively perform computations of a Lie group theoretic
8nature.  It focuses on the representation theory of complex
9semisimple (reductive) Lie groups and algebras, and on the
10structure of their Weyl groups and root systems.
11
12Type \code{lie.[tab]} for a list of all the functions available
13from your LiE install.  Type \code{lie.[tab]?} for LiE's
14help about a given function.  Type \code{lie(...)} to create
15a new LiE object, and \code{lie.eval(...)} to run a string
16using LiE (and get the result back as a string).
17
18To access the LiE interpreter directly, run lie_console().
19
20
21EXAMPLES:
22    sage: a4 = lie('A4')  # optional -- requires lie package
23    sage: lie.diagram('A4')          # optional
24    O---O---O---O
25    1   2   3   4   
26    A4
27
28    sage: lie.diagram(a4)            # optional
29    O---O---O---O
30    1   2   3   4   
31    A4
32
33    sage: a4.diagram()               # optional
34    O---O---O---O
35    1   2   3   4   
36    A4
37
38    sage: a4.Cartan()                # optional
39         [[ 2,-1, 0, 0]
40         ,[-1, 2,-1, 0]
41         ,[ 0,-1, 2,-1]
42         ,[ 0, 0,-1, 2]
43         ]
44    sage: lie.LR_tensor([3,1],[2,2]) # optional
45    1X[5,3]
46
47
48\subsection{Tutorial}
49
50The following examples are taken from Section 2.1 of the LiE manual.
51
52You can perform basic arithmetic operations in LiE.
53
54    sage: lie.eval('19+68') # optional
55    '87'
56    sage: a = lie('1111111111*1111111111') # optional
57    sage: a # optional
58    1234567900987654321
59    sage: a/1111111111 # optional
60    1111111111
61    sage: a = lie('345') # optional
62    sage: a^2+3*a-5 # optional
63    120055
64    sage: _ / 7*a # optional
65    5916750
66
67Vectors in LiE are created using square brackets.  Notice that
68the indexing in LiE is 1-based, unlike Python/SAGE which is
690-based.
70
71    sage: v = lie('[3,2,6873,-38]') # optional
72    sage: v # optional
73    [3,2,6873,-38]
74    sage: v[3] # optional
75    6873
76    sage: v+v # optional
77    [6,4,13746,-76]
78    sage: v*v # optional
79    47239586
80    sage: v+234786 # optional
81    [3,2,6873,-38,234786]
82    sage: v-3 # optional
83    [3,2,-38]
84    sage: v^v # optional
85    [3,2,6873,-38,3,2,6873,-38]
86
87You can also work with matrices in LiE.
88
89    sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional
90    sage: m # optional
91         [[ 1, 0, 3,3]
92         ,[12, 4,-4,7]
93         ,[-1, 9, 8,0]
94         ,[ 3,-5,-2,9]
95         ]
96    sage: print lie.eval('*'+m._name) # optional
97         [[1,12,-1, 3]
98         ,[0, 4, 9,-5]
99         ,[3,-4, 8,-2]
100         ,[3, 7, 0, 9]
101         ]
102
103    sage: m^3 # optional
104         [[ 220,   87, 81, 375]
105         ,[-168,-1089, 13,1013]
106         ,[1550,  357,-55,1593]
107         ,[-854, -652, 98,-170]
108         ]
109    sage: v*m # optional
110    [-6960,62055,55061,-319]
111    sage: m*v # optional
112    [20508,-27714,54999,-14089]
113    sage: v*m*v # optional
114    378549605
115    sage: m+v # optional
116         [[ 1, 0,   3,  3]
117         ,[12, 4,  -4,  7]
118         ,[-1, 9,   8,  0]
119         ,[ 3,-5,  -2,  9]
120         ,[ 3, 2,6873,-38]
121         ]
122
123    sage: m-2 # optional
124         [[ 1, 0, 3,3]
125         ,[-1, 9, 8,0]
126         ,[ 3,-5,-2,9]
127         ]
128
129
130LiE handles multivariate (Laurent) polynomials.
131
132    sage: lie('X[1,2]') # optional
133    1X[1,2]
134    sage: -3*_ # optional
135    -3X[1,2]
136    sage: _ + lie('4X[-1,4]') # optional
137    4X[-1,4] - 3X[ 1,2]
138    sage: _^2 # optional
139    16X[-2,8] - 24X[ 0,6] +  9X[ 2,4]
140    sage: lie('(4X[-1,4]-3X[1,2])*(X[2,0]-X[0,-4])') # optional
141    -4X[-1, 0] + 3X[ 1,-2] + 4X[ 1, 4] - 3X[ 3, 2]
142    sage: _ - _ # optional
143    0X[0,0]
144
145
146You can call LiE's built-in functions using lie.functionname .
147
148    sage: lie.partitions(6) # optional
149         [[6,0,0,0,0,0]
150         ,[5,1,0,0,0,0]
151         ,[4,2,0,0,0,0]
152         ,[4,1,1,0,0,0]
153         ,[3,3,0,0,0,0]
154         ,[3,2,1,0,0,0]
155         ,[3,1,1,1,0,0]
156         ,[2,2,2,0,0,0]
157         ,[2,2,1,1,0,0]
158         ,[2,1,1,1,1,0]
159         ,[1,1,1,1,1,1]
160         ]
161    sage: lie.diagram('E8') # optional
162            O 2
163            |
164            |
165    O---O---O---O---O---O---O
166    1   3   4   5   6   7   8   
167    E8
168
169
170You can define your own functions in LiE using lie.eval .  Once you've defined
171a function (say f), you can call it using lie.f ; however, user-defined functions
172do not show up when using tab-completion.
173
174    sage: lie.eval('f(int x) = 2*x') # optional
175    ''
176    sage: lie.f(984) # optional
177    1968
178    sage: lie.eval('f(int n) = a=3*n-7; if a < 0 then a = -a fi; 7^a+a^3-4*a-57') # optional
179    ''
180    sage: lie.f(2) # optional
181    -53
182    sage: lie.f(5) # optional
183    5765224
184
185
186
187LiE's help can be accessed through lie.help('functionname') where
188functionname is the function you want to receive help for.
189
190   sage: print lie.help('diagram') # optional
191   diagram(g).   Prints the Dynkin diagram of g, also indicating
192      the type of each simple component printed, and labeling the nodes as
193      done by Bourbaki (for the second and further simple components the
194      labels are given an offset so as to make them disjoint from earlier
195      labels). The labeling of the vertices of the Dynkin diagram prescribes
196      the order of the coordinates of root- and weight vectors used in LiE.
197
198This can also be accessed with lie.functionname? .
199
200
201
202With the exception of groups, all LiE data types can be converted into
203native SAGE data types by calling the .sage() method.
204
205Integers:
206
207    sage: a = lie('1234') # optional
208    sage: b = a.sage(); b # optional
209    1234
210    sage: type(b) # optional
211    <type 'sage.rings.integer.Integer'>
212
213Vectors:
214
215    sage: a = lie('[1,2,3]')# optional
216    sage: b = a.sage(); b # optional
217    [1, 2, 3]
218    sage: type(b) # optional
219    <type 'list'>
220
221Matrices:
222
223    sage: a = lie('[[1,2],[3,4]]') # optional
224    sage: b = a.sage(); b # optional
225    [1 2]
226    [3 4]
227    sage: type(b) # optional
228    <type 'sage.matrix.matrix_integer_dense.Matrix_integer_dense'>
229
230
231Polynomials:
232
233    sage: a = lie('X[1,2] - 2*X[2,1]') # optional
234    sage: b = a.sage(); b # optional
235    -2*x0^2*x1 + x0*x1^2
236    sage: is_MPolynomial(b) # optional
237    True
238
239
240Text:
241
242    sage: a = lie('"text"') # optional
243    sage: b = a.sage(); b # optional
244    'text'
245    sage: type(b) # optional
246    <type 'str'>
247
248
249LiE can be programmed using the SAGE interface as well. Section 5.1.5
250of the manual gives an example of a function written in LiE's language
251which evaluates a polynomial at a point.  Below is a (roughly) direct
252translation of that program into Python / SAGE.
253
254    sage: def eval_pol(p, pt): # optional
255    ...       s = 0
256    ...       for i in range(1,p.length().sage()+1):
257    ...           m = 1
258    ...           for j in range(1,pt.size().sage()+1):
259    ...               m *= pt[j]^p.expon(i)[j]
260    ...           s += p.coef(i)*m   
261    ...       return s   
262    sage: a = lie('X[1,2]') # optional
263    sage: b1 = lie('[1,2]') # optional
264    sage: b2 = lie('[2,3]') # optional
265    sage: eval_pol(a, b1) # optional
266    4
267    sage: eval_pol(a, b2) # optional
268    18
269   
270
271   
272AUTHORS:
273    -- Mike Hansen 2007-08-27
274    -- William Stein (template)
275"""
276
277##########################################################################
278#
279#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>
280#       Copyright (C) 2006 William Stein <wstein@gmail.com>
281#
282#  Distributed under the terms of the GNU General Public License (GPL)
283#
284#                  http://www.gnu.org/licenses/
285#
286##########################################################################
287
288from expect import Expect, ExpectElement, ExpectFunction, FunctionElement, AsciiArtString
289from sage.misc.misc import verbose, DOT_SAGE, SAGE_LOCAL
290
291
292COMMANDS_CACHE = '%s/lie_commandlist_cache.sobj'%DOT_SAGE
293HELP_CACHE = '%s/lie_helpdict_cache.sobj'%DOT_SAGE
294
295class LiE(Expect):
296    r"""
297    Interface to the LiE interpreter.
298
299    Type \code{lie.[tab]} for a list of all the functions available
300    from your LiE install.  Type \code{lie.[tab]?} for LiE's
301    help about a given function.  Type \code{lie(...)} to create
302    a new LiE object, and \code{lie.eval(...)} to run a string
303    using LiE (and get the result back as a string).
304
305    """
306    def __init__(self, stacksize=10000000,   # 10MB
307                 maxread=100000, script_subdirectory=None,
308                 logfile=None,
309                 server=None,
310                 init_list_length=1024):
311        Expect.__init__(self,
312
313                        # The capitalized versionof this is used for printing.
314                        name = 'LiE',
315
316                        # This is regexp of the input prompt.  If you can change
317                        # it to be very obfuscated that would be better.   Even
318                        # better is to use sequence numbers.
319                        prompt = '> ',
320
321                        # This is the command that starts up your program
322                        command = "bash "+ SAGE_LOCAL + "/bin/lie",
323
324                        maxread = maxread,
325                        server=server,
326                        script_subdirectory = script_subdirectory,
327
328                        # If this is true, then whenever the user presses Control-C to
329                        # interrupt a calculation, the whole interface is restarted.
330                        restart_on_ctrlc = False,
331
332                        # If true, print out a message when starting
333                        # up the command when you first send a command
334                        # to this interface.
335                        verbose_start = False,
336
337                        logfile=logfile,
338
339                        # If an input is longer than this number of characters, then
340                        # try to switch to outputing to a file.
341                        eval_using_file_cutoff=1024)
342       
343        self.__seq = 0
344        self.__var_store_len = 0
345        self.__init_list_length = init_list_length
346
347        self.__trait_names_dict = None
348        self.__trait_names_list = None
349        self.__help_dict = None
350
351    def __read_info_files(self):
352        use_disk_cache = True
353
354        import sage.misc.persist
355        if use_disk_cache:
356            try:
357                trait_dict = sage.misc.persist.load(COMMANDS_CACHE)
358                help_dict = sage.misc.persist.load(HELP_CACHE)
359                v = []
360                for key in trait_dict:
361                    v += trait_dict[key]
362                self.__trait_names_list = v
363                self.__trait_names_dict = trait_dict
364                self.__help_dict = help_dict
365                return
366            except IOError:
367                pass
368           
369
370        #Go through INFO.3 and get the necessary information
371        filenames = ['INFO.3', 'INFO.0']
372        commands = {}
373        commands['vid'] = []
374        help = {}
375
376
377        for f in filenames:
378            filename = SAGE_LOCAL + "/lib/lie/" + f
379            info = open(filename)
380            prev_command = ""
381            help_text = ""
382            for line in info:
383                #If the line doesn't start with an "@", then
384                #it is part of the help text for the previous
385                #command
386                if len(line) == 0 or line[0] != "@":
387                    if prev_command != "":
388                        help_text += line
389                    continue
390
391               
392                #Do not add not completions that do not start with an
393                #alphabetical character or that contain 'silence'
394                if len(line) > 1 and (not line[1].isalpha() or line.find('silence') != -1):
395                    help[prev_command] = help.get(prev_command, "") + help_text
396                    help_text = ""
397                    prev_command = ""
398                    continue
399
400
401                #At this point we should be at the start of a new
402                #command definition
403
404               
405                #Get the type of the first argument of the command
406                i = line.find('(')
407                if line[i+1] == ")":
408                    t = 'vid'
409                else:
410                    t = line[i+1:i+4]
411
412                #Save the help text for the command
413                help[prev_command] = help.get(prev_command, "") + help_text
414                help_text = ""
415                prev_command = line[1:i]
416
417                #Add the commad
418                if t in commands:
419                    commands[t].append(line[1:i])
420                else:
421                    commands[t] = [ line[1:i] ]
422
423            #Take care of the last help text which doesn't get processed
424            #since there's no following @ symbol
425            help[prev_command] = help.get(prev_command, "") + help_text
426
427        info.close()
428
429
430        #Build the list of all possible command completions
431        l = []
432        for key in commands:
433            l += commands[key]
434
435        #Save the data
436        self.__trait_names_dict = commands
437        self.__trait_names_list = l
438        self.__help_dict = help
439
440        #Write them to file
441        if use_disk_cache:
442            sage.misc.persist.save(commands, COMMANDS_CACHE)
443            sage.misc.persist.save(help, HELP_CACHE)       
444
445    def _repr_(self):
446        return 'LiE Interpreter'
447
448    def __reduce__(self):
449        return reduce_load_lie, tuple([])
450   
451    def __getattr__(self, attrname):
452        if attrname[:1] == "_":
453            raise AttributeError
454        return LiEFunction(self, attrname)
455
456    def _quit_string(self):
457        return 'quit'
458   
459    def _read_in_file_command(self, filename):
460        raise NotImplementedError       
461
462
463    def trait_names(self, type=None, verbose=False, use_disk_cache=True):
464        if self.__trait_names_dict is None:
465            self.__read_info_files()       
466        if type:
467            return self.__trait_names_dict[type]
468        else:
469            return self.__trait_names_list
470
471       
472    def read(self, filename):
473        # [[implement loading of the contents of filename into the system]]
474        self.eval('read %s'%filename)
475
476
477    def kill(self, var):
478        # [[send code that kills the variable with given name in the system.]]
479        pass
480       
481    def console(self):
482        # run the console command (defined below).
483        lie_console()
484
485    def version(self):
486        # run the version command (defined below)
487        pass
488   
489    def _object_class(self):
490        return LiEElement
491
492    def _true_symbol(self):
493        # return the string rep of truth, i.e., what the system outputs
494        # when you type 1==1.
495        return '1'
496
497    def _false_symbol(self):
498        # return the string rep of false, i.e., what the system outputs
499        # when you type 1==2.
500        return '0'
501       
502    def _equality_symbol(self):
503        # return the symbol for checking equality, e.g., == or eq.
504        return '=='
505
506    def help(self, command):
507        # return help on a given command.
508        if self.__help_dict is None:
509            self.__read_info_files()
510        try:
511            return self.__help_dict[command]
512        except KeyError:
513            return "Could not find help for " + command
514
515    def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True):
516        #if line.find('\n') != -1:
517        #    raise ValueError, "line must not contain any newlines"
518        if allow_use_file and self._eval_using_file_cutoff and len(line) > self._eval_using_file_cutoff:
519            return self._eval_line_using_file(line, tmp)
520        try:
521            if self._expect is None:
522                self._start()
523            E = self._expect
524            try:
525                if len(line) >= 4096:
526                    raise RuntimeError, "Sending more than 4096 characters with %s on a line may cause a hang and you're sending %s characters"%(self, len(line))
527                E.sendline(line)
528                if wait_for_prompt == False:
529                    return ''
530               
531            except OSError, msg:
532                raise RuntimeError, "%s\nError evaluating %s in %s"%(msg, line, self)
533           
534            if len(line)>0:
535                try:
536                    if isinstance(wait_for_prompt, basestring):
537                        E.expect(wait_for_prompt)
538                    else:
539                        E.expect(self._prompt)
540                except pexpect.EOF, msg:
541                    try:
542                        if self._read_in_file_command(tmp) in line:
543                            raise pexpect.EOF, msg
544                    except NotImplementedError:
545                        pass
546                    if self._quit_string() in line:
547                        # we expect to get an EOF if we're quitting.
548                        return ''
549                    raise RuntimeError, "%s\n%s crashed executing %s"%(msg,
550                                                   self, line)
551                out = E.before
552            else:
553                out = '\n\r'
554        except KeyboardInterrupt:
555            self._keyboard_interrupt()
556            raise KeyboardInterrupt, "Ctrl-c pressed while running %s"%self
557        i = out.find("\n")
558        j = out.rfind("\r")
559
560        err = max( out.find("\n(in"), out.find('not defined'), out.find('Argument types')  )
561        if err != -1:
562            raise RuntimeError, "An error occured running a LiE command:\n%s"%(out[i+1:j].replace('\r\n','\n'))
563       
564        return out[i+1:j].replace('\r\n','\n')
565
566
567    def eval(self, code, strip=True, **kwds):
568        s = Expect.eval(self,code, strip=True, **kwds)
569        #return s.strip()
570        if len(s) > 0 and s.find("\n") != -1:
571            return s
572        else:
573            return s.strip()
574
575    def set(self, var, value):
576        """
577        Set the variable var to the given value.
578        """
579        cmd = '%s=%s'%(var,value)
580        out = self.eval(cmd)
581        i = min( out.find('not defined'), out.find('\(in'), out.find('Argument types') )
582        if i != -1:
583            raise RuntimeError, out
584
585    def get(self, var):
586        """
587        Get the value of the variable var.
588        """
589        s = self.eval('%s'%var)
590        return s
591
592    def get_using_file(self, var):
593        raise NotImplementedError
594
595    def function_call(self, function, args=[]):
596        if function == '':
597            raise ValueError, "function name must be nonempty"
598        if function[:2] == "__":
599            raise AttributeError
600        if not isinstance(args, list):
601            args = [args]
602        for i in range(len(args)):
603            if not isinstance(args[i], ExpectElement):
604                args[i] = self.new(args[i])
605        #Handle the functions that do not return a value that can
606        #be assigned to a variable
607        if function in ['diagram', 'setdefault', 'print_tab', 'type', 'factor', 'void', 'gcol']:
608            return AsciiArtString(self.eval("%s(%s)"%(function, ",".join([s.name() for s in args]))))
609        #Otherwise, create a new object
610        else:
611            return self.new("%s(%s)"%(function, ",".join([s.name() for s in args])))
612
613class LiEElement(ExpectElement):
614    """
615    Describe elements of your system here.
616    """
617    def __getattr__(self, attrname):
618        if attrname[:1] == "_":
619            raise AttributeError
620        return LiEFunctionElement(self, attrname)
621   
622    def trait_names(self):
623        # This is if your system doesn't really have types.  If you have types
624        # this function should only return the relevant methods that take self
625        # as their first argument.
626        return self.parent().trait_names(type=self.type())
627
628    def type(self):
629        t = self.parent().eval('type(%s)'%self._name)
630        i = t.find(':')
631        return t[i+1:].strip()
632
633
634    def _matrix_(self, R):
635        if self.type() == 'mat':
636            return R( eval( str(self).replace('\n','').strip() ) )
637        else:
638            raise ValueError, "not a matrix"
639
640    def _vector_(self, R):
641        if self.type() == 'vec':
642            return R(eval(str(self)))
643        else:
644            raise ValueError, "not a vector"
645   
646    def sage(self):
647        t = self.type()
648        if t == 'grp':
649            raise ValueError, "cannot convert Lie groups to native SAGE objects"
650        elif t == 'mat':
651            import sage.matrix.constructor         
652            return  sage.matrix.constructor.matrix( eval( str(self).replace('\n','').strip())  )
653        elif t == 'pol':
654            import sage.misc.misc
655            from sage.rings.all import PolynomialRing, QQ
656           
657            #Figure out the number of variables
658            s = str(self)
659            open_bracket = s.find('[')
660            close_bracket = s.find(']')
661            nvars = len(s[open_bracket:close_bracket].split(','))
662
663            #create the polynomial ring
664            R = PolynomialRing(QQ, nvars, 'x')
665            x = R.gens()
666            pol = R(0)
667
668            #Split up the polynomials into terms
669            terms = []
670            for termgrp in s.split(' - '):
671                #The first entry in termgrp has
672                #a negative coefficient
673                termgrp = "-"+termgrp.strip()
674                terms += termgrp.split('+')
675            #Make sure we don't accidently add a negative
676            #sign to the first monomial
677            if s[0] != "-":
678                terms[0] = terms[0][1:]
679
680            #go through all the terms in s
681            for term in terms:
682                xpos = term.find('X')
683                coef = eval(term[:xpos].strip())
684                exps = eval(term[xpos+1:].strip())
685                monomial = sage.misc.misc.prod(map(lambda i: x[i]**exps[i] , range(nvars)))
686                pol += coef * monomial
687
688            return pol
689        elif t == 'tex':
690            return repr(self)
691        elif t == 'vid':
692            return None
693        else:
694            return ExpectElement.sage(self)
695                           
696
697class LiEFunctionElement(FunctionElement):
698    def _sage_doc_(self):
699        M = self._obj.parent()
700        return M.help(self._name)
701       
702   
703class LiEFunction(ExpectFunction):
704    def _sage_doc_(self):
705        M = self._parent
706        return M.help(self._name)
707       
708
709
710def is_LiEElement(x):
711    return isinstance(x, LiEElement)
712
713# An instance
714lie = LiE()
715
716def reduce_load_lie():
717    return lie
718
719import os
720def lie_console():
721    os.system('bash `which lie`')
722
723
724def lie_version():
725    raise NotImplementedError
Note: See TracBrowser for help on using the repository browser.