def add_variables_and_ranges(plot): """make sure all limits are in the form (symbol, a, b)""" # find where the limits begin and expressions end for i in range(len(plot)): if isinstance(plot[i], Tuple): break else: i = len(plot) + 1 exprs = list(plot[:i]) assert all(isinstance(e, Expr) for e in exprs) assert all(isinstance(t, Tuple) for t in plot[i:]) ranges = set([i for i in plot[i:] if isinstance(i, Tuple) and len(i) > 1]) range_variables = set([t[0] for t in ranges if len(t) == 3]) expr_free = set_union(*[e.free_symbols for e in exprs if isinstance(e, Expr)]) default_range = Tuple(-10, 10) # unambiguous cases for limits # no ranges if not ranges: plot = exprs + [Tuple(e) + default_range for e in expr_free or [Dummy()]] # all limits of length 3 elif all(len(i) == 3 for i in ranges): pass # all ranges the same elif len(ranges) == 1: range1 = ranges.pop() if len(range1) == 2: plot = exprs + [Tuple(x) + range1 for x in expr_free] # ranges cover free variables of expression elif expr_free < range_variables: plot = exprs + [i if len(i) == 3 else Tuple(Dummy()) + i for i in ranges] # ranges cover all but 1 free variable elif len(expr_free - range_variables) == 1: x = (expr_free - range_variables).pop() ranges = list(ranges) for i, ri in enumerate(ranges): if len(ri) == 2: ranges[i] = Tuple(x) + ri break else: ranges.append(Tuple(x) + default_range) for i, ri in enumerate(ranges): if len(ri) == 2: ranges[i] = Tuple(Dummy()) + ri plot = exprs + ranges # all implicit ranges elif all(len(i) == 2 for i in ranges): more = len(ranges) - len(expr_free) all_free = list(expr_free) + [Dummy() for i in range(more)] ranges = list(ranges) ranges.extend(-more*[default_range]) plot = exprs + [Tuple(all_free[i]) + ri for i, ri in enumerate(ranges)] else: raise ValueError('erroneous or unanticipated range input') return plot
def plot_implicit(expr, *args, **kwargs): """A plot function to plot implicit equations / inequalities. Arguments ========= - ``expr`` : The equation / inequality that is to be plotted. - ``(x, xmin, xmax)`` optional, 3-tuple denoting the range of symbol ``x`` - ``(y, ymin, ymax)`` optional, 3-tuple denoting the range of symbol ``y`` The following arguments can be passed as named parameters. - ``adaptive``. Boolean. The default value is set to True. It has to be set to False if you want to use a mesh grid. - ``depth`` integer. The depth of recursion for adaptive mesh grid. Default value is 0. Takes value in the range (0, 4). - ``points`` integer. The number of points if adaptive mesh grid is not used. Default value is 200. - ``title`` string .The title for the plot. - ``xlabel`` string. The label for the x - axis - ``ylabel`` string. The label for the y - axis plot_implicit, by default, uses interval arithmetic to plot functions. If the expression cannot be plotted using interval arithmetic, it defaults to a generating a contour using a mesh grid of fixed number of points. By setting adaptive to False, you can force plot_implicit to use the mesh grid. The mesh grid method can be effective when adaptive plotting using interval arithmetic, fails to plot with small line width. Examples: ========= Plot expressions: >>> from sympy import plot_implicit, cos, sin, symbols, Eq >>> x, y = symbols('x y') Without any ranges for the symbols in the expression >>> p1 = plot_implicit(Eq(x**2 + y**2, 5)) #doctest: +SKIP With the range for the symbols >>> p2 = plot_implicit(Eq(x**2 + y**2, 3), (x, -3, 3), (y, -3, 3)) #doctest: +SKIP With depth of recursion as argument. >>> p3 = plot_implicit(Eq(x**2 + y**2, 5), (x, -4, 4), (y, -4, 4), depth = 2) #doctest: +SKIP Using mesh grid and not using adaptive meshing. >>> p4 = plot_implicit(Eq(x**2 + y**2, 5), (x, -5, 5), (y, -2, 2), adaptive=False) #doctest: +SKIP Using mesh grid with number of points as input. >>> p5 = plot_implicit(Eq(x**2 + y**2, 5), (x, -5, 5), (y, -2, 2), adaptive=False, points=400) #doctest: +SKIP Plotting regions. >>> p6 = plot_implicit(y > x**2) #doctest: +SKIP Plotting Using boolean conjunctions. >>> p7 = plot_implicit(And(y > x, y > -x)) #doctest: +SKIP """ assert isinstance(expr, Expr) has_equality = False #Represents whether the expression contains an Equality, #GreaterThan or LessThan def arg_expand(bool_expr): """ Recursively expands the arguments of an Boolean Function """ for arg in bool_expr.args: if isinstance(arg, BooleanFunction): arg_expand(arg) elif isinstance(arg, Relational): arg_list.append(arg) arg_list = [] if isinstance(expr, BooleanFunction): arg_expand(expr) #Check whether there is an equality in the expression provided. if any(isinstance(e, (Equality, GreaterThan, LessThan)) for e in arg_list): has_equality = True elif not isinstance(expr, Relational): expr = Eq(expr, 0) has_equality = True elif isinstance(expr, (Equality, GreaterThan, LessThan)): has_equality = True free_symbols = set(expr.free_symbols) range_symbols = set([t[0] for t in args]) symbols = set_union(free_symbols, range_symbols) if len(symbols) > 2: raise NotImplementedError("Implicit plotting is not implemented for " "more than 2 variables") #Create default ranges if the range is not provided. default_range = Tuple(-5, 5) if len(args) == 2: var_start_end_x = args[0] var_start_end_y = args[1] elif len(args) == 1: if len(free_symbols) == 2: var_start_end_x = args[0] var_start_end_y, = (Tuple(e) + default_range for e in (free_symbols - range_symbols)) else: var_start_end_x, = (Tuple(e) + default_range for e in free_symbols) #Create a random symbol var_start_end_y = Tuple(Dummy()) + default_range elif len(args) == 0: if len(free_symbols) == 1: var_start_end_x, = (Tuple(e) + default_range for e in free_symbols) #create a random symbol var_start_end_y = Tuple(Dummy()) + default_range else: var_start_end_x, var_start_end_y = (Tuple(e) + default_range for e in free_symbols) use_interval = kwargs.pop('adaptive', True) nb_of_points = kwargs.pop('points', 300) depth = kwargs.pop('depth', 0) #Check whether the depth is greater than 4 or less than 0. if depth > 4: depth = 4 elif depth < 0: depth = 0 series_argument = ImplicitSeries(expr, var_start_end_x, var_start_end_y, has_equality, use_interval, depth, nb_of_points) show = kwargs.pop('show', True) #set the x and y limits kwargs['xlim'] = tuple(float(x) for x in var_start_end_x[1:]) kwargs['ylim'] = tuple(float(y) for y in var_start_end_y[1:]) p = Plot(series_argument, **kwargs) if show: p.show() return p
def _preprocess(expr, func=None, hint='_Integral'): """Prepare expr for solving by making sure that differentiation is done so that only func remains in unevaluated derivatives and (if hint doesn't end with _Integral) that doit is applied to all other derivatives. If hint is None, don't do any differentiation. (Currently this may cause some simple differential equations to fail.) In case func is None, an attempt will be made to autodetect the function to be solved for. >>> from sympy.solvers.deutils import _preprocess >>> from sympy import Derivative, Function, Integral, sin >>> from sympy.abc import x, y, z >>> f, g = map(Function, 'fg') Apply doit to derivatives that contain more than the function of interest: >>> _preprocess(Derivative(f(x) + x, x)) (Derivative(f(x), x) + 1, f(x)) Do others if the differentiation variable(s) intersect with those of the function of interest or contain the function of interest: >>> _preprocess(Derivative(g(x), y, z), f(y)) (0, f(y)) >>> _preprocess(Derivative(f(y), z), f(y)) (0, f(y)) Do others if the hint doesn't end in '_Integral' (the default assumes that it does): >>> _preprocess(Derivative(g(x), y), f(x)) (Derivative(g(x), y), f(x)) >>> _preprocess(Derivative(f(x), y), f(x), hint='') (0, f(x)) Don't do any derivatives if hint is None: >>> eq = Derivative(f(x) + 1, x) + Derivative(f(x), y) >>> _preprocess(eq, f(x), hint=None) (Derivative(f(x) + 1, x) + Derivative(f(x), y), f(x)) If it's not clear what the function of interest is, it must be given: >>> eq = Derivative(f(x) + g(x), x) >>> _preprocess(eq, g(x)) (Derivative(f(x), x) + Derivative(g(x), x), g(x)) >>> try: _preprocess(eq) ... except ValueError: print "A ValueError was raised." A ValueError was raised. """ derivs = expr.atoms(Derivative) if not func: funcs = set_union(*[d.atoms(AppliedUndef) for d in derivs]) if len(funcs) != 1: raise ValueError('The function cannot be ' 'automatically detected for %s.' % expr) func = funcs.pop() fvars = set(func.args) if hint is None: return expr, func reps = [(d, d.doit()) for d in derivs if not hint.endswith('_Integral') or d.has(func) or set(d.variables) & fvars] eq = expr.subs(reps) return eq, func
def _preprocess(expr, func=None, hint='_Integral'): """Prepare expr for solving by making sure that differentiation is done so that only func remains in unevaluated derivatives and (if hint doesn't end with _Integral) that doit is applied to all other derivatives. If hint is None, don't do any differentiation. (Currently this may cause some simple differential equations to fail.) In case func is None, an attempt will be made to autodetect the function to be solved for. >>> from sympy.solvers.deutils import _preprocess >>> from sympy import Derivative, Function, Integral, sin >>> from sympy.abc import x, y, z >>> f, g = map(Function, 'fg') Apply doit to derivatives that contain more than the function of interest: >>> _preprocess(Derivative(f(x) + x, x)) (Derivative(f(x), x) + 1, f(x)) Do others if the differentiation variable(s) intersect with those of the function of interest or contain the function of interest: >>> _preprocess(Derivative(g(x), y, z), f(y)) (0, f(y)) >>> _preprocess(Derivative(f(y), z), f(y)) (0, f(y)) Do others if the hint doesn't end in '_Integral' (the default assumes that it does): >>> _preprocess(Derivative(g(x), y), f(x)) (Derivative(g(x), y), f(x)) >>> _preprocess(Derivative(f(x), y), f(x), hint='') (0, f(x)) Don't do any derivatives if hint is None: >>> eq = Derivative(f(x) + 1, x) + Derivative(f(x), y) >>> _preprocess(eq, f(x), hint=None) (Derivative(f(x) + 1, x) + Derivative(f(x), y), f(x)) If it's not clear what the function of interest is, it must be given: >>> eq = Derivative(f(x) + g(x), x) >>> _preprocess(eq, g(x)) (Derivative(f(x), x) + Derivative(g(x), x), g(x)) >>> try: _preprocess(eq) ... except ValueError: print "A ValueError was raised." A ValueError was raised. """ derivs = expr.atoms(Derivative) if not func: funcs = set_union(*[d.atoms(AppliedUndef) for d in derivs]) if len(funcs) != 1: raise ValueError('The function cannot be ' 'automatically detected for %s.' % expr) func = funcs.pop() fvars = set(func.args) if hint is None: return expr, func reps = [ (d, d.doit()) for d in derivs if not hint.endswith('_Integral') or d.has(func) or set(d.variables) & fvars ] eq = expr.subs(reps) return eq, func