~dinko-metalac/calculus-app2/trunk

« back to all changes in this revision

Viewing changes to lib/py/sympy/core/logic.py

  • Committer: dinko.metalac at gmail
  • Date: 2015-04-14 13:28:14 UTC
  • Revision ID: dinko.metalac@gmail.com-20150414132814-j25k3qd7sq3warup
new sympy

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
at present this is mainly needed for facts.py , feel free however to improve
7
7
this stuff for general purpose.
8
8
"""
9
 
from sympy.core.compatibility import iterable, cmp
 
9
from __future__ import print_function, division
 
10
 
 
11
from sympy.core.compatibility import iterable
 
12
 
 
13
 
 
14
def _fuzzy_group(args, quick_exit=False):
 
15
    """Return True if all args are True, None if there is any None else False
 
16
    unless ``quick_exit`` is True (then return None as soon as a second False
 
17
    is seen.
 
18
 
 
19
     ``_fuzzy_group`` is like ``fuzzy_and`` except that it is more
 
20
    conservative in returning a False, waiting to make sure that all
 
21
    arguments are True or False and returning None if any arguments are
 
22
    None. It also has the capability of permiting only a single False and
 
23
    returning None if more than one is seen. For example, the presence of a
 
24
    single transcendental amongst rationals would indicate that the group is
 
25
    no longer rational; but a second transcendental in the group would make the
 
26
    determination impossible.
 
27
 
 
28
 
 
29
    Examples
 
30
    ========
 
31
 
 
32
    >>> from sympy.core.logic import _fuzzy_group
 
33
 
 
34
    By default, multiple Falses mean the group is broken:
 
35
 
 
36
    >>> _fuzzy_group([False, False, True])
 
37
    False
 
38
 
 
39
    If multiple Falses mean the group status is unknown then set
 
40
    `quick_exit` to True so None can be returned when the 2nd False is seen:
 
41
 
 
42
    >>> _fuzzy_group([False, False, True], quick_exit=True)
 
43
 
 
44
    But if only a single False is seen then the group is known to
 
45
    be broken:
 
46
 
 
47
    >>> _fuzzy_group([False, True, True], quick_exit=True)
 
48
    False
 
49
 
 
50
    """
 
51
    saw_other = False
 
52
    for a in args:
 
53
        if a is True:
 
54
            continue
 
55
        if a is None:
 
56
            return
 
57
        if quick_exit and saw_other:
 
58
            return
 
59
        saw_other = True
 
60
    return not saw_other
10
61
 
11
62
 
12
63
def fuzzy_bool(x):
20
71
    return bool(x)
21
72
 
22
73
 
23
 
def fuzzy_and(*args):
 
74
def fuzzy_and(args):
24
75
    """Return True (all True), False (any False) or None.
25
76
 
26
 
    If `a` is an iterable it must not be empty; it
27
 
    can be an iterator.
 
77
    Examples
 
78
    ========
28
79
 
29
80
    >>> from sympy.core.logic import fuzzy_and
30
81
    >>> from sympy import Dummy
46
97
    False
47
98
    """
48
99
 
49
 
    if len(args) == 2:
50
 
        a, b = [fuzzy_bool(i) for i in args]
51
 
        if a is True and b is True:
52
 
            return True
53
 
        elif a is False or b is False:
 
100
    rv = True
 
101
    for ai in args:
 
102
        ai = fuzzy_bool(ai)
 
103
        if ai is False:
54
104
            return False
55
 
    elif (len(args) == 1 and iterable(args[0]) or len(args) > 2):
56
 
        if len(args) == 1:
57
 
            args = args[0]
58
 
        if args:
59
 
            rv = True
60
 
            for ai in args:
61
 
                ai = fuzzy_bool(ai)
62
 
                if ai is False:
63
 
                    return False
64
 
                if rv:  # this will stop updating if a None is ever trapped
65
 
                    rv = ai
66
 
            return rv
67
 
    if not args:
68
 
        raise ValueError('fuzzy_and needs at least 1 argument')
69
 
    elif len(args) == 1:
70
 
        return fuzzy_bool(args[0])
 
105
        if rv:  # this will stop updating if a None is ever trapped
 
106
            rv = ai
 
107
    return rv
71
108
 
72
109
 
73
110
def fuzzy_not(v):
74
111
    """
75
112
    Not in fuzzy logic
76
113
 
77
 
    Will return Not if arg is a boolean value, and None if argument
78
 
    is None.
 
114
    Return None if `v` is None else `not v`.
79
115
 
80
 
    Examples:
 
116
    Examples
 
117
    ========
81
118
 
82
119
    >>> from sympy.core.logic import fuzzy_not
83
120
    >>> fuzzy_not(True)
93
130
        return not v
94
131
 
95
132
 
 
133
def fuzzy_or(args):
 
134
    """
 
135
    Or in fuzzy logic. Returns True (any True), False (all False), or None
 
136
 
 
137
    See the docstrings of fuzzy_and and fuzzy_not for more info.  fuzzy_or is
 
138
    related to the two by the standard De Morgan's law.
 
139
 
 
140
    >>> from sympy.core.logic import fuzzy_or
 
141
    >>> fuzzy_or([True, False])
 
142
    True
 
143
    >>> fuzzy_or([True, None])
 
144
    True
 
145
    >>> fuzzy_or([False, False])
 
146
    False
 
147
    >>> print(fuzzy_or([False, None]))
 
148
    None
 
149
 
 
150
    """
 
151
    return fuzzy_not(fuzzy_and(fuzzy_not(i) for i in args))
 
152
 
 
153
 
96
154
class Logic(object):
97
155
    """Logical expression"""
98
156
    # {} 'op' -> LogicClass
128
186
 
129
187
    def __cmp__(a, b):
130
188
        if type(a) is not type(b):
131
 
            return cmp( str(type(a)), str(type(b)) )
132
 
 
 
189
            a = str(type(a))
 
190
            b = str(type(b))
133
191
        else:
134
 
            return cmp(a.args, b.args)
 
192
            a = a.args
 
193
            b = b.args
 
194
        return (a > b) - (a < b)
135
195
 
136
196
    def __str__(self):
137
197
        return '%s(%s)' % (self.__class__.__name__, ', '.join(str(a) for a in self.args))
140
200
 
141
201
    @staticmethod
142
202
    def fromstring(text):
143
 
        """Logic from string
 
203
        """Logic from string with space around & and | but none after !.
144
204
 
145
205
           e.g.
146
206
 
147
 
           !a & !b | c
 
207
           !a & b | c
148
208
        """
149
209
        lexpr = None  # current logical expression
150
210
        schedop = None  # scheduled operation
159
219
                        '%s cannot be in the beginning of expression' % term)
160
220
                schedop = term
161
221
                continue
 
222
            if '&' in term or '|' in term:
 
223
                raise ValueError('& and | must have space around them')
162
224
            if term[0] == '!':
 
225
                if len(term) == 1:
 
226
                    raise ValueError('do not include space after "!"')
163
227
                term = Not(term[1:])
164
228
 
165
229
            # already scheduled operation, e.g. '&'
290
354
    def arg(self):
291
355
        return self.args[0]
292
356
 
 
357
 
293
358
Logic.op_2class['&'] = And
294
359
Logic.op_2class['|'] = Or
295
360
Logic.op_2class['!'] = Not