Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

""" 

This module contains the machinery handling assumptions. 

 

All symbolic objects have assumption attributes that can be accessed via 

.is_<assumption name> attribute. 

 

Assumptions determine certain properties of symbolic objects and can 

have 3 possible values: True, False, None. True is returned if the 

object has the property and False is returned if it doesn't or can't 

(i.e. doesn't make sense): 

 

>>> from sympy import I 

>>> I.is_algebraic 

True 

>>> I.is_real 

False 

>>> I.is_prime 

False 

 

When the property cannot be determined (or when a method is not 

implemented) None will be returned, e.g. a generic symbol, x, may or 

may not be positive so a value of None is returned for x.is_positive. 

 

By default, all symbolic values are in the largest set in the given context 

without specifying the property. For example, a symbol that has a property 

being integer, is also real, complex, etc. 

 

Here follows a list of possible assumption names: 

 

.. glossary:: 

 

commutative 

object commutes with any other object with 

respect to multiplication operation. 

 

complex 

object can have only values from the set 

of complex numbers. 

 

imaginary 

object value is a number that can be written as a real 

number multiplied by the imaginary unit ``I``. See 

[3]_. Please note, that ``0`` is not considered to be an 

imaginary number, see 

`issue #7649 <https://github.com/sympy/sympy/issues/7649>`_. 

 

real 

object can have only values from the set 

of real numbers. 

 

integer 

object can have only values from the set 

of integers. 

 

odd 

even 

object can have only values from the set of 

odd (even) integers [2]_. 

 

prime 

object is a natural number greater than ``1`` that has 

no positive divisors other than ``1`` and itself. See [6]_. 

 

composite 

object is a positive integer that has at least one positive 

divisor other than ``1`` or the number itself. See [4]_. 

 

zero 

object has the value of ``0``. 

 

nonzero 

object is a real number that is not zero. 

 

rational 

object can have only values from the set 

of rationals. 

 

algebraic 

object can have only values from the set 

of algebraic numbers [11]_. 

 

transcendental 

object can have only values from the set 

of transcendental numbers [10]_. 

 

irrational 

object value cannot be represented exactly by Rational, see [5]_. 

 

finite 

infinite 

object absolute value is bounded (arbitrarily large). 

See [7]_, [8]_, [9]_. 

 

negative 

nonnegative 

object can have only negative (nonnegative) 

values [1]_. 

 

positive 

nonpositive 

object can have only positive (only 

nonpositive) values. 

 

hermitian 

antihermitian 

object belongs to the field of hermitian 

(antihermitian) operators. 

 

Examples 

======== 

 

>>> from sympy import Symbol 

>>> x = Symbol('x', real=True); x 

x 

>>> x.is_real 

True 

>>> x.is_complex 

True 

 

See Also 

======== 

 

.. seealso:: 

 

:py:class:`sympy.core.numbers.ImaginaryUnit` 

:py:class:`sympy.core.numbers.Zero` 

:py:class:`sympy.core.numbers.One` 

 

Notes 

===== 

 

Assumption values are stored in obj._assumptions dictionary or 

are returned by getter methods (with property decorators) or are 

attributes of objects/classes. 

 

 

References 

========== 

 

.. [1] http://en.wikipedia.org/wiki/Negative_number 

.. [2] http://en.wikipedia.org/wiki/Parity_%28mathematics%29 

.. [3] http://en.wikipedia.org/wiki/Imaginary_number 

.. [4] http://en.wikipedia.org/wiki/Composite_number 

.. [5] http://en.wikipedia.org/wiki/Irrational_number 

.. [6] http://en.wikipedia.org/wiki/Prime_number 

.. [7] http://en.wikipedia.org/wiki/Finite 

.. [8] https://docs.python.org/3/library/math.html#math.isfinite 

.. [9] http://docs.scipy.org/doc/numpy/reference/generated/numpy.isfinite.html 

.. [10] http://en.wikipedia.org/wiki/Transcendental_number 

.. [11] http://en.wikipedia.org/wiki/Algebraic_number 

 

""" 

from __future__ import print_function, division 

 

from sympy.core.facts import FactRules, FactKB 

from sympy.core.core import BasicMeta 

from sympy.core.compatibility import integer_types 

 

 

from random import shuffle 

 

 

_assume_rules = FactRules([ 

 

'integer -> rational', 

'rational -> real', 

'rational -> algebraic', 

'algebraic -> complex', 

'real -> complex', 

'real -> hermitian', 

'imaginary -> complex', 

'imaginary -> antihermitian', 

'complex -> commutative', 

 

'odd == integer & !even', 

'even == integer & !odd', 

 

'real == negative | zero | positive', 

'transcendental == complex & !algebraic', 

 

'negative == nonpositive & nonzero', 

'positive == nonnegative & nonzero', 

'zero == nonnegative & nonpositive', 

 

'nonpositive == real & !positive', 

'nonnegative == real & !negative', 

 

'zero -> even & finite', 

 

'prime -> integer & positive', 

'composite -> integer & positive & !prime', 

 

'irrational == real & !rational', 

 

'imaginary -> !real', 

 

'infinite -> !finite', 

'noninteger == real & !integer', 

'nonzero == real & !zero', 

]) 

 

