def checkpdesol(pde, sol, func=None, solve_for_func=True): """ Checks if the given solution satisfies the partial differential equation. pde is the partial differential equation which can be given in the form of an equation or an expression. sol is the solution for which the pde is to be checked. This can also be given in an equation or an expression form. If the function is not provided, the helper function _preprocess from deutils is used to identify the function. If a sequence of solutions is passed, the same sort of container will be used to return the result for each solution. The following methods are currently being implemented to check if the solution satisfies the PDE: 1. Directly substitute the solution in the PDE and check. If the solution hasn't been solved for f, then it will solve for f provided solve_for_func hasn't been set to False. If the solution satisfies the PDE, then a tuple (True, 0) is returned. Otherwise a tuple (False, expr) where expr is the value obtained after substituting the solution in the PDE. However if a known solution returns False, it may be due to the inability of doit() to simplify it to zero. Examples ======== >>> from sympy import Function, symbols, diff >>> from sympy.solvers.pde import checkpdesol, pdsolve >>> x, y = symbols('x y') >>> f = Function('f') >>> eq = 2*f(x,y) + 3*f(x,y).diff(x) + 4*f(x,y).diff(y) >>> sol = pdsolve(eq) >>> assert checkpdesol(eq, sol)[0] >>> eq = x*f(x,y) + f(x,y).diff(x) >>> checkpdesol(eq, sol) (False, (x*F(4*x - 3*y) - 6*F(4*x - 3*y)/25 + 4*Subs(Derivative(F(_xi_1), _xi_1), (_xi_1,), (4*x - 3*y,)))*exp(-6*x/25 - 8*y/25)) """ # Converting the pde into an equation if not isinstance(pde, Equality): pde = Eq(pde, 0) # If no function is given, try finding the function present. if func is None: try: _, func = _preprocess(pde.lhs) except ValueError: funcs = [s.atoms(AppliedUndef) for s in ( sol if is_sequence(sol, set) else [sol])] funcs = reduce(set.union, funcs, set()) if len(funcs) != 1: raise ValueError( 'must pass func arg to checkpdesol for this case.') func = funcs.pop() # If the given solution is in the form of a list or a set # then return a list or set of tuples. if is_sequence(sol, set): return type(sol)(map(lambda i: checkpdesol(pde, i, solve_for_func=solve_for_func), sol)) # Convert solution into an equation if not isinstance(sol, Equality): sol = Eq(func, sol) # Try solving for the function if solve_for_func and not (sol.lhs == func and not sol.rhs.has(func)) and not \ (sol.rhs == func and not sol.lhs.has(func)): try: solved = solve(sol, func) if not solved: raise NotImplementedError except NotImplementedError: pass else: if len(solved) == 1: result = checkpdesol(pde, Eq(func, solved[0]), order=order, solve_for_func=False) else: result = checkpdesol(pde, [Eq(func, t) for t in solved], order=order, solve_for_func=False) # The first method includes direct substitution of the solution in # the PDE and simplifying. pde = pde.lhs - pde.rhs if sol.lhs == func: s = pde.subs(func, sol.rhs).doit() elif sol.rhs == func: s = pde.subs(func, sol.lhs).doit() if s: ss = simplify(s) if ss: return False, ss else: return True, 0 else: return True, 0
class EllipticCurve(): """ Create the following Elliptic Curve over domain. `y^{2} + a_{1} x y + a_{3} y = x^{3} + a_{2} x^{2} + a_{4} x + a_{6}` The default domain is ``QQ``. If no coefficient ``a1``, ``a2``, ``a3``, it create curve as following form. `y^{2} = x^{3} + a_{4} x + a_{6}` Examples ======== References ========== [1] J. Silverman "A Friendly Introduction to Number Theory" Third Edition [2] http://mathworld.wolfram.com/EllipticDiscriminant.html [3] G. Hardy, E. Wright "An Introduction to the Theory of Numbers" Sixth Edition """ def __init__(self, a1, a2, a3, a4, a6, domain=QQ): self._dom = domain # Calculate discriminant self._b2 = a1**2 + 4 * a2 self._b4 = 2 * a4 + a1 * a3 self._b6 = a3**2 + 4 * a6 self._b8 = a1**2 * a6 + 4 * a2 * a6 - a1 * a3 * a4 + a2 * a3**2 - a4**2 self._discrim = self._dom(-self._b2**2 * self._b8 - 8 * self._b4**3 - 27 * self._b6**2 + 9 * self._b2 * self._b4 * self._b6) self._a1 = self._dom(a1) self._a2 = self._dom(a2) self._a3 = self._dom(a3) self._a4 = self._dom(a4) self._a6 = self._dom(a6) self._eq = Eq(y**2 + self._a1 * x * y + self._a3 * y, x**3 + self._a2 * x**2 + self._a4 * x + self._a6) if isinstance(self._dom, FiniteField): self._rank = 0 elif isinstance(self._dom, RationalField): self._rank = None @classmethod def minimal(cls, a4, a6, domain=QQ): return cls(0, 0, 0, a4, a6, domain) def __call__(self, x, y, z=1): if z == 0: return InfinityPoint(self) return Point(x, y, z, self) def __contains__(self, point): if is_sequence(point): if len(point) == 2: z1 = 1 else: z1 = point[2] x1, y1 = point[:2] elif isinstance(point, Point): x1, y1, z1 = point.x, point.y, point.z else: raise ValueError('Invalid point.') if self.characteristic == 0 and z1 == 0: return True return self._eq.subs({x: x1, y: y1}) def __repr__(self): return 'E({}): {}'.format(self._dom, self._eq) def points(self): """ Return points of curve over Finite Field. Examples ======== >>> from sympy.polys.domains import FF >>> from ec import EllipticCurve >>> e2 = EllipticCurve.minimal(1, 0, domain=FF(2)) >>> list(e2.points()) [(0, 0), (1, 0)] """ char = self.characteristic if char > 1: for i in range(char): y = sqrt_mod(i**3 + self._a2 * i**2 + self._a4 * i + self._a6, char) if y is not None: yield self(i, y) if y != 0: yield self(i, char - y) else: raise NotImplementedError("Still not implemented") def to_minimal(self): """ Return minimal Weierstrass equation. Examples ======== >>> from ec import EllipticCurve >>> e1 = EllipticCurve(0, -1, 1, -10, -20) >>> e1.to_minimal() E(QQ): y**2 == x**3 - 13392*x - 1080432 """ char = self.characteristic if char == 2: return self if char == 3: return EllipticCurve(0, self._b2 / 4, 0, self._b4 / 2, self._b6 / 4, self._dom) c4 = self._b2**2 - 24 * self._b4 c6 = -self._b2**3 + 36 * self._b2 * self._b4 - 216 * self._b6 return EllipticCurve.minimal(-27 * c4, -54 * c6, self._dom) def torsion_points(self): """ Return torsion points of curve over Rational number. Return point objects those are finite order. According to Nagell-Lutz theorem, torsion point p(x, y) x and y are integers, either y = 0 or y**2 is divisor of discriminent. According to Mazur's theorem, there are at most 15 points in torsion collection. Examples ======== >>> from ec import EllipticCurve >>> e2 = EllipticCurve.minimal(-43, 166) >>> [i for i in e2.torsion_points()] [O, (3, 8), (3, -8), (-5, 16), (-5, -16), (11, 32), (11, -32)] """ if self.characteristic > 0: raise ValueError("No torsion point for Finite Field.") yield InfinityPoint(self) for x in solve(self._eq.subs(y, 0)): if x.is_rational: yield self(x, 0) for i in divisors(self.discriminant, generator=True): j = int(i**.5) if j**2 == i: for x in solve(self._eq.subs(y, j)): p = self(x, j) if x.is_rational and p.order() != oo: yield p yield -p @property def characteristic(self): """ Return domain characteristic. Examples ======== >>> from ec import EllipticCurve >>> e2 = EllipticCurve.minimal(-43, 166) >>> e2.characteristic 0 """ return self._dom.characteristic() @property def discriminant(self): """ Return curve discriminant. Examples ======== >>> from ec import EllipticCurve >>> e2 = EllipticCurve.minimal(0, 17) >>> e2.discriminant -124848 """ return int(self._discrim) @property def is_singular(self): """ Return True if curve discriminant is equal to zero. """ return self.discriminant == 0 @property def j_invariant(self): """ Return curve j-invariant. Examples ======== >>> from ec import EllipticCurve >>> e1 = EllipticCurve(0, 1, 1, -2, 0) >>> e1.j_invariant 1404928/389 """ c4 = self._b2**2 - 24 * self._b4 return self._dom.to_sympy(c4**3 / self._discrim) @property def order(self): """ Number of points in Finite field. Examples ======== >>> from sympy.polys.domains import FF >>> from ec import EllipticCurve >>> e2 = EllipticCurve.minimal(1, 0, domain=FF(19)) >>> e2.order 19 """ if self.characteristic == 0: raise NotImplementedError("Still not implemented") return len(list(self.points())) @property def rank(self): """ Number of independent points of infinite order. For Finite field, it must be 0. """ if self._rank is not None: return self._rank raise NotImplementedError("Still not implemented")
def test_equality_subs2(): f = Function('f') eq = Eq(f(x)**2, 16) assert bool(eq.subs(f(x), 3)) is False assert bool(eq.subs(f(x), 4)) is True
class EllipticCurve: """ Create the following Elliptic Curve over domain. `y^{2} + a_{1} x y + a_{3} y = x^{3} + a_{2} x^{2} + a_{4} x + a_{6}` The default domain is ``QQ``. If no coefficient ``a1``, ``a2``, ``a3``, it create curve as following form. `y^{2} = x^{3} + a_{4} x + a_{6}` Examples ======== References ========== .. [1] J. Silverman "A Friendly Introduction to Number Theory" Third Edition .. [2] http://mathworld.wolfram.com/EllipticDiscriminant.html .. [3] G. Hardy, E. Wright "An Introduction to the Theory of Numbers" Sixth Edition """ def __init__(self, a4, a6, a1=0, a2=0, a3=0, modulus=0): if modulus == 0: domain = QQ else: domain = FF(modulus) a1, a2, a3, a4, a6 = map(domain.convert, (a1, a2, a3, a4, a6)) self._domain = domain self.modulus = modulus # Calculate discriminant b2 = a1**2 + 4 * a2 b4 = 2 * a4 + a1 * a3 b6 = a3**2 + 4 * a6 b8 = a1**2 * a6 + 4 * a2 * a6 - a1 * a3 * a4 + a2 * a3**2 - a4**2 self._b2, self._b4, self._b6, self._b8 = b2, b4, b6, b8 self._discrim = -b2**2 * b8 - 8 * b4**3 - 27 * b6**2 + 9 * b2 * b4 * b6 self._a1 = a1 self._a2 = a2 self._a3 = a3 self._a4 = a4 self._a6 = a6 x, y, z = symbols('x y z') self.x, self.y, self.z = x, y, z self._eq = Eq(y**2 * z + a1 * x * y * z + a3 * y * z**2, x**3 + a2 * x**2 * z + a4 * x * z**2 + a6 * z**3) if isinstance(self._domain, FiniteField): self._rank = 0 elif isinstance(self._domain, RationalField): self._rank = None def __call__(self, x, y, z=1): return EllipticCurvePoint(x, y, z, self) def __contains__(self, point): if is_sequence(point): if len(point) == 2: z1 = 1 else: z1 = point[2] x1, y1 = point[:2] elif isinstance(point, EllipticCurvePoint): x1, y1, z1 = point.x, point.y, point.z else: raise ValueError('Invalid point.') if self.characteristic == 0 and z1 == 0: return True return self._eq.subs({self.x: x1, self.y: y1, self.z: z1}) def __repr__(self): return 'E({}): {}'.format(self._domain, self._eq) def minimal(self): """ Return minimal Weierstrass equation. Examples ======== >>> from sympy.ntheory.elliptic_curve import EllipticCurve >>> e1 = EllipticCurve(-10, -20, 0, -1, 1) >>> e1.minimal() E(QQ): Eq(y**2*z, x**3 - 13392*x*z**2 - 1080432*z**3) """ char = self.characteristic if char == 2: return self if char == 3: return EllipticCurve(self._b4 / 2, self._b6 / 4, a2=self._b2 / 4, modulus=self.modulus) c4 = self._b2**2 - 24 * self._b4 c6 = -self._b2**3 + 36 * self._b2 * self._b4 - 216 * self._b6 return EllipticCurve(-27 * c4, -54 * c6, modulus=self.modulus) def points(self): """ Return points of curve over Finite Field. Examples ======== >>> from sympy.ntheory.elliptic_curve import EllipticCurve >>> e2 = EllipticCurve(1, 1, 1, 1, 1, modulus=5) >>> e2.points() {(0, 2), (1, 4), (2, 0), (2, 2), (3, 0), (3, 1), (4, 0)} """ char = self.characteristic all_pt = set() if char >= 1: for i in range(char): congruence_eq = ((self._eq.lhs - self._eq.rhs).subs({ self.x: i, self.z: 1 })) sol = polynomial_congruence(congruence_eq, char) for num in sol: all_pt.add((i, num)) return all_pt else: raise ValueError("Infinitely many points") def points_x(self, x): "Returns points on with curve where xcoordinate = x" pt = [] if self._domain == QQ: for y in solve(self._eq.subs(self.x, x)): pt.append((x, y)) congruence_eq = ((self._eq.lhs - self._eq.rhs).subs({ self.x: x, self.z: 1 })) for y in polynomial_congruence(congruence_eq, self.characteristic): pt.append((x, y)) return pt def torsion_points(self): """ Return torsion points of curve over Rational number. Return point objects those are finite order. According to Nagell-Lutz theorem, torsion point p(x, y) x and y are integers, either y = 0 or y**2 is divisor of discriminent. According to Mazur's theorem, there are at most 15 points in torsion collection. Examples ======== >>> from sympy.ntheory.elliptic_curve import EllipticCurve >>> e2 = EllipticCurve(-43, 166) >>> sorted(e2.torsion_points()) [(-5, -16), (-5, 16), O, (3, -8), (3, 8), (11, -32), (11, 32)] """ if self.characteristic > 0: raise ValueError("No torsion point for Finite Field.") l = [EllipticCurvePoint.point_at_infinity(self)] for xx in solve(self._eq.subs({self.y: 0, self.z: 1})): if xx.is_rational: l.append(self(xx, 0)) for i in divisors(self.discriminant, generator=True): j = int(i**.5) if j**2 == i: for xx in solve(self._eq.subs({self.y: j, self.z: 1})): if not xx.is_rational: continue p = self(xx, j) if p.order() != oo: l.extend([p, -p]) return l @property def characteristic(self): """ Return domain characteristic. Examples ======== >>> from sympy.ntheory.elliptic_curve import EllipticCurve >>> e2 = EllipticCurve(-43, 166) >>> e2.characteristic 0 """ return self._domain.characteristic() @property def discriminant(self): """ Return curve discriminant. Examples ======== >>> from sympy.ntheory.elliptic_curve import EllipticCurve >>> e2 = EllipticCurve(0, 17) >>> e2.discriminant -124848 """ return int(self._discrim) @property def is_singular(self): """ Return True if curve discriminant is equal to zero. """ return self.discriminant == 0 @property def j_invariant(self): """ Return curve j-invariant. Examples ======== >>> from sympy.ntheory.elliptic_curve import EllipticCurve >>> e1 = EllipticCurve(-2, 0, 0, 1, 1) >>> e1.j_invariant 1404928/389 """ c4 = self._b2**2 - 24 * self._b4 return self._domain.to_sympy(c4**3 / self._discrim) @property def order(self): """ Number of points in Finite field. Examples ======== >>> from sympy.ntheory.elliptic_curve import EllipticCurve >>> e2 = EllipticCurve(1, 0, modulus=19) >>> e2.order 19 """ if self.characteristic == 0: raise NotImplementedError("Still not implemented") return len(list(self.points())) @property def rank(self): """ Number of independent points of infinite order. For Finite field, it must be 0. """ if self._rank is not None: return self._rank raise NotImplementedError("Still not implemented")
def test_equality_subs1(): f = Function('f') eq = Eq(f(x)**2, x) res = Eq(Integer(16), x) assert eq.subs(f(x), 4) == res