facts.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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 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. # primitive predicates for extended real exclude each other.
  48. Exclusive(Q.negative_infinite(x), Q.negative(x), Q.zero(x),
  49. Q.positive(x), Q.positive_infinite(x)),
  50. # build complex plane
  51. Exclusive(Q.real(x), Q.imaginary(x)),
  52. Implies(Q.real(x) | Q.imaginary(x), Q.complex(x)),
  53. # other subsets of complex
  54. Exclusive(Q.transcendental(x), Q.algebraic(x)),
  55. Equivalent(Q.real(x), Q.rational(x) | Q.irrational(x)),
  56. Exclusive(Q.irrational(x), Q.rational(x)),
  57. Implies(Q.rational(x), Q.algebraic(x)),
  58. # integers
  59. Exclusive(Q.even(x), Q.odd(x)),
  60. Implies(Q.integer(x), Q.rational(x)),
  61. Implies(Q.zero(x), Q.even(x)),
  62. Exclusive(Q.composite(x), Q.prime(x)),
  63. Implies(Q.composite(x) | Q.prime(x), Q.integer(x) & Q.positive(x)),
  64. Implies(Q.even(x) & Q.positive(x) & ~Q.prime(x), Q.composite(x)),
  65. # hermitian and antihermitian
  66. Implies(Q.real(x), Q.hermitian(x)),
  67. Implies(Q.imaginary(x), Q.antihermitian(x)),
  68. Implies(Q.zero(x), Q.hermitian(x) | Q.antihermitian(x)),
  69. # define finity and infinity, and build extended real line
  70. Exclusive(Q.infinite(x), Q.finite(x)),
  71. Implies(Q.complex(x), Q.finite(x)),
  72. Implies(Q.negative_infinite(x) | Q.positive_infinite(x), Q.infinite(x)),
  73. # commutativity
  74. Implies(Q.finite(x) | Q.infinite(x), Q.commutative(x)),
  75. # matrices
  76. Implies(Q.orthogonal(x), Q.positive_definite(x)),
  77. Implies(Q.orthogonal(x), Q.unitary(x)),
  78. Implies(Q.unitary(x) & Q.real_elements(x), Q.orthogonal(x)),
  79. Implies(Q.unitary(x), Q.normal(x)),
  80. Implies(Q.unitary(x), Q.invertible(x)),
  81. Implies(Q.normal(x), Q.square(x)),
  82. Implies(Q.diagonal(x), Q.normal(x)),
  83. Implies(Q.positive_definite(x), Q.invertible(x)),
  84. Implies(Q.diagonal(x), Q.upper_triangular(x)),
  85. Implies(Q.diagonal(x), Q.lower_triangular(x)),
  86. Implies(Q.lower_triangular(x), Q.triangular(x)),
  87. Implies(Q.upper_triangular(x), Q.triangular(x)),
  88. Implies(Q.triangular(x), Q.upper_triangular(x) | Q.lower_triangular(x)),
  89. Implies(Q.upper_triangular(x) & Q.lower_triangular(x), Q.diagonal(x)),
  90. Implies(Q.diagonal(x), Q.symmetric(x)),
  91. Implies(Q.unit_triangular(x), Q.triangular(x)),
  92. Implies(Q.invertible(x), Q.fullrank(x)),
  93. Implies(Q.invertible(x), Q.square(x)),
  94. Implies(Q.symmetric(x), Q.square(x)),
  95. Implies(Q.fullrank(x) & Q.square(x), Q.invertible(x)),
  96. Equivalent(Q.invertible(x), ~Q.singular(x)),
  97. Implies(Q.integer_elements(x), Q.real_elements(x)),
  98. Implies(Q.real_elements(x), Q.complex_elements(x)),
  99. )
  100. return fact
  101. def generate_known_facts_dict(keys, fact):
  102. """
  103. Computes and returns a dictionary which contains the relations between
  104. unary predicates.
  105. Each key is a predicate, and item is two groups of predicates.
  106. First group contains the predicates which are implied by the key, and
  107. second group contains the predicates which are rejected by the key.
  108. All predicates in *keys* and *fact* must be unary and have same placeholder
  109. symbol.
  110. Parameters
  111. ==========
  112. keys : list of AppliedPredicate instances.
  113. fact : Fact between predicates in conjugated normal form.
  114. Examples
  115. ========
  116. >>> from sympy import Q, And, Implies
  117. >>> from sympy.assumptions.facts import generate_known_facts_dict
  118. >>> from sympy.abc import x
  119. >>> keys = [Q.even(x), Q.odd(x), Q.zero(x)]
  120. >>> fact = And(Implies(Q.even(x), ~Q.odd(x)),
  121. ... Implies(Q.zero(x), Q.even(x)))
  122. >>> generate_known_facts_dict(keys, fact)
  123. {Q.even: ({Q.even}, {Q.odd}),
  124. Q.odd: ({Q.odd}, {Q.even, Q.zero}),
  125. Q.zero: ({Q.even, Q.zero}, {Q.odd})}
  126. """
  127. fact_cnf = to_cnf(fact)
  128. mapping = single_fact_lookup(keys, fact_cnf)
  129. ret = {}
  130. for key, value in mapping.items():
  131. implied = set()
  132. rejected = set()
  133. for expr in value:
  134. if isinstance(expr, AppliedPredicate):
  135. implied.add(expr.function)
  136. elif isinstance(expr, Not):
  137. pred = expr.args[0]
  138. rejected.add(pred.function)
  139. ret[key.function] = (implied, rejected)
  140. return ret
  141. @cacheit
  142. def get_known_facts_keys():
  143. """
  144. Return every unary predicates registered to ``Q``.
  145. This function is used to generate the keys for
  146. ``generate_known_facts_dict``.
  147. """
  148. exclude = set()
  149. for pred in [Q.eq, Q.ne, Q.gt, Q.lt, Q.ge, Q.le]:
  150. # exclude polyadic predicates
  151. exclude.add(pred)
  152. result = []
  153. for attr in Q.__class__.__dict__:
  154. if attr.startswith('__'):
  155. continue
  156. pred = getattr(Q, attr)
  157. if pred in exclude:
  158. continue
  159. result.append(pred)
  160. return result
  161. def single_fact_lookup(known_facts_keys, known_facts_cnf):
  162. # Return the dictionary for quick lookup of single fact
  163. mapping = {}
  164. for key in known_facts_keys:
  165. mapping[key] = {key}
  166. for other_key in known_facts_keys:
  167. if other_key != key:
  168. if ask_full_inference(other_key, key, known_facts_cnf):
  169. mapping[key].add(other_key)
  170. if ask_full_inference(~other_key, key, known_facts_cnf):
  171. mapping[key].add(~other_key)
  172. return mapping
  173. def ask_full_inference(proposition, assumptions, known_facts_cnf):
  174. """
  175. Method for inferring properties about objects.
  176. """
  177. if not satisfiable(And(known_facts_cnf, assumptions, proposition)):
  178. return False
  179. if not satisfiable(And(known_facts_cnf, assumptions, Not(proposition))):
  180. return True
  181. return None