facts.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. """
  2. Known facts in assumptions module.
  3. This module defines the facts between unary predicates in ``get_known_facts()``,
  4. and supports functions to generate the contents in
  5. ``sympy.assumptions.ask_generated`` file.
  6. """
  7. from sympy.assumptions.ask import Q
  8. from sympy.assumptions.assume import AppliedPredicate
  9. from sympy.core.cache import cacheit
  10. from sympy.core.symbol import Symbol
  11. from sympy.logic.boolalg import (to_cnf, And, Not, Implies, Equivalent,
  12. Exclusive,)
  13. from sympy.logic.inference import satisfiable
  14. @cacheit
  15. def get_composite_predicates():
  16. # To reduce the complexity of sat solver, these predicates are
  17. # transformed into the combination of primitive predicates.
  18. return {
  19. Q.real : Q.negative | Q.zero | Q.positive,
  20. Q.integer : Q.even | Q.odd,
  21. Q.nonpositive : Q.negative | Q.zero,
  22. Q.nonzero : Q.negative | Q.positive,
  23. Q.nonnegative : Q.zero | Q.positive,
  24. Q.extended_real : Q.negative_infinite | Q.negative | Q.zero | Q.positive | Q.positive_infinite,
  25. Q.extended_positive: Q.positive | Q.positive_infinite,
  26. Q.extended_negative: Q.negative | Q.negative_infinite,
  27. Q.extended_nonzero: Q.negative_infinite | Q.negative | Q.positive | Q.positive_infinite,
  28. Q.extended_nonpositive: Q.negative_infinite | Q.negative | Q.zero,
  29. Q.extended_nonnegative: Q.zero | Q.positive | Q.positive_infinite,
  30. Q.complex : Q.algebraic | Q.transcendental
  31. }
  32. @cacheit
  33. def get_known_facts(x=None):
  34. """
  35. Facts between unary predicates.
  36. Parameters
  37. ==========
  38. x : Symbol, optional
  39. Placeholder symbol for unary facts. Default is ``Symbol('x')``.
  40. Returns
  41. =======
  42. fact : Known facts in conjugated normal form.
  43. """
  44. if x is None:
  45. x = Symbol('x')
  46. fact = And(
  47. get_number_facts(x),
  48. get_matrix_facts(x)
  49. )
  50. return fact
  51. @cacheit
  52. def get_number_facts(x = None):
  53. """
  54. Facts between unary number predicates.
  55. Parameters
  56. ==========
  57. x : Symbol, optional
  58. Placeholder symbol for unary facts. Default is ``Symbol('x')``.
  59. Returns
  60. =======
  61. fact : Known facts in conjugated normal form.
  62. """
  63. if x is None:
  64. x = Symbol('x')
  65. fact = And(
  66. # primitive predicates for extended real exclude each other.
  67. Exclusive(Q.negative_infinite(x), Q.negative(x), Q.zero(x),
  68. Q.positive(x), Q.positive_infinite(x)),
  69. # build complex plane
  70. Exclusive(Q.real(x), Q.imaginary(x)),
  71. Implies(Q.real(x) | Q.imaginary(x), Q.complex(x)),
  72. # other subsets of complex
  73. Exclusive(Q.transcendental(x), Q.algebraic(x)),
  74. Equivalent(Q.real(x), Q.rational(x) | Q.irrational(x)),
  75. Exclusive(Q.irrational(x), Q.rational(x)),
  76. Implies(Q.rational(x), Q.algebraic(x)),
  77. # integers
  78. Exclusive(Q.even(x), Q.odd(x)),
  79. Implies(Q.integer(x), Q.rational(x)),
  80. Implies(Q.zero(x), Q.even(x)),
  81. Exclusive(Q.composite(x), Q.prime(x)),
  82. Implies(Q.composite(x) | Q.prime(x), Q.integer(x) & Q.positive(x)),
  83. Implies(Q.even(x) & Q.positive(x) & ~Q.prime(x), Q.composite(x)),
  84. # hermitian and antihermitian
  85. Implies(Q.real(x), Q.hermitian(x)),
  86. Implies(Q.imaginary(x), Q.antihermitian(x)),
  87. Implies(Q.zero(x), Q.hermitian(x) | Q.antihermitian(x)),
  88. # define finity and infinity, and build extended real line
  89. Exclusive(Q.infinite(x), Q.finite(x)),
  90. Implies(Q.complex(x), Q.finite(x)),
  91. Implies(Q.negative_infinite(x) | Q.positive_infinite(x), Q.infinite(x)),
  92. # commutativity
  93. Implies(Q.finite(x) | Q.infinite(x), Q.commutative(x)),
  94. )
  95. return fact
  96. @cacheit
  97. def get_matrix_facts(x = None):
  98. """
  99. Facts between unary matrix predicates.
  100. Parameters
  101. ==========
  102. x : Symbol, optional
  103. Placeholder symbol for unary facts. Default is ``Symbol('x')``.
  104. Returns
  105. =======
  106. fact : Known facts in conjugated normal form.
  107. """
  108. if x is None:
  109. x = Symbol('x')
  110. fact = And(
  111. # matrices
  112. Implies(Q.orthogonal(x), Q.positive_definite(x)),
  113. Implies(Q.orthogonal(x), Q.unitary(x)),
  114. Implies(Q.unitary(x) & Q.real_elements(x), Q.orthogonal(x)),
  115. Implies(Q.unitary(x), Q.normal(x)),
  116. Implies(Q.unitary(x), Q.invertible(x)),
  117. Implies(Q.normal(x), Q.square(x)),
  118. Implies(Q.diagonal(x), Q.normal(x)),
  119. Implies(Q.positive_definite(x), Q.invertible(x)),
  120. Implies(Q.diagonal(x), Q.upper_triangular(x)),
  121. Implies(Q.diagonal(x), Q.lower_triangular(x)),
  122. Implies(Q.lower_triangular(x), Q.triangular(x)),
  123. Implies(Q.upper_triangular(x), Q.triangular(x)),
  124. Implies(Q.triangular(x), Q.upper_triangular(x) | Q.lower_triangular(x)),
  125. Implies(Q.upper_triangular(x) & Q.lower_triangular(x), Q.diagonal(x)),
  126. Implies(Q.diagonal(x), Q.symmetric(x)),
  127. Implies(Q.unit_triangular(x), Q.triangular(x)),
  128. Implies(Q.invertible(x), Q.fullrank(x)),
  129. Implies(Q.invertible(x), Q.square(x)),
  130. Implies(Q.symmetric(x), Q.square(x)),
  131. Implies(Q.fullrank(x) & Q.square(x), Q.invertible(x)),
  132. Equivalent(Q.invertible(x), ~Q.singular(x)),
  133. Implies(Q.integer_elements(x), Q.real_elements(x)),
  134. Implies(Q.real_elements(x), Q.complex_elements(x)),
  135. )
  136. return fact
  137. def generate_known_facts_dict(keys, fact):
  138. """
  139. Computes and returns a dictionary which contains the relations between
  140. unary predicates.
  141. Each key is a predicate, and item is two groups of predicates.
  142. First group contains the predicates which are implied by the key, and
  143. second group contains the predicates which are rejected by the key.
  144. All predicates in *keys* and *fact* must be unary and have same placeholder
  145. symbol.
  146. Parameters
  147. ==========
  148. keys : list of AppliedPredicate instances.
  149. fact : Fact between predicates in conjugated normal form.
  150. Examples
  151. ========
  152. >>> from sympy import Q, And, Implies
  153. >>> from sympy.assumptions.facts import generate_known_facts_dict
  154. >>> from sympy.abc import x
  155. >>> keys = [Q.even(x), Q.odd(x), Q.zero(x)]
  156. >>> fact = And(Implies(Q.even(x), ~Q.odd(x)),
  157. ... Implies(Q.zero(x), Q.even(x)))
  158. >>> generate_known_facts_dict(keys, fact)
  159. {Q.even: ({Q.even}, {Q.odd}),
  160. Q.odd: ({Q.odd}, {Q.even, Q.zero}),
  161. Q.zero: ({Q.even, Q.zero}, {Q.odd})}
  162. """
  163. fact_cnf = to_cnf(fact)
  164. mapping = single_fact_lookup(keys, fact_cnf)
  165. ret = {}
  166. for key, value in mapping.items():
  167. implied = set()
  168. rejected = set()
  169. for expr in value:
  170. if isinstance(expr, AppliedPredicate):
  171. implied.add(expr.function)
  172. elif isinstance(expr, Not):
  173. pred = expr.args[0]
  174. rejected.add(pred.function)
  175. ret[key.function] = (implied, rejected)
  176. return ret
  177. @cacheit
  178. def get_known_facts_keys():
  179. """
  180. Return every unary predicates registered to ``Q``.
  181. This function is used to generate the keys for
  182. ``generate_known_facts_dict``.
  183. """
  184. # exclude polyadic predicates
  185. exclude = {Q.eq, Q.ne, Q.gt, Q.lt, Q.ge, Q.le}
  186. result = []
  187. for attr in Q.__class__.__dict__:
  188. if attr.startswith('__'):
  189. continue
  190. pred = getattr(Q, attr)
  191. if pred in exclude:
  192. continue
  193. result.append(pred)
  194. return result
  195. def single_fact_lookup(known_facts_keys, known_facts_cnf):
  196. # Return the dictionary for quick lookup of single fact
  197. mapping = {}
  198. for key in known_facts_keys:
  199. mapping[key] = {key}
  200. for other_key in known_facts_keys:
  201. if other_key != key:
  202. if ask_full_inference(other_key, key, known_facts_cnf):
  203. mapping[key].add(other_key)
  204. if ask_full_inference(~other_key, key, known_facts_cnf):
  205. mapping[key].add(~other_key)
  206. return mapping
  207. def ask_full_inference(proposition, assumptions, known_facts_cnf):
  208. """
  209. Method for inferring properties about objects.
  210. """
  211. if not satisfiable(And(known_facts_cnf, assumptions, proposition)):
  212. return False
  213. if not satisfiable(And(known_facts_cnf, assumptions, Not(proposition))):
  214. return True
  215. return None