Implemented crisscross algorithm for solving LP problems.
[sympycore.git] / sympycore / core.py
blob306872d9fd71ee82a36b499ffa6c7a9a2d7b23bc
1 """Provides Basic class and classes object.
2 """
4 __docformat__ = 'restructuredtext'
5 __all__ = ['classes', 'Expr', 'defined_functions', 'DefinedFunction',
6 'objects', 'Pair', 'IntegerList']
8 import sys
9 import types
10 import inspect
11 import textwrap
13 using_C_Expr = False
14 try:
15 from . import expr_ext as expr_module
16 using_C_Expr = True
17 except ImportError, msg:
18 msg = str(msg)
19 if msg!='No module named expr_ext' and msg!='cannot import name expr_ext':
20 print msg
21 from . import expr as expr_module
23 Expr = expr_module.Expr
24 Pair = expr_module.Pair
26 # Pickling support:
27 def _reconstruct(version, state):
28 # Returned by <Expr instance>.__reduce__
29 if version==1:
30 cls, (head, data), hashvalue = state
31 head = get_head(head)
32 obj = cls(head, data)
33 obj._sethash(hashvalue)
34 return obj
35 elif version==2:
36 from .utils import get_head
37 cls, (head, data), hashvalue = state
38 if type(cls) is tuple:
39 cls = cls[0](*cls[1])
40 head = get_head(head)
41 obj = cls(head, data)
42 obj._sethash(hashvalue)
43 return obj
44 elif version==3:
45 cls, (head, data), hashvalue = state
46 if type(cls) is tuple:
47 cls = cls[0](*cls[1])
48 head = getattr(head, 'as_unique_head', lambda head=head: head)()
49 obj = cls(head, data)
50 obj._sethash(hashvalue)
51 return obj
52 raise NotImplementedError('pickle _reconstruct version=%r' % (version))
54 if using_C_Expr:
55 # To add support pickling pure Expr instances, uncomment the
56 # following line (but it is hackish):
58 #__builtins__['Expr'] = Expr
60 # Pickling Python classes derived from Expr should work fine
61 # without this hack.
62 pass
64 def get_base_attribute(cls, name):
65 """
66 Get attribute from cls base classes.
67 """
68 if name in cls.__dict__:
69 return cls, cls.__dict__[name]
70 for base in cls.__bases__:
71 r = get_base_attribute(base, name)
72 if r is not None:
73 return r
74 return
76 def inheritdoc(obj, cls):
77 """
78 If obj is a function intance then update its documentation string
79 from the corresponding function documentation string defined in
80 cls or one of its base classes.
82 Returns obj.
83 """
84 if isinstance(obj, types.FunctionType):
85 base_cls, base_func, base_doc = None, None, None
86 for base in cls.__bases__:
87 r = get_base_attribute(base, obj.__name__)
88 if r is not None:
89 base_cls, base_func = r
90 base_doc = base_func.__doc__
91 break
92 if base_doc is not None:
93 doc = obj.__doc__ or ''
94 title = '### doc-string inherited from %s.%s ###'\
95 % (base_cls.__name__, obj.__name__)
96 if title not in doc:
97 obj.__doc__ = '%s\n%s\n%s' % (textwrap.dedent(doc),
98 title,
99 textwrap.dedent(base_doc).strip())
100 if obj.__doc__ is None:
101 print '%s.%s does not define documentation string'\
102 % (cls.__name__, obj.__name__)
103 return obj
105 def fcopy(func, name):
107 Return a copy of a function func with a new name.
109 return types.FunctionType(func.func_code, func.func_globals, name, func.func_closure)
111 class MetaCopyMethods(type):
113 Metaclass that makes copies of class methods that are defined
114 by equality. For example,
116 class A:
117 __metaclass__ = MetaCopyMethods
118 def foo(self):
119 pass
121 bar = foo
123 is equivalent to
125 class A:
126 def foo(self):
127 pass
129 bar = fcopy(foo, 'bar')
131 def __init__(cls, name, bases, dict):
132 cls.copy_methods(dict)
133 type.__init__(cls, name, bases, dict)
135 def copy_methods(cls, dict):
136 for name in dict.keys():
137 attr = dict[name]
138 if isinstance(attr, types.FunctionType):
139 if name != attr.__name__:
140 dict[name] = fcopy(attr, name)
142 class MetaInheritDocs(type):
144 Metaclass that sets undefined documentation strings of methods to
145 the documentation strings of base class methods. For example,
147 class A:
148 def foo(self):
149 '''foo doc'''
151 class B(A):
152 __metaclass__ = MetaCopyMethods
153 def foo(self):
154 pass
156 is equivalent to
158 class A:
159 def foo(self):
160 '''foo doc'''
162 class B(A):
163 def foo(self):
164 '''### doc-string inherited from A.foo ###
165 foo doc
169 def __init__(cls, name, bases, dict):
170 type.__init__(cls, name, bases, dict)
171 cls.inherit_docs(dict)
173 def inherit_docs(cls, dict):
174 for name in dict.keys():
175 attr = dict[name]
176 if hasattr(attr, '__doc__'):
177 inheritdoc(attr, cls)
179 class MetaCopyMethodsInheritDocs(MetaCopyMethods, MetaInheritDocs):
181 A composite of MetaCopyMethods and MetaInheritDocs meta classes.
183 def __init__(cls, name, bases, dict):
184 cls.copy_methods(dict)
185 type.__init__(cls, name, bases, dict)
186 cls.inherit_docs(dict)
188 class Holder:
189 """ Holds pairs ``(name, value)`` as instance attributes.
191 The set of Holder pairs is extendable by
195 <Holder instance>.<name> = <value>
197 and the values are accessible as
201 value = <Holder instance>.<name>
203 def __init__(self, descr):
204 self._descr = descr
205 self._counter = 0
207 def __str__(self):
208 return self._descr % (self.__dict__)
210 def __repr__(self):
211 return '%s(%r)' % (self.__class__.__name__, str(self))
213 def __setattr__(self, name, obj):
214 if not self.__dict__.has_key(name) and self.__dict__.has_key('_counter'):
215 self._counter += 1
216 self.__dict__[name] = obj
218 def iterNameValue(self):
219 for k,v in self.__dict__.iteritems():
220 if k.startswith('_'):
221 continue
222 yield k,v
224 classes = Holder('SympyCore classes holder (%(_counter)s classes)')
225 objects = Holder('SympyCore objects holder (%(_counter)s classes)')
226 heads = Holder('SympyCore expression heads holder (%(_counter)s heads)')
227 heads_precedence = Holder('SympyCore heads precedence holder (%(_counter)s items).')
228 defined_functions = Holder('SympyCore defined functions holder (%(_counter)s classes)')
230 class FunctionType(type):
231 """ Metaclass to Function class.
233 FunctionType implements the following features:
235 1. If a class derived from ``Function`` has a name containing
236 substring ``Function`` then the class will be saved as an
237 attribute to ``classes`` holder. Such classes are assumed to
238 be base classes to defined functions.
240 2. Otherwise, ``Function`` subclasses are saved as attributes to
241 ``defined_functions`` holder.
245 def __new__(typ, name, bases, attrdict):
246 cls = type.__new__(typ, name, bases, attrdict)
247 if 'Function' in name:
248 setattr(classes, name, cls)
249 else:
250 setattr(defined_functions, name, cls)
251 return cls
253 class DefinedFunction(object):
254 """ Base class to symbolic functions.
256 __metaclass__ = FunctionType
258 @classmethod
259 def derivative(cls, arg):
260 """ Return derivative function of cls at arg.
262 raise NotImplementedError(`cls, arg`)
264 @classmethod
265 def get_argument_algebras(cls):
266 raise NotImplementedError(`cls`)
268 def get_nargs(obj):
269 assert callable(obj),`obj`
270 if isinstance(obj, classes.FunctionRing):
271 return obj.nargs
272 if inspect.isclass(obj):
273 return len(inspect.getargspec(obj.__new__)[0])-1
274 if inspect.isfunction(obj):
275 return len(inspect.getargspec(obj)[0])
276 if inspect.ismethod(obj):
277 return len(inspect.getargspec(obj)[0]) - 1
278 raise NotImplementedError(`obj, type(obj)`)
280 classes.Expr = Expr
281 classes.Pair = Pair
283 class SymbolicEquality:
284 """ Contex for logical operations.
286 In the ``SymbolicEquality(<Algebra class>)`` context relational
287 operations return ``Logic`` instances instead of computing
288 lexicographic value of relational operations.
290 For example,
292 >>> x = Calculus('x')
293 >>> with SymbolicEquality(Calculus):
294 >>> print x==x
295 ...
296 ...
297 x==x
298 >>> print x==x
299 True
301 Add ``from __future__ import with_statement`` to the header of
302 python file when using Python version 2.5.
306 def __init__(self, *classes):
307 self.classes = classes
309 def __enter__(self):
310 for cls in self.classes:
311 cls.enable_symbolic_comparison()
313 def __exit__(self, type, value, tb):
314 for cls in self.classes:
315 cls.disable_symbolic_comparison()
316 return tb is None
318 class UnevaluatedAddition:
319 """ Contex for not evaluating additive group operations.
321 For example,
323 >>> x = Calculus('x')
324 >>> with UnevaluatedAddition(AdditiveGroup):
325 >>> print x - x
326 ...
327 ...
328 x - x
329 >>> print x - x
332 Add ``from __future__ import with_statement`` to the header of
333 python file when using Python version 2.5.
337 def __init__(self, *classes):
338 self.classes = classes
340 def __enter__(self):
341 for cls in self.classes:
342 cls.algebra_options['evaluate_addition'] = False
343 cls.algebra_options['evaluate_multiplication'] = False
345 def __exit__(self, type, value, tb):
346 for cls in self.classes:
347 cls.algebra_options['evaluate_addition'] = True
348 cls.algebra_options['evaluate_multiplication'] = True
349 return tb is None
351 class InitModule:
353 """ Holds a list of functions (or callable objects), composed by
354 that are called exactly once by the execute methods. Functions are
355 assumed to take one argument holding a module object. InitModule
356 instance can be used as a decorator or functions can be added to
357 the list using the register method.
360 def __init__(self):
361 self.func_list = []
363 def register(self, init_module_func):
364 """ Register a function for execution.
366 if callable(init_module_func):
367 self.func_list.append(init_module_func)
368 else:
369 raise NotImplementedError(`init_module_func`)
371 def execute(self):
372 """ Execute registered functions and discard them.
374 while self.func_list:
375 func = self.func_list.pop(0)
376 module_name = func.__module__
377 module = sys.modules[module_name]
378 #print 'Executing %s(%s):' % (func.__name__, module.__name__)
379 func(module)
381 def __call__(self, func):
382 """ Register function via decorating it.
384 self.register(func)
386 def import_heads(self):
387 """Registers a function that adds heads symbols to a calling
388 module.
390 frame = sys._getframe(1)
391 module_name = frame.f_locals['__name__']
393 def _import_heads(module):
394 from sympycore.core import heads
395 for n,h in heads.iterNameValue():
396 setattr(module, n, h)
398 _import_heads.__module__ = module_name
400 self.register(_import_heads)
402 def import_lowlevel_operations(self):
403 """Registers a function that adds lowlevel operation functions
404 like dict_get_item, dict_add_item, etc to a calling module.
406 frame = sys._getframe(1)
407 module_name = frame.f_locals['__name__']
409 def _import_lowlevel_operations(module):
410 for n in dir(expr_module):
411 if (('dict' in n or 'new' in n) and not n.startswith('_')) \
412 or n in ['add', 'mul', 'term_coeff']:
413 setattr(module, n, getattr(expr_module, n))
414 module.IntegerList = IntegerList
416 _import_lowlevel_operations.__module__ = module_name
418 self.register(_import_lowlevel_operations)
420 def import_numbers(self):
421 """Registers a function that adds numbers and number types
422 collections to a calling module.
424 frame = sys._getframe(1)
425 module_name = frame.f_locals['__name__']
427 def _import_numbers(module):
428 from sympycore.arithmetic import numbers
429 from sympycore.arithmetic import number_theory
430 module.numbertypes = numbers.numbertypes
431 module.numbertypes_set = numbers.numbertypes_set
432 module.inttypes = numbers.inttypes
433 module.inttypes_set = numbers.inttypes_set
434 module.rationaltypes = numbers.rationaltypes
435 module.realtypes = numbers.realtypes
436 module.complextypes = numbers.complextypes
437 module.Infinity = numbers.Infinity
438 module.number_div = numbers.number_div
439 module.mpq = numbers.mpq
440 module.try_power = numbers.try_power
441 module.gcd = number_theory.gcd
443 _import_numbers.__module__ = module_name
445 self.register(_import_numbers)
447 init_module = InitModule()
448 init_module.register(expr_module.init_module)
450 init_module.import_heads()
452 class IntegerList(Expr):
453 """ IntegerList holds a list of integers and supports element-wise
454 arithmetic operations.
457 @classmethod
458 def convert(cls, arg, typeerror=True):
459 t = type(arg)
460 if t is int or t is long:
461 return cls(INTEGER_LIST, [arg])
462 if t is list:
463 return cls(INTEGER_LIST, arg)
464 if t is tuple:
465 return cls(INTEGER_LIST, list(arg))
466 if t is IntegerList:
467 return arg
468 if typeerror:
469 raise TypeError('failed to convert %r to IntegerList' % (arg))
470 return NotImplemented
472 def __repr__(self):
473 return '%s(%s)' % (type(self).__name__, self.data)
475 def __len__(self): return len(self.data)
477 def copy(self):
478 return type(self)(self.data[:])
480 def __getitem__(self, index):
481 return self.data[index]
483 def __setitem__(self, index, value):
484 if self.is_writable:
485 self.data[index] = value
486 else:
487 raise TypeError('this IntegerList instance is not writable')
489 def __pos__(self):
490 return self
492 def __neg__(self):
493 return IntegerList(INTEGER_LIST, [-i for i in self.data])
495 def __add__(self, other):
496 lhead, ldata = self.pair
497 t = type(other)
498 if t is int or t is long:
499 if other==0:
500 return self
501 return IntegerList(INTEGER_LIST, [i + other for i in ldata])
502 if t is IntegerList:
503 rdata = other.data
504 elif t is list or t is tuple:
505 rdata = other
506 else:
507 return NotImplemented
508 if len(ldata)!=len(rdata):
509 raise TypeError('IntegerList.__add__ operands must have the same size')
510 return IntegerList(INTEGER_LIST, [i + j for i,j in zip(ldata, rdata)])
512 __radd__ = __add__
514 def __sub__(self, other):
515 t = type(other)
516 if t is int or t is long:
517 if other==0:
518 return self
519 return IntegerList(INTEGER_LIST, [i - other for i in self.data])
520 ldata = self.data
521 if t is IntegerList:
522 rdata = other.data
523 elif t is list or t is tuple:
524 rdata = other
525 else:
526 return NotImplemented
527 if len(ldata)!=len(rdata):
528 raise TypeError('IntegerList.__sub__ operands must have the same size')
529 return IntegerList(INTEGER_LIST, [i - j for i,j in zip(ldata, rdata)])
531 def __rsub__(self, other):
532 return (-self) + other
534 def __mul__(self, other):
535 t = type(other)
536 if t is int or t is long:
537 if other==1:
538 return self
539 return IntegerList(INTEGER_LIST, [i * other for i in self.data])
540 ldata = self.data
541 if t is IntegerList:
542 rdata = other.data
543 elif t is list or t is tuple:
544 rdata = other
545 else:
546 return NotImplemented
547 if len(ldata)!=len(rdata):
548 raise TypeError('IntegerList.__mul__ operands must have the same size')
549 return IntegerList(INTEGER_LIST, [i * j for i,j in zip(ldata, rdata)])
551 __rmul__ = __mul__