m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
7.3 KiB

6 months ago
  1. from .add import Add
  2. from .exprtools import gcd_terms
  3. from .function import Function
  4. from .kind import NumberKind
  5. from .logic import fuzzy_and, fuzzy_not
  6. from .mul import Mul
  7. from .singleton import S
  8. class Mod(Function):
  9. """Represents a modulo operation on symbolic expressions.
  10. Parameters
  11. ==========
  12. p : Expr
  13. Dividend.
  14. q : Expr
  15. Divisor.
  16. Notes
  17. =====
  18. The convention used is the same as Python's: the remainder always has the
  19. same sign as the divisor.
  20. Examples
  21. ========
  22. >>> from sympy.abc import x, y
  23. >>> x**2 % y
  24. Mod(x**2, y)
  25. >>> _.subs({x: 5, y: 6})
  26. 1
  27. """
  28. kind = NumberKind
  29. @classmethod
  30. def eval(cls, p, q):
  31. def doit(p, q):
  32. """Try to return p % q if both are numbers or +/-p is known
  33. to be less than or equal q.
  34. """
  35. if q.is_zero:
  36. raise ZeroDivisionError("Modulo by zero")
  37. if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False:
  38. return S.NaN
  39. if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1):
  40. return S.Zero
  41. if q.is_Number:
  42. if p.is_Number:
  43. return p%q
  44. if q == 2:
  45. if p.is_even:
  46. return S.Zero
  47. elif p.is_odd:
  48. return S.One
  49. if hasattr(p, '_eval_Mod'):
  50. rv = getattr(p, '_eval_Mod')(q)
  51. if rv is not None:
  52. return rv
  53. # by ratio
  54. r = p/q
  55. if r.is_integer:
  56. return S.Zero
  57. try:
  58. d = int(r)
  59. except TypeError:
  60. pass
  61. else:
  62. if isinstance(d, int):
  63. rv = p - d*q
  64. if (rv*q < 0) == True:
  65. rv += q
  66. return rv
  67. # by difference
  68. # -2|q| < p < 2|q|
  69. d = abs(p)
  70. for _ in range(2):
  71. d -= abs(q)
  72. if d.is_negative:
  73. if q.is_positive:
  74. if p.is_positive:
  75. return d + q
  76. elif p.is_negative:
  77. return -d
  78. elif q.is_negative:
  79. if p.is_positive:
  80. return d
  81. elif p.is_negative:
  82. return -d + q
  83. break
  84. rv = doit(p, q)
  85. if rv is not None:
  86. return rv
  87. # denest
  88. if isinstance(p, cls):
  89. qinner = p.args[1]
  90. if qinner % q == 0:
  91. return cls(p.args[0], q)
  92. elif (qinner*(q - qinner)).is_nonnegative:
  93. # |qinner| < |q| and have same sign
  94. return p
  95. elif isinstance(-p, cls):
  96. qinner = (-p).args[1]
  97. if qinner % q == 0:
  98. return cls(-(-p).args[0], q)
  99. elif (qinner*(q + qinner)).is_nonpositive:
  100. # |qinner| < |q| and have different sign
  101. return p
  102. elif isinstance(p, Add):
  103. # separating into modulus and non modulus
  104. both_l = non_mod_l, mod_l = [], []
  105. for arg in p.args:
  106. both_l[isinstance(arg, cls)].append(arg)
  107. # if q same for all
  108. if mod_l and all(inner.args[1] == q for inner in mod_l):
  109. net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l])
  110. return cls(net, q)
  111. elif isinstance(p, Mul):
  112. # separating into modulus and non modulus
  113. both_l = non_mod_l, mod_l = [], []
  114. for arg in p.args:
  115. both_l[isinstance(arg, cls)].append(arg)
  116. if mod_l and all(inner.args[1] == q for inner in mod_l):
  117. # finding distributive term
  118. non_mod_l = [cls(x, q) for x in non_mod_l]
  119. mod = []
  120. non_mod = []
  121. for j in non_mod_l:
  122. if isinstance(j, cls):
  123. mod.append(j.args[0])
  124. else:
  125. non_mod.append(j)
  126. prod_mod = Mul(*mod)
  127. prod_non_mod = Mul(*non_mod)
  128. prod_mod1 = Mul(*[i.args[0] for i in mod_l])
  129. net = prod_mod1*prod_mod
  130. return prod_non_mod*cls(net, q)
  131. if q.is_Integer and q is not S.One:
  132. _ = []
  133. for i in non_mod_l:
  134. if i.is_Integer and (i % q is not S.Zero):
  135. _.append(i%q)
  136. else:
  137. _.append(i)
  138. non_mod_l = _
  139. p = Mul(*(non_mod_l + mod_l))
  140. # XXX other possibilities?
  141. from sympy.polys.polyerrors import PolynomialError
  142. from sympy.polys.polytools import gcd
  143. # extract gcd; any further simplification should be done by the user
  144. try:
  145. G = gcd(p, q)
  146. if G != 1:
  147. p, q = [gcd_terms(i/G, clear=False, fraction=False)
  148. for i in (p, q)]
  149. except PolynomialError: # issue 21373
  150. G = S.One
  151. pwas, qwas = p, q
  152. # simplify terms
  153. # (x + y + 2) % x -> Mod(y + 2, x)
  154. if p.is_Add:
  155. args = []
  156. for i in p.args:
  157. a = cls(i, q)
  158. if a.count(cls) > i.count(cls):
  159. args.append(i)
  160. else:
  161. args.append(a)
  162. if args != list(p.args):
  163. p = Add(*args)
  164. else:
  165. # handle coefficients if they are not Rational
  166. # since those are not handled by factor_terms
  167. # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y)
  168. cp, p = p.as_coeff_Mul()
  169. cq, q = q.as_coeff_Mul()
  170. ok = False
  171. if not cp.is_Rational or not cq.is_Rational:
  172. r = cp % cq
  173. if r == 0:
  174. G *= cq
  175. p *= int(cp/cq)
  176. ok = True
  177. if not ok:
  178. p = cp*p
  179. q = cq*q
  180. # simple -1 extraction
  181. if p.could_extract_minus_sign() and q.could_extract_minus_sign():
  182. G, p, q = [-i for i in (G, p, q)]
  183. # check again to see if p and q can now be handled as numbers
  184. rv = doit(p, q)
  185. if rv is not None:
  186. return rv*G
  187. # put 1.0 from G on inside
  188. if G.is_Float and G == 1:
  189. p *= G
  190. return cls(p, q, evaluate=False)
  191. elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1:
  192. p = G.args[0]*p
  193. G = Mul._from_args(G.args[1:])
  194. return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
  195. def _eval_is_integer(self):
  196. p, q = self.args
  197. if fuzzy_and([p.is_integer, q.is_integer, fuzzy_not(q.is_zero)]):
  198. return True
  199. def _eval_is_nonnegative(self):
  200. if self.args[1].is_positive:
  201. return True
  202. def _eval_is_nonpositive(self):
  203. if self.args[1].is_negative:
  204. return True
  205. def _eval_rewrite_as_floor(self, a, b, **kwargs):
  206. from sympy.functions.elementary.integers import floor
  207. return a - b*floor(a/b)