_assume_defined = _assume_rules.defined_facts.copy() 

_assume_defined.add('polar') 

_assume_defined = frozenset(_assume_defined) 

 

 

class StdFactKB(FactKB): 

"""A FactKB specialised for the built-in rules 

 

This is the only kind of FactKB that Basic objects should use. 

""" 

rules = _assume_rules 

 

def __init__(self, facts=None): 

# save a copy of the facts dict 

if not facts: 

self._generator = {}; 

elif not isinstance(facts, FactKB): 

self._generator = facts.copy() 

else: 

self._generator = facts.generator 

if facts: 

self.deduce_all_facts(facts) 

 

def copy(self): 

return self.__class__(self) 

 

@property 

def generator(self): 

return self._generator.copy() 

 

 

def as_property(fact): 

"""Convert a fact name to the name of the corresponding property""" 

return 'is_%s' % fact 

 

 

def make_property(fact): 

"""Create the automagic property corresponding to a fact.""" 

 

def getit(self): 

try: 

return self._assumptions[fact] 

except KeyError: 

if self._assumptions is self.default_assumptions: 

self._assumptions = self.default_assumptions.copy() 

return _ask(fact, self) 

 

getit.func_name = as_property(fact) 

return property(getit) 

 

 

def _ask(fact, obj): 

""" 

Find the truth value for a property of an object. 

 

This function is called when a request is made to see what a fact 

value is. 

 

For this we use several techniques: 

 

First, the fact-evaluation function is tried, if it exists (for 

example _eval_is_integer). Then we try related facts. For example 

 

rational --> integer 

 

another example is joined rule: 

 

integer & !odd --> even 

 

so in the latter case if we are looking at what 'even' value is, 

'integer' and 'odd' facts will be asked. 

 

In all cases, when we settle on some fact value, its implications are 

deduced, and the result is cached in ._assumptions. 

""" 

assumptions = obj._assumptions 

handler_map = obj._prop_handler 

 

# Store None into the assumptions so that recursive attempts at 

# evaluating the same fact don't trigger infinite recursion. 

assumptions._tell(fact, None) 

 

# First try the assumption evaluation function if it exists 

try: 

evaluate = handler_map[fact] 

except KeyError: 

pass 

else: 

a = evaluate(obj) 

if a is not None: 

assumptions.deduce_all_facts(((fact, a),)) 

return a 

 

# Try assumption's prerequisites 

prereq = list(_assume_rules.prereq[fact]) 

shuffle(prereq) 

for pk in prereq: 

if pk in assumptions: 

continue 

if pk in handler_map: 

_ask(pk, obj) 

 

# we might have found the value of fact 

ret_val = assumptions.get(fact) 

if ret_val is not None: 

return ret_val 

 

# Note: the result has already been cached 

return None 

 

 

class ManagedProperties(BasicMeta): 

"""Metaclass for classes with old-style assumptions""" 

def __init__(cls, *args, **kws): 

BasicMeta.__init__(cls, *args, **kws) 

 

local_defs = {} 

for k in _assume_defined: 

attrname = as_property(k) 

v = cls.__dict__.get(attrname, '') 

if isinstance(v, (bool, integer_types, type(None))): 

if v is not None: 

v = bool(v) 

local_defs[k] = v 

 

defs = {} 

for base in reversed(cls.__bases__): 

try: 

defs.update(base._explicit_class_assumptions) 

except AttributeError: 

pass 

defs.update(local_defs) 

 

cls._explicit_class_assumptions = defs 

cls.default_assumptions = StdFactKB(defs) 

 

cls._prop_handler = {} 

for k in _assume_defined: 

try: 

cls._prop_handler[k] = getattr(cls, '_eval_is_%s' % k) 

except AttributeError: 

pass 

 

# Put definite results directly into the class dict, for speed 

for k, v in cls.default_assumptions.items(): 

setattr(cls, as_property(k), v) 

 

# protection e.g. for Integer.is_even=F <- (Rational.is_integer=F) 

derived_from_bases = set() 

for base in cls.__bases__: 

try: 

derived_from_bases |= set(base.default_assumptions) 

except AttributeError: 

continue # not an assumption-aware class 

for fact in derived_from_bases - set(cls.default_assumptions): 

pname = as_property(fact) 

if pname not in cls.__dict__: 

setattr(cls, pname, make_property(fact)) 

 

# Finally, add any missing automagic property (e.g. for Basic) 

for fact in _assume_defined: 

pname = as_property(fact) 

if not hasattr(cls, pname): 

setattr(cls, pname, make_property(fact))