def sdp_design_matrix(self): r""" Return the incidence matrix of the SDP design of the bent function. This method returns the incidence matrix of the design of type :math:`R(\mathtt{self})`, as described by Dillon and Schatz [DS1987]_. This is a design with the symmetric difference property [Kan1975]_. INPUT: - ``self`` -- the current object. OUTPUT: The incidence matrix of the SDP design corresponding to ``self``. EXAMPLES: :: sage: from boolean_cayley_graphs.bent_function import BentFunction sage: bentf = BentFunction([0,0,0,1,0,0,0,1,0,0,0,1,1,1,1,0]) sage: sdp = bentf.sdp_design_matrix() sage: print(sdp) [0 0 0 1 0 0 0 1 0 0 0 1 1 1 1 0] [0 1 0 0 0 1 0 0 0 1 0 0 1 0 1 1] [0 0 1 0 0 0 1 0 0 0 1 0 1 1 0 1] [1 0 0 0 1 0 0 0 1 0 0 0 0 1 1 1] [0 0 0 1 1 1 1 0 0 0 0 1 0 0 0 1] [0 1 0 0 1 0 1 1 0 1 0 0 0 1 0 0] [0 0 1 0 1 1 0 1 0 0 1 0 0 0 1 0] [1 0 0 0 0 1 1 1 1 0 0 0 1 0 0 0] [0 0 0 1 0 0 0 1 1 1 1 0 0 0 0 1] [0 1 0 0 0 1 0 0 1 0 1 1 0 1 0 0] [0 0 1 0 0 0 1 0 1 1 0 1 0 0 1 0] [1 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0] [1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1] [1 0 1 1 0 1 0 0 0 1 0 0 0 1 0 0] [1 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0] [0 1 1 1 1 0 0 0 1 0 0 0 1 0 0 0] sage: from sage.combinat.designs.incidence_structures import IncidenceStructure sage: sdp_design = IncidenceStructure(sdp) sage: sdp_design.is_t_design(return_parameters=True) (True, (2, 16, 6, 2)) REFERENCES: .. Dillon and Schatz [DS1987]_, Kantor [Kan1975]_. """ dim = self.nvariables() v = 2**dim result = matrix(v, v) dual_self = self.walsh_hadamard_dual() dual_f = dual_self.extended_translate() for c in xsrange(v): result[c, :] = matrix([ self.extended_translate(0, c, dual_f(c))(x) for x in xsrange(v) ]) return result
def small_prime_value(self, Bmax=1000): r""" Returns a prime represented by this (primitive positive definite) binary form. INPUT: - ``Bmax`` -- a positive bound on the representing integers. OUTPUT: A prime number represented by the form. .. NOTE:: This is a very elementary implementation which just substitutes values until a prime is found. EXAMPLES:: sage: [Q.small_prime_value() for Q in BinaryQF_reduced_representatives(-23, primitive_only=True)] [23, 2, 2] sage: [Q.small_prime_value() for Q in BinaryQF_reduced_representatives(-47, primitive_only=True)] [47, 2, 2, 3, 3] """ from sage.sets.all import Set from sage.arith.srange import xsrange B = 10 while True: llist = list(Set([self(x,y) for x in xsrange(-B,B) for y in xsrange(B)])) llist = sorted([l for l in llist if l.is_prime()]) if llist: return llist[0] if B >= Bmax: raise ValueError("Unable to find a prime value of %s" % self) B += 10
def boolean_linear_code(dim, f): r""" Return the Boolean linear code corresponding to a Boolean function. INPUT: - ``dim`` -- positive integer. The assumed dimension of function ``f``. - ``f`` -- a Python function that takes a positive integer and returns 0 or 1. This is assumed to represent a Boolean function on :math:`\mathbb{F}_2^{dim}` via lexicographical ordering. OUTPUT: An object of class ``LinearCode``, representing the Boolean linear code corresponding to the Boolean function represented by ``f``. EXAMPLES: :: sage: from sage.crypto.boolean_function import BooleanFunction sage: bf = BooleanFunction([0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,1]) sage: dim = bf.nvariables() sage: from boolean_cayley_graphs.boolean_linear_code import boolean_linear_code sage: bc = boolean_linear_code(dim, bf) sage: bc.characteristic_polynomial() -2/3*x + 2 sage: bc.generator_matrix().echelon_form() [1 0 0 0 1] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 1 1] REFERENCES: .. Carlet [Car2010]_. .. Calderbank and Kantor [CalK1986]_. .. Ding [Din2015]_ Corollary 10. """ v = 2**dim support = [y for y in xsrange(v) if f(y) == 1] M = matrix(GF(2), [[inner(2**k, y) for y in support] for k in xsrange(dim)]) return LinearCode(M)
def least_quadratic_nonresidue(p): """ Returns the smallest positive integer quadratic non-residue in Z/pZ for primes p>2. EXAMPLES:: sage: least_quadratic_nonresidue(5) 2 sage: [least_quadratic_nonresidue(p) for p in prime_range(3,100)] [2, 2, 3, 2, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 2, 2, 2, 7, 5, 3, 2, 3, 5] TESTS: Raises an error if input is a positive composite integer. :: sage: least_quadratic_nonresidue(20) Traceback (most recent call last): ... ValueError: Oops! p must be a prime number > 2. Raises an error if input is 2. This is because every integer is a quadratic residue modulo 2. :: sage: least_quadratic_nonresidue(2) Traceback (most recent call last): ... ValueError: Oops! There are no quadratic non-residues in Z/2Z. """ p1 = abs(p) ## Deal with the prime p = 2 and |p| <= 1. if p1 == 2: raise ValueError("Oops! There are no quadratic non-residues in Z/2Z.") if p1 < 2: raise ValueError("Oops! p must be a prime number > 2.") ## Find the smallest non-residue mod p ## For 7/8 of primes the answer is 2, 3 or 5: if p % 8 in (3, 5): return ZZ(2) if p % 12 in (5, 7): return ZZ(3) if p % 5 in (2, 3): return ZZ(5) ## default case (first needed for p=71): if not p.is_prime(): raise ValueError("Oops! p must be a prime number > 2.") from sage.arith.srange import xsrange for r in xsrange(7, p): if legendre_symbol(r, p) == -1: return ZZ(r)
def least_quadratic_nonresidue(p): """ Returns the smallest positive integer quadratic non-residue in Z/pZ for primes p>2. EXAMPLES:: sage: least_quadratic_nonresidue(5) 2 sage: [least_quadratic_nonresidue(p) for p in prime_range(3,100)] [2, 2, 3, 2, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 2, 2, 2, 7, 5, 3, 2, 3, 5] TESTS: Raises an error if input is a positive composite integer. :: sage: least_quadratic_nonresidue(20) Traceback (most recent call last): ... ValueError: Oops! p must be a prime number > 2. Raises an error if input is 2. This is because every integer is a quadratic residue modulo 2. :: sage: least_quadratic_nonresidue(2) Traceback (most recent call last): ... ValueError: Oops! There are no quadratic non-residues in Z/2Z. """ p1 = abs(p) ## Deal with the prime p = 2 and |p| <= 1. if p1 == 2: raise ValueError("Oops! There are no quadratic non-residues in Z/2Z.") if p1 < 2: raise ValueError("Oops! p must be a prime number > 2.") ## Find the smallest non-residue mod p ## For 7/8 of primes the answer is 2, 3 or 5: if p%8 in (3,5): return ZZ(2) if p%12 in (5,7): return ZZ(3) if p%5 in (2,3): return ZZ(5) ## default case (first needed for p=71): if not p.is_prime(): raise ValueError("Oops! p must be a prime number > 2.") from sage.arith.srange import xsrange for r in xsrange(7,p): if legendre_symbol(r, p) == -1: return ZZ(r)
def _parametric_plot3d_curve(f, urange, plot_points, **kwds): r""" Return a parametric three-dimensional space curve. This function is used internally by the :func:`parametric_plot3d` command. There are two ways this function is invoked by :func:`parametric_plot3d`. - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max))``: `f_x, f_y, f_z` are three functions and `u_{\min}` and `u_{\max}` are real numbers - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` INPUT: - ``f`` - a 3-tuple of functions or expressions, or vector of size 3 - ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple (u, u_min, u_max) - ``plot_points`` - (default: "automatic", which is 75) initial number of sample points in each parameter; an integer. EXAMPLES: We demonstrate each of the two ways of calling this. See :func:`parametric_plot3d` for many more examples. We do the first one with a lambda function, which creates a callable Python function that sends `u` to `u/10`:: sage: parametric_plot3d((sin, cos, lambda u: u/10), (0,20)) # indirect doctest Graphics3d Object Now we do the same thing with symbolic expressions:: sage: u = var('u') sage: parametric_plot3d((sin(u), cos(u), u/10), (u,0,20)) Graphics3d Object """ from sage.plot.misc import setup_for_eval_on_grid g, ranges = setup_for_eval_on_grid(f, [urange], plot_points) f_x, f_y, f_z = g w = [(f_x(u), f_y(u), f_z(u)) for u in xsrange(*ranges[0], include_endpoint=True)] return line3d(w, **kwds)
def is_linear_equivalent(self, other, certificate=False): r""" Check if there is a linear equivalence between ``self`` and ``other``: :math:`\mathtt{other}(M x) = \mathtt{self}(x)`, where M is a GF(2) matrix. INPUT: - ``self`` -- the current object. - ``other`` -- another object of class BooleanFunctionImproved. - ``certificate`` -- bool (default False). If true, return a GF(2) matrix that defines the isomorphism. OUTPUT: If ``certificate`` is false, a bool value. If ``certificate`` is true, a tuple consisting of either (False, None) or (True, M), where M is a GF(2) matrix that defines the equivalence. EXAMPLES: :: sage: from boolean_cayley_graphs.boolean_function_improved import BooleanFunctionImproved sage: bf1 = BooleanFunctionImproved([0,1,0,0]) sage: bf2 = BooleanFunctionImproved([0,0,1,0]) sage: bf1.is_linear_equivalent(bf2) True sage: bf2.is_linear_equivalent(bf1, certificate=True) ( [0 1] True, [1 0] ) """ dim = self.nvariables() self_bg = BooleanGraph(self.cayley_graph()) other_bg = BooleanGraph(other.cayley_graph()) is_linear_isomorphic, M = self_bg.is_linear_isomorphic( other_bg, certificate=True) if not is_linear_isomorphic: return (False, None) if certificate else False self_et = self.extended_translate() v = 2**dim for ix in xsrange(v): x = vector(GF(2), base2(dim, ix)) if Integer(other(list(M * x))) != self_et(ix): return (False, None) if certificate else False return (True, M) if certificate else True
def f(self): r""" Returns the set between ``self.start`` and ``self.stop``. EXAMPLES:: sage: from sage.sets.set_from_iterator import DummyExampleForPicklingTest sage: d = DummyExampleForPicklingTest() sage: d.f() {10, 11, 12, 13, 14, ...} sage: d.start = 4 sage: d.stop = 200 sage: d.f() {4, 5, 6, 7, 8, ...} """ from sage.arith.srange import xsrange return xsrange(self.start, self.stop)
def solve_integer(self, n): r""" Solve `Q(x,y) = n` in integers `x` and `y` where `Q` is this quadratic form. INPUT: - ``Q`` (BinaryQF) -- a positive definite primitive integral binary quadratic form - ``n`` (int) -- a positive integer OUTPUT: A tuple (x,y) of integers satisfying `Q(x,y) = n` or ``None`` if no such `x` and `y` exist. EXAMPLES:: sage: Qs = BinaryQF_reduced_representatives(-23,primitive_only=True) sage: Qs [x^2 + x*y + 6*y^2, 2*x^2 - x*y + 3*y^2, 2*x^2 + x*y + 3*y^2] sage: [Q.solve_integer(3) for Q in Qs] [None, (0, 1), (0, 1)] sage: [Q.solve_integer(5) for Q in Qs] [None, None, None] sage: [Q.solve_integer(6) for Q in Qs] [(0, 1), (-1, 1), (1, 1)] """ a, b, c = self d = self.discriminant() if d >= 0 or a <= 0: raise ValueError("%s is not positive definite" % self) ad = -d an4 = 4*a*n a2 = 2*a from sage.arith.srange import xsrange for y in xsrange(0, 1+an4//ad): z2 = an4 + d*y**2 for z in z2.sqrt(extend=False, all=True): if a2.divides(z-b*y): x = (z-b*y)//a2 return (x,y) return None
def royle_x_graph(): r""" Return a strongly regular graph, as described by Royle [Roy2008]_. INPUT: None. OUTPUT: An object of class ``Graph``, representing Royle's X graph [Roy2008]_. EXAMPLES: :: sage: from boolean_cayley_graphs.royle_x_graph import royle_x_graph sage: g = royle_x_graph() sage: g.is_strongly_regular() True sage: g.is_strongly_regular(parameters=True) (64, 35, 18, 20) REFERENCES: Royle [Roy2008]_. """ n = 8 order = 64 vecs = [vector([1] * n)] for a in Combinations(xsrange(1, n), 4): vecs.append(vector([-1 if x in a else 1 for x in xsrange(n)])) for b in Combinations(xsrange(n), 2): vecs.append(vector([-1 if x in b else 1 for x in xsrange(n)])) return Graph([(i, j) for i in xsrange(order) for j in xsrange(i + 1, order) if vecs[i] * vecs[j] == 0])
def is_linear_isomorphic(self, other, certificate=False): r""" Check that the two BooleanGraphs ``self`` and ``other`` are isomorphic and that the isomorphism is given by a GF(2) linear mapping on the vector space of vertices. INPUT: - ``self`` -- the current object. - ``other`` -- another object of class BooleanFunctionImproved. - ``certificate`` -- bool (default False). If true, return a GF(2) matrix that defines the isomorphism. OUTPUT: If ``certificate`` is false, a bool value. If ``certificate`` is true, a tuple consisting of either (False, None) or (True, M), where M is a GF(2) matrix that defines the isomorphism. EXAMPLES: :: sage: from boolean_cayley_graphs.boolean_function_improved import BooleanFunctionImproved sage: from boolean_cayley_graphs.boolean_graph import BooleanGraph sage: bf1 = BooleanFunctionImproved([0,1,0,0]) sage: cg1 = BooleanGraph(bf1.cayley_graph()) sage: bf2 = BooleanFunctionImproved([0,0,1,0]) sage: cg2 = BooleanGraph(bf2.cayley_graph()) sage: cg1.is_linear_isomorphic(cg2) True sage: cg2.is_linear_isomorphic(cg1, certificate=True) ( [0 1] True, [1 0] ) """ # Check the isomorphism via canonical labels. # This is to work around the slow speed of is_isomorphic in some cases. if self.canonical_label() != other.canonical_label(): return (False, None) # Obtain the mapping that defines the isomorphism. is_isomorphic, mapping = self.is_isomorphic(other, certificate=True) # If self is not isomorphic to other, it is not linear isomorphic. if not is_isomorphic: return (False, None) if certificate else False # Check that the mapping is linear on each pair of basis vectors. dim = Integer(log(self.order(), 2)) for a in xsrange(dim): for b in xsrange(a + 1, dim): if mapping[2**a] ^ mapping[2**b] != mapping[(2**a) ^ (2**b)]: return (False, None) if certificate else False # The mapping is linear. # If the caller does not want a certificate, just return True. if not certificate: return True # Create the G(2) matrix corresponding to the mapping. mapping_matrix = matrix( GF(2), [base2(dim, Integer(mapping[2**a])) for a in xsrange(dim)]).transpose() return (True, mapping_matrix)
def plot_vector_field(f_g, xrange, yrange, **options): r""" ``plot_vector_field`` takes two functions of two variables xvar and yvar (for instance, if the variables are `x` and `y`, take `(f(x,y), g(x,y))`) and plots vector arrows of the function over the specified ranges, with xrange being of xvar between xmin and xmax, and yrange similarly (see below). ``plot_vector_field((f,g), (xvar,xmin,xmax), (yvar,ymin,ymax))`` EXAMPLES: Plot some vector fields involving sin and cos:: sage: x,y = var('x y') sage: plot_vector_field((sin(x),cos(y)), (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = plot_vector_field((sin(x),cos(y)), (x,-3,3), (y,-3,3)) sphinx_plot(g) :: sage: plot_vector_field((y,(cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = plot_vector_field((y,(cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) sphinx_plot(g) Plot a gradient field:: sage: u, v = var('u v') sage: f = exp(-(u^2 + v^2)) sage: plot_vector_field(f.gradient(), (u,-2,2), (v,-2,2), color='blue') Graphics object consisting of 1 graphics primitive .. PLOT:: u, v = var('u v') f = exp(-(u**2 + v**2)) g = plot_vector_field(f.gradient(), (u,-2,2), (v,-2,2), color='blue') sphinx_plot(g) Plot two orthogonal vector fields:: sage: x,y = var('x,y') sage: a = plot_vector_field((x,y), (x,-3,3), (y,-3,3), color='blue') sage: b = plot_vector_field((y,-x), (x,-3,3), (y,-3,3), color='red') sage: show(a + b) .. PLOT:: x,y = var('x,y') a = plot_vector_field((x,y), (x,-3,3), (y,-3,3), color='blue') b = plot_vector_field((y,-x), (x,-3,3), (y,-3,3), color='red') sphinx_plot(a + b) We ignore function values that are infinite or NaN:: sage: x,y = var('x,y') sage: plot_vector_field((-x/sqrt(x^2+y^2),-y/sqrt(x^2+y^2)), (x,-10,10), (y,-10,10)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = plot_vector_field((-x/sqrt(x**2+y**2),-y/sqrt(x**2+y**2)), (x,-10,10), (y,-10,10)) sphinx_plot(g) :: sage: x,y = var('x,y') sage: plot_vector_field((-x/sqrt(x+y),-y/sqrt(x+y)), (x,-10, 10), (y,-10,10)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = plot_vector_field((-x/sqrt(x+y),-y/sqrt(x+y)), (x,-10,10), (y,-10,10)) sphinx_plot(g) Extra options will get passed on to show(), as long as they are valid:: sage: plot_vector_field((x,y), (x,-2,2), (y,-2,2), xmax=10) Graphics object consisting of 1 graphics primitive sage: plot_vector_field((x,y), (x,-2,2), (y,-2,2)).show(xmax=10) # These are equivalent .. PLOT:: x,y = var('x,y') g = plot_vector_field((x,y), (x,-2,2), (y,-2,2), xmax=10) sphinx_plot(g) """ (f,g) = f_g from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid z, ranges = setup_for_eval_on_grid([f,g], [xrange,yrange], options['plot_points']) f, g = z xpos_array, ypos_array, xvec_array, yvec_array = [], [], [], [] for x in xsrange(*ranges[0], include_endpoint=True): for y in xsrange(*ranges[1], include_endpoint=True): xpos_array.append(x) ypos_array.append(y) xvec_array.append(f(x, y)) yvec_array.append(g(x, y)) import numpy xvec_array = numpy.ma.masked_invalid(numpy.array(xvec_array, dtype=float)) yvec_array = numpy.ma.masked_invalid(numpy.array(yvec_array, dtype=float)) g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) g.add_primitive(PlotField(xpos_array, ypos_array, xvec_array, yvec_array, options)) return g
def _render_on_subplot(self, subplot): """ TESTS: A somewhat random plot, but fun to look at:: sage: x,y = var('x,y') sage: contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv') Graphics object consisting of 1 graphics primitive """ from sage.rings.integer import Integer options = self.options() fill = options['fill'] contours = options['contours'] if 'cmap' in options: cmap = get_cmap(options['cmap']) elif fill or contours is None: cmap = get_cmap('gray') else: if isinstance(contours, (int, Integer)): cmap = get_cmap([(i, i, i) for i in xsrange(0, 1, 1 / contours)]) else: l = Integer(len(contours)) cmap = get_cmap([(i, i, i) for i in xsrange(0, 1, 1 / l)]) x0, x1 = float(self.xrange[0]), float(self.xrange[1]) y0, y1 = float(self.yrange[0]), float(self.yrange[1]) if isinstance(contours, (int, Integer)): contours = int(contours) CSF = None if fill: if contours is None: CSF = subplot.contourf(self.xy_data_array, cmap=cmap, extent=(x0, x1, y0, y1), label=options['legend_label']) else: CSF = subplot.contourf(self.xy_data_array, contours, cmap=cmap, extent=(x0, x1, y0, y1), extend='both', label=options['legend_label']) linewidths = options.get('linewidths', None) if isinstance(linewidths, (int, Integer)): linewidths = int(linewidths) elif isinstance(linewidths, (list, tuple)): linewidths = tuple(int(x) for x in linewidths) from sage.plot.misc import get_matplotlib_linestyle linestyles = options.get('linestyles', None) if isinstance(linestyles, (list, tuple)): linestyles = [ get_matplotlib_linestyle(l, 'long') for l in linestyles ] else: linestyles = get_matplotlib_linestyle(linestyles, 'long') if contours is None: CS = subplot.contour(self.xy_data_array, cmap=cmap, extent=(x0, x1, y0, y1), linewidths=linewidths, linestyles=linestyles, label=options['legend_label']) else: CS = subplot.contour(self.xy_data_array, contours, cmap=cmap, extent=(x0, x1, y0, y1), linewidths=linewidths, linestyles=linestyles, label=options['legend_label']) if options.get('labels', False): label_options = options['label_options'] label_options['fontsize'] = int(label_options['fontsize']) if fill and label_options is None: label_options['inline'] = False subplot.clabel(CS, **label_options) if options.get('colorbar', False): colorbar_options = options['colorbar_options'] from matplotlib import colorbar cax, kwds = colorbar.make_axes_gridspec(subplot, **colorbar_options) if CSF is None: cb = colorbar.Colorbar(cax, CS, **kwds) else: cb = colorbar.Colorbar(cax, CSF, **kwds) cb.add_lines(CS)
def BinaryQF_reduced_representatives(D, primitive_only=False): r""" Return representatives for the classes of binary quadratic forms of discriminant `D`. INPUT: - ``D`` -- (integer) a discriminant - ``primitive_only`` -- (boolean, default True): if True, only return primitive forms. OUTPUT: (list) A lexicographically-ordered list of inequivalent reduced representatives for the equivalence classes of binary quadratic forms of discriminant `D`. If ``primitive_only`` is ``True`` then imprimitive forms (which only exist when `D` is not fundamental) are omitted; otherwise they are included. EXAMPLES:: sage: BinaryQF_reduced_representatives(-4) [x^2 + y^2] sage: BinaryQF_reduced_representatives(-163) [x^2 + x*y + 41*y^2] sage: BinaryQF_reduced_representatives(-12) [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2] sage: BinaryQF_reduced_representatives(-16) [x^2 + 4*y^2, 2*x^2 + 2*y^2] sage: BinaryQF_reduced_representatives(-63) [x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2, 3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2] The number of inequivalent reduced binary forms with a fixed negative fundamental discriminant D is the class number of the quadratic field `\QQ(\sqrt{D})`:: sage: len(BinaryQF_reduced_representatives(-13*4)) 2 sage: QuadraticField(-13*4, 'a').class_number() 2 sage: p=next_prime(2^20); p 1048583 sage: len(BinaryQF_reduced_representatives(-p)) 689 sage: QuadraticField(-p, 'a').class_number() 689 sage: BinaryQF_reduced_representatives(-23*9) [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 3*x^2 + 3*x*y + 18*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 6*x^2 - 3*x*y + 9*y^2, 6*x^2 + 3*x*y + 9*y^2, 8*x^2 + 7*x*y + 8*y^2] sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True) [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 8*x^2 + 7*x*y + 8*y^2] TESTS:: sage: BinaryQF_reduced_representatives(73) [-6*x^2 + 5*x*y + 2*y^2, -6*x^2 + 7*x*y + y^2, -4*x^2 + 3*x*y + 4*y^2, -4*x^2 + 3*x*y + 4*y^2, -4*x^2 + 5*x*y + 3*y^2, -3*x^2 + 5*x*y + 4*y^2, -3*x^2 + 7*x*y + 2*y^2, -2*x^2 + 5*x*y + 6*y^2, -2*x^2 + 7*x*y + 3*y^2, -x^2 + 7*x*y + 6*y^2, x^2 + 7*x*y - 6*y^2, 2*x^2 + 5*x*y - 6*y^2, 2*x^2 + 7*x*y - 3*y^2, 3*x^2 + 5*x*y - 4*y^2, 3*x^2 + 7*x*y - 2*y^2, 4*x^2 + 3*x*y - 4*y^2, 4*x^2 + 3*x*y - 4*y^2, 4*x^2 + 5*x*y - 3*y^2, 6*x^2 + 5*x*y - 2*y^2, 6*x^2 + 7*x*y - y^2] sage: BinaryQF_reduced_representatives(76, primitive_only=True) [-5*x^2 + 4*x*y + 3*y^2, -5*x^2 + 6*x*y + 2*y^2, -3*x^2 + 4*x*y + 5*y^2, -3*x^2 + 8*x*y + y^2, -2*x^2 + 6*x*y + 5*y^2, -x^2 + 8*x*y + 3*y^2, x^2 + 8*x*y - 3*y^2, 2*x^2 + 6*x*y - 5*y^2, 3*x^2 + 4*x*y - 5*y^2, 3*x^2 + 8*x*y - y^2, 5*x^2 + 4*x*y - 3*y^2, 5*x^2 + 6*x*y - 2*y^2] Check that the primitive_only keyword does something:: sage: BinaryQF_reduced_representatives(4*5, primitive_only=True) [-x^2 + 4*x*y + y^2, -x^2 + 4*x*y + y^2, x^2 + 4*x*y - y^2, x^2 + 4*x*y - y^2] sage: BinaryQF_reduced_representatives(4*5, primitive_only=False) [-2*x^2 + 2*x*y + 2*y^2, -2*x^2 + 2*x*y + 2*y^2, -x^2 + 4*x*y + y^2, -x^2 + 4*x*y + y^2, x^2 + 4*x*y - y^2, x^2 + 4*x*y - y^2, 2*x^2 + 2*x*y - 2*y^2, 2*x^2 + 2*x*y - 2*y^2] """ D = ZZ(D) # For a fundamental discriminant all forms are primitive so we need not check: if primitive_only: primitive_only = not is_fundamental_discriminant(D) form_list = [] from sage.arith.srange import xsrange D4 = D % 4 if D4 == 2 or D4 == 3: raise ValueError("%s is not a discriminant" % D) if D > 0: # Indefinite # We follow the description of Buchmann/Vollmer 6.7.1 if D.is_square(): # Buchmann/Vollmer 6.7.1. require D squarefree. raise ValueError("%s is a square" % D) sqrt_d = D.sqrt(prec=53) for b in xsrange(1, sqrt_d.floor() + 1): if (D - b) % 2 != 0: continue A = (D - b**2) / 4 Low_a = ((sqrt_d - b) / 2).ceil() High_a = (A.sqrt(prec=53)).floor() for a in xsrange(Low_a, High_a + 1): if a == 0: continue c = -A / a if c in ZZ: if (not primitive_only) or gcd([a, b, c]) == 1: Q = BinaryQF(a, b, c) Q1 = BinaryQF(-a, b, -c) Q2 = BinaryQF(c, b, a) Q3 = BinaryQF(-c, b, -a) form_list.append(Q) form_list.append(Q1) form_list.append(Q2) form_list.append(Q3) else: # Definite # Only iterate over positive a and over b of the same # parity as D such that 4a^2 + D <= b^2 <= a^2 for a in xsrange(1, 1 + ((-D) // 3).isqrt()): a4 = 4 * a s = D + a * a4 w = 1 + (s - 1).isqrt() if s > 0 else 0 if w % 2 != D % 2: w += 1 for b in xsrange(w, a + 1, 2): t = b * b - D if t % a4 == 0: c = t // a4 if (not primitive_only) or gcd([a, b, c]) == 1: if b > 0 and a > b and c > a: form_list.append(BinaryQF([a, -b, c])) form_list.append(BinaryQF([a, b, c])) form_list.sort() return form_list
def plot_vector_field(f_g, xrange, yrange, **options): r""" ``plot_vector_field`` takes two functions of two variables xvar and yvar (for instance, if the variables are `x` and `y`, take `(f(x,y), g(x,y))`) and plots vector arrows of the function over the specified ranges, with xrange being of xvar between xmin and xmax, and yrange similarly (see below). ``plot_vector_field((f,g), (xvar,xmin,xmax), (yvar,ymin,ymax))`` EXAMPLES: Plot some vector fields involving sin and cos:: sage: x,y = var('x y') sage: plot_vector_field((sin(x),cos(y)), (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = plot_vector_field((sin(x),cos(y)), (x,-3,3), (y,-3,3)) sphinx_plot(g) :: sage: plot_vector_field((y,(cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = plot_vector_field((y,(cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) sphinx_plot(g) Plot a gradient field:: sage: u, v = var('u v') sage: f = exp(-(u^2 + v^2)) sage: plot_vector_field(f.gradient(), (u,-2,2), (v,-2,2), color='blue') Graphics object consisting of 1 graphics primitive .. PLOT:: u, v = var('u v') f = exp(-(u**2 + v**2)) g = plot_vector_field(f.gradient(), (u,-2,2), (v,-2,2), color='blue') sphinx_plot(g) Plot two orthogonal vector fields:: sage: x,y = var('x,y') sage: a = plot_vector_field((x,y), (x,-3,3), (y,-3,3), color='blue') sage: b = plot_vector_field((y,-x), (x,-3,3), (y,-3,3), color='red') sage: show(a + b) .. PLOT:: x,y = var('x,y') a = plot_vector_field((x,y), (x,-3,3), (y,-3,3), color='blue') b = plot_vector_field((y,-x), (x,-3,3), (y,-3,3), color='red') sphinx_plot(a + b) We ignore function values that are infinite or NaN:: sage: x,y = var('x,y') sage: plot_vector_field((-x/sqrt(x^2+y^2),-y/sqrt(x^2+y^2)), (x,-10,10), (y,-10,10)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = plot_vector_field((-x/sqrt(x**2+y**2),-y/sqrt(x**2+y**2)), (x,-10,10), (y,-10,10)) sphinx_plot(g) :: sage: x,y = var('x,y') sage: plot_vector_field((-x/sqrt(x+y),-y/sqrt(x+y)), (x,-10, 10), (y,-10,10)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = plot_vector_field((-x/sqrt(x+y),-y/sqrt(x+y)), (x,-10,10), (y,-10,10)) sphinx_plot(g) Extra options will get passed on to show(), as long as they are valid:: sage: plot_vector_field((x,y), (x,-2,2), (y,-2,2), xmax=10) Graphics object consisting of 1 graphics primitive sage: plot_vector_field((x,y), (x,-2,2), (y,-2,2)).show(xmax=10) # These are equivalent .. PLOT:: x,y = var('x,y') g = plot_vector_field((x,y), (x,-2,2), (y,-2,2), xmax=10) sphinx_plot(g) """ (f, g) = f_g from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid z, ranges = setup_for_eval_on_grid([f, g], [xrange, yrange], options['plot_points']) f, g = z xpos_array, ypos_array, xvec_array, yvec_array = [], [], [], [] for x in xsrange(*ranges[0], include_endpoint=True): for y in xsrange(*ranges[1], include_endpoint=True): xpos_array.append(x) ypos_array.append(y) xvec_array.append(f(x, y)) yvec_array.append(g(x, y)) import numpy xvec_array = numpy.ma.masked_invalid(numpy.array(xvec_array, dtype=float)) yvec_array = numpy.ma.masked_invalid(numpy.array(yvec_array, dtype=float)) g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) g.add_primitive( PlotField(xpos_array, ypos_array, xvec_array, yvec_array, options)) return g
def contour_plot(f, xrange, yrange, **options): r""" ``contour_plot`` takes a function of two variables, `f(x,y)` and plots contour lines of the function over the specified ``xrange`` and ``yrange`` as demonstrated below. ``contour_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid. For old computers, 25 is fine, but should not be used to verify specific intersection points. - ``fill`` -- bool (default: ``True``), whether to color in the area between contour lines - ``cmap`` -- a colormap (default: ``'gray'``), the name of a predefined colormap, a list of colors or an instance of a matplotlib Colormap. Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()`` for available colormap names. - ``contours`` -- integer or list of numbers (default: ``None``): If a list of numbers is given, then this specifies the contour levels to use. If an integer is given, then this many contour lines are used, but the exact levels are determined automatically. If ``None`` is passed (or the option is not given), then the number of contour lines is determined automatically, and is usually about 5. - ``linewidths`` -- integer or list of integer (default: None), if a single integer all levels will be of the width given, otherwise the levels will be plotted with the width in the order given. If the list is shorter than the number of contours, then the widths will be repeated cyclically. - ``linestyles`` -- string or list of strings (default: None), the style of the lines to be plotted, one of: ``"solid"``, ``"dashed"``, ``"dashdot"``, ``"dotted"``, respectively ``"-"``, ``"--"``, ``"-."``, ``":"``. If the list is shorter than the number of contours, then the styles will be repeated cyclically. - ``labels`` -- boolean (default: False) Show level labels or not. The following options are to adjust the style and placement of labels, they have no effect if no labels are shown. - ``label_fontsize`` -- integer (default: 9), the font size of the labels. - ``label_colors`` -- string or sequence of colors (default: None) If a string, gives the name of a single color with which to draw all labels. If a sequence, gives the colors of the labels. A color is a string giving the name of one or a 3-tuple of floats. - ``label_inline`` -- boolean (default: False if fill is True, otherwise True), controls whether the underlying contour is removed or not. - ``label_inline_spacing`` -- integer (default: 3), When inline, this is the amount of contour that is removed from each side, in pixels. - ``label_fmt`` -- a format string (default: "%1.2f"), this is used to get the label text from the level. This can also be a dictionary with the contour levels as keys and corresponding text string labels as values. It can also be any callable which returns a string when called with a numeric contour level. - ``colorbar`` -- boolean (default: False) Show a colorbar or not. The following options are to adjust the style and placement of colorbars. They have no effect if a colorbar is not shown. - ``colorbar_orientation`` -- string (default: 'vertical'), controls placement of the colorbar, can be either 'vertical' or 'horizontal' - ``colorbar_format`` -- a format string, this is used to format the colorbar labels. - ``colorbar_spacing`` -- string (default: 'proportional'). If 'proportional', make the contour divisions proportional to values. If 'uniform', space the colorbar divisions uniformly, without regard for numeric values. - ``legend_label`` -- the label for this item in the legend - ``region`` - (default: None) If region is given, it must be a function of two variables. Only segments of the surface where region(x,y) returns a number >0 will be included in the plot. EXAMPLES: Here we plot a simple function of two variables. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: x,y = var('x,y') sage: contour_plot(cos(x^2+y^2), (x, -4, 4), (y, -4, 4)) Graphics object consisting of 1 graphics primitive Here we change the ranges and add some options:: sage: x,y = var('x,y') sage: contour_plot((x^2)*cos(x*y), (x, -10, 5), (y, -5, 5), fill=False, plot_points=150) Graphics object consisting of 1 graphics primitive An even more complicated plot:: sage: x,y = var('x,y') sage: contour_plot(sin(x^2 + y^2)*cos(x)*sin(y), (x, -4, 4), (y, -4, 4),plot_points=150) Graphics object consisting of 1 graphics primitive Some elliptic curves, but with symbolic endpoints. In the first example, the plot is rotated 90 degrees because we switch the variables `x`, `y`:: sage: x,y = var('x,y') sage: contour_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi)) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive We can play with the contour levels:: sage: x,y = var('x,y') sage: f(x,y) = x^2 + y^2 sage: contour_plot(f, (-2, 2), (-2, 2)) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=2, cmap=[(1,0,0), (0,1,0), (0,0,1)]) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(0.1, 1.0, 1.2, 1.4), cmap='hsv') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(1.0,), fill=False) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,0,1]) Graphics object consisting of 1 graphics primitive We can change the style of the lines:: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linewidths=10) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linestyles='dashdot') Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed'],fill=False) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed']) sage: P Graphics object consisting of 1 graphics primitive sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['-',':']) sage: P Graphics object consisting of 1 graphics primitive We can add labels and play with them:: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv', labels=True) Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',\ ... labels=True, label_fmt="%1.0f", label_colors='black') sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt={-4:"low", 0:"medium", 4: "hi"}, label_colors='black') sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt=lambda x: "$z=%s$"%x, label_colors='black', label_inline=True, \ ... label_fontsize=12) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_fontsize=18) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline_spacing=1) sage: P Graphics object consisting of 1 graphics primitive :: sage: P= contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline=False) sage: P Graphics object consisting of 1 graphics primitive We can change the color of the labels if so desired:: sage: contour_plot(f, (-2,2), (-2,2), labels=True, label_colors='red') Graphics object consisting of 1 graphics primitive We can add a colorbar as well:: sage: f(x,y)=x^2-y^2 sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True,colorbar_orientation='horizontal') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True,colorbar_spacing='uniform') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[0,2,3,6],colorbar=True,colorbar_format='%.3f') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), labels=True,label_colors='red',contours=[0,2,3,6],colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), cmap='winter', contours=20, fill=False, colorbar=True) Graphics object consisting of 1 graphics primitive This should plot concentric circles centered at the origin:: sage: x,y = var('x,y') sage: contour_plot(x^2+y^2-2,(x,-1,1), (y,-1,1)) Graphics object consisting of 1 graphics primitive Extra options will get passed on to show(), as long as they are valid:: sage: f(x, y) = cos(x) + sin(y) sage: contour_plot(f, (0, pi), (0, pi), axes=True) Graphics object consisting of 1 graphics primitive One can also plot over a reduced region:: sage: contour_plot(x**2-y**2, (x,-2, 2), (y,-2, 2),region=x-y,plot_points=300) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (0, pi), (0, pi)).show(axes=True) # These are equivalent Note that with ``fill=False`` and grayscale contours, there is the possibility of confusion between the contours and the axes, so use ``fill=False`` together with ``axes=True`` with caution:: sage: contour_plot(f, (-pi, pi), (-pi, pi), fill=False, axes=True) Graphics object consisting of 1 graphics primitive TESTS: To check that :trac:`5221` is fixed, note that this has three curves, not two:: sage: x,y = var('x,y') sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,-2,0], fill=False) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid region = options.pop('region') ev = [f] if region is None else [f, region] F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], options['plot_points']) g = F[0] xrange, yrange = [r[:2] for r in ranges] xy_data_array = [[ g(x, y) for x in xsrange(*ranges[0], include_endpoint=True) ] for y in xsrange(*ranges[1], include_endpoint=True)] if region is not None: import numpy xy_data_array = numpy.ma.asarray(xy_data_array, dtype=float) m = F[1] mask = numpy.asarray([[ m(x, y) <= 0 for x in xsrange(*ranges[0], include_endpoint=True) ] for y in xsrange(*ranges[1], include_endpoint=True)], dtype=bool) xy_data_array[mask] = numpy.ma.masked g = Graphics() # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. # Otherwise matplotlib complains. scale = options.get('scale', None) if isinstance(scale, (list, tuple)): scale = scale[0] if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' g._set_extra_kwds( Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options)) return g
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth, alpha, **options): r""" ``region_plot`` takes a boolean function of two variables, `f(x,y)` and plots the region where f is True over the specified ``xrange`` and ``yrange`` as demonstrated below. ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a boolean function or a list of boolean functions of two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid - ``incol`` -- a color (default: ``'blue'``), the color inside the region - ``outcol`` -- a color (default: ``None``), the color of the outside of the region If any of these options are specified, the border will be shown as indicated, otherwise it is only implicit (with color ``incol``) as the border of the inside of the region. - ``bordercol`` -- a color (default: ``None``), the color of the border (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``) - ``borderstyle`` -- string (default: 'solid'), one of ``'solid'``, ``'dashed'``, ``'dotted'``, ``'dashdot'``, respectively ``'-'``, ``'--'``, ``':'``, ``'-.'``. - ``borderwidth`` -- integer (default: None), the width of the border in pixels - ``alpha`` -- (default: 1) How transparent the fill is. A number between 0 and 1. - ``legend_label`` -- the label for this item in the legend - ``base`` - (default: 10) the base of the logarithm if a logarithmic scale is set. This must be greater than 1. The base can be also given as a list or tuple ``(basex, basey)``. ``basex`` sets the base of the logarithm along the horizontal axis and ``basey`` sets the base along the vertical axis. - ``scale`` -- (default: ``"linear"``) string. The scale of the axes. Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``, ``"semilogy"``. The scale can be also be given as single argument that is a list or tuple ``(scale, base)`` or ``(scale, basex, basey)``. The ``"loglog"`` scale sets both the horizontal and vertical axes to logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis to logarithmic scale. The ``"linear"`` scale is the default value when :class:`~sage.plot.graphics.Graphics` is initialized. EXAMPLES: Here we plot a simple function of two variables:: sage: x,y = var('x,y') sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3)) Graphics object consisting of 1 graphics primitive Here we play with the colors:: sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray') Graphics object consisting of 2 graphics primitives An even more complicated plot, with dashed borders:: sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250) Graphics object consisting of 2 graphics primitives A disk centered at the origin:: sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1)) Graphics object consisting of 1 graphics primitive A plot with more than one condition (all conditions must be true for the statement to be true):: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2)) Graphics object consisting of 1 graphics primitive Since it doesn't look very good, let's increase ``plot_points``:: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive To get plots where only one condition needs to be true, use a function. Using lambda functions, we definitely need the extra ``plot_points``:: sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive The first quadrant of the unit circle:: sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400) Graphics object consisting of 1 graphics primitive Here is another plot, with a huge border:: sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50) Graphics object consisting of 2 graphics primitives If we want to keep only the region where x is positive:: sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50) Graphics object consisting of 1 graphics primitive Here we have a cut circle:: sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200) Graphics object consisting of 2 graphics primitives The first variable range corresponds to the horizontal axis and the second variable range corresponds to the vertical axis:: sage: s,t=var('s,t') sage: region_plot(s>0,(t,-2,2),(s,-2,2)) Graphics object consisting of 1 graphics primitive :: sage: region_plot(s>0,(s,-2,2),(t,-2,2)) Graphics object consisting of 1 graphics primitive An example of a region plot in 'loglog' scale:: sage: region_plot(x^2+y^2<100, (x,1,10), (y,1,10), scale='loglog') Graphics object consisting of 1 graphics primitive TESTS: To check that :trac:`16907` is fixed:: sage: x, y = var('x, y') sage: disc1 = region_plot(x^2+y^2 < 1, (x, -1, 1), (y, -1, 1), alpha=0.5) sage: disc2 = region_plot((x-0.7)^2+(y-0.7)^2 < 0.5, (x, -2, 2), (y, -2, 2), incol='red', alpha=0.5) sage: disc1 + disc2 Graphics object consisting of 2 graphics primitives To check that :trac:`18286` is fixed:: sage: x, y = var('x, y') sage: region_plot([x == 0], (x, -1, 1), (y, -1, 1)) Graphics object consisting of 1 graphics primitive sage: region_plot([x^2+y^2==1, x<y], (x, -1, 1), (y, -1, 1)) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid from sage.symbolic.expression import is_Expression from warnings import warn import numpy if not isinstance(f, (list, tuple)): f = [f] feqs = [equify(g) for g in f if is_Expression(g) and g.operator() is operator.eq and not equify(g).is_zero()] f = [equify(g) for g in f if not (is_Expression(g) and g.operator() is operator.eq)] neqs = len(feqs) if neqs > 1: warn("There are at least 2 equations; If the region is degenerated to points, plotting might show nothing.") feqs = [sum([fn**2 for fn in feqs])] neqs = 1 if neqs and not bordercol: bordercol = incol if not f: return implicit_plot(feqs[0], xrange, yrange, plot_points=plot_points, fill=False, \ linewidth=borderwidth, linestyle=borderstyle, color=bordercol, **options) f_all, ranges = setup_for_eval_on_grid(feqs + f, [xrange, yrange], plot_points) xrange,yrange=[r[:2] for r in ranges] xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] for func in f_all[neqs::]],dtype=float) xy_data_array=numpy.abs(xy_data_arrays.prod(axis=0)) # Now we need to set entries to negative iff all # functions were negative at that point. neg_indices = (xy_data_arrays<0).all(axis=0) xy_data_array[neg_indices]=-xy_data_array[neg_indices] from matplotlib.colors import ListedColormap incol = rgbcolor(incol) if outcol: outcol = rgbcolor(outcol) cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol, alpha=alpha) else: outcol = rgbcolor('white') cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol, alpha=0) cmap.set_under(incol, alpha=alpha) g = Graphics() # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. # Otherwise matplotlib complains. scale = options.get('scale', None) if isinstance(scale, (list, tuple)): scale = scale[0] if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) if neqs == 0: g.add_primitive(ContourPlot(xy_data_array, xrange,yrange, dict(contours=[-1e-20, 0, 1e-20], cmap=cmap, fill=True, **options))) else: mask = numpy.asarray([[elt > 0 for elt in rows] for rows in xy_data_array], dtype=bool) xy_data_array = numpy.asarray([[f_all[0](x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)], dtype=float) xy_data_array[mask] = None if bordercol or borderstyle or borderwidth: cmap = [rgbcolor(bordercol)] if bordercol else ['black'] linestyles = [borderstyle] if borderstyle else None linewidths = [borderwidth] if borderwidth else None g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, dict(linestyles=linestyles, linewidths=linewidths, contours=[0], cmap=[bordercol], fill=False, **options))) return g
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth, alpha, **options): r""" ``region_plot`` takes a boolean function of two variables, `f(x,y)` and plots the region where f is True over the specified ``xrange`` and ``yrange`` as demonstrated below. ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a boolean function or a list of boolean functions of two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid - ``incol`` -- a color (default: ``'blue'``), the color inside the region - ``outcol`` -- a color (default: ``None``), the color of the outside of the region If any of these options are specified, the border will be shown as indicated, otherwise it is only implicit (with color ``incol``) as the border of the inside of the region. - ``bordercol`` -- a color (default: ``None``), the color of the border (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``) - ``borderstyle`` -- string (default: 'solid'), one of ``'solid'``, ``'dashed'``, ``'dotted'``, ``'dashdot'``, respectively ``'-'``, ``'--'``, ``':'``, ``'-.'``. - ``borderwidth`` -- integer (default: None), the width of the border in pixels - ``alpha`` -- (default: 1) How transparent the fill is. A number between 0 and 1. - ``legend_label`` -- the label for this item in the legend - ``base`` - (default: 10) the base of the logarithm if a logarithmic scale is set. This must be greater than 1. The base can be also given as a list or tuple ``(basex, basey)``. ``basex`` sets the base of the logarithm along the horizontal axis and ``basey`` sets the base along the vertical axis. - ``scale`` -- (default: ``"linear"``) string. The scale of the axes. Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``, ``"semilogy"``. The scale can be also be given as single argument that is a list or tuple ``(scale, base)`` or ``(scale, basex, basey)``. The ``"loglog"`` scale sets both the horizontal and vertical axes to logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis to logarithmic scale. The ``"linear"`` scale is the default value when :class:`~sage.plot.graphics.Graphics` is initialized. EXAMPLES: Here we plot a simple function of two variables:: sage: x,y = var('x,y') sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3)) Graphics object consisting of 1 graphics primitive Here we play with the colors:: sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray') Graphics object consisting of 2 graphics primitives An even more complicated plot, with dashed borders:: sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250) Graphics object consisting of 2 graphics primitives A disk centered at the origin:: sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1)) Graphics object consisting of 1 graphics primitive A plot with more than one condition (all conditions must be true for the statement to be true):: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2)) Graphics object consisting of 1 graphics primitive Since it doesn't look very good, let's increase ``plot_points``:: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive To get plots where only one condition needs to be true, use a function. Using lambda functions, we definitely need the extra ``plot_points``:: sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive The first quadrant of the unit circle:: sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400) Graphics object consisting of 1 graphics primitive Here is another plot, with a huge border:: sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50) Graphics object consisting of 2 graphics primitives If we want to keep only the region where x is positive:: sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50) Graphics object consisting of 1 graphics primitive Here we have a cut circle:: sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200) Graphics object consisting of 2 graphics primitives The first variable range corresponds to the horizontal axis and the second variable range corresponds to the vertical axis:: sage: s,t=var('s,t') sage: region_plot(s>0,(t,-2,2),(s,-2,2)) Graphics object consisting of 1 graphics primitive :: sage: region_plot(s>0,(s,-2,2),(t,-2,2)) Graphics object consisting of 1 graphics primitive An example of a region plot in 'loglog' scale:: sage: region_plot(x^2+y^2<100, (x,1,10), (y,1,10), scale='loglog') Graphics object consisting of 1 graphics primitive TESTS: To check that :trac:`16907` is fixed:: sage: x, y = var('x, y') sage: disc1 = region_plot(x^2+y^2 < 1, (x, -1, 1), (y, -1, 1), alpha=0.5) sage: disc2 = region_plot((x-0.7)^2+(y-0.7)^2 < 0.5, (x, -2, 2), (y, -2, 2), incol='red', alpha=0.5) sage: disc1 + disc2 Graphics object consisting of 2 graphics primitives To check that :trac:`18286` is fixed:: sage: x, y = var('x, y') sage: region_plot([x == 0], (x, -1, 1), (y, -1, 1)) Graphics object consisting of 1 graphics primitive sage: region_plot([x^2+y^2==1, x<y], (x, -1, 1), (y, -1, 1)) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid from sage.symbolic.expression import is_Expression from warnings import warn import numpy if not isinstance(f, (list, tuple)): f = [f] feqs = [ equify(g) for g in f if is_Expression(g) and g.operator() is operator.eq and not equify(g).is_zero() ] f = [ equify(g) for g in f if not (is_Expression(g) and g.operator() is operator.eq) ] neqs = len(feqs) if neqs > 1: warn( "There are at least 2 equations; If the region is degenerated to points, plotting might show nothing." ) feqs = [sum([fn**2 for fn in feqs])] neqs = 1 if neqs and not bordercol: bordercol = incol if not f: return implicit_plot(feqs[0], xrange, yrange, plot_points=plot_points, fill=False, \ linewidth=borderwidth, linestyle=borderstyle, color=bordercol, **options) f_all, ranges = setup_for_eval_on_grid(feqs + f, [xrange, yrange], plot_points) xrange, yrange = [r[:2] for r in ranges] xy_data_arrays = numpy.asarray( [[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] for func in f_all[neqs::]], dtype=float) xy_data_array = numpy.abs(xy_data_arrays.prod(axis=0)) # Now we need to set entries to negative iff all # functions were negative at that point. neg_indices = (xy_data_arrays < 0).all(axis=0) xy_data_array[neg_indices] = -xy_data_array[neg_indices] from matplotlib.colors import ListedColormap incol = rgbcolor(incol) if outcol: outcol = rgbcolor(outcol) cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol, alpha=alpha) else: outcol = rgbcolor('white') cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol, alpha=0) cmap.set_under(incol, alpha=alpha) g = Graphics() # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. # Otherwise matplotlib complains. scale = options.get('scale', None) if isinstance(scale, (list, tuple)): scale = scale[0] if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' g._set_extra_kwds( Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) if neqs == 0: g.add_primitive( ContourPlot( xy_data_array, xrange, yrange, dict(contours=[-1e-20, 0, 1e-20], cmap=cmap, fill=True, **options))) else: mask = numpy.asarray([[elt > 0 for elt in rows] for rows in xy_data_array], dtype=bool) xy_data_array = numpy.asarray([[ f_all[0](x, y) for x in xsrange(*ranges[0], include_endpoint=True) ] for y in xsrange(*ranges[1], include_endpoint=True)], dtype=float) xy_data_array[mask] = None if bordercol or borderstyle or borderwidth: cmap = [rgbcolor(bordercol)] if bordercol else ['black'] linestyles = [borderstyle] if borderstyle else None linewidths = [borderwidth] if borderwidth else None g.add_primitive( ContourPlot( xy_data_array, xrange, yrange, dict(linestyles=linestyles, linewidths=linewidths, contours=[0], cmap=[bordercol], fill=False, **options))) return g
def BinaryQF_reduced_representatives(D, primitive_only=False): r""" Returns a list of inequivalent reduced representatives for the equivalence classes of positive definite binary forms of discriminant D. INPUT: - `D` -- (integer) A negative discriminant. - ``primitive_only`` -- (bool, default False) flag controlling whether only primitive forms are included. OUTPUT: (list) A lexicographically-ordered list of inequivalent reduced representatives for the equivalence classes of positive definite binary forms of discriminant `D`. If ``primitive_only`` is ``True`` then imprimitive forms (which only exist when `D` is not fundamental) are omitted; otherwise they are included. EXAMPLES:: sage: BinaryQF_reduced_representatives(-4) [x^2 + y^2] sage: BinaryQF_reduced_representatives(-163) [x^2 + x*y + 41*y^2] sage: BinaryQF_reduced_representatives(-12) [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2] sage: BinaryQF_reduced_representatives(-16) [x^2 + 4*y^2, 2*x^2 + 2*y^2] sage: BinaryQF_reduced_representatives(-63) [x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2, 3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2] The number of inequivalent reduced binary forms with a fixed negative fundamental discriminant D is the class number of the quadratic field `Q(\sqrt{D})`:: sage: len(BinaryQF_reduced_representatives(-13*4)) 2 sage: QuadraticField(-13*4, 'a').class_number() 2 sage: p=next_prime(2^20); p 1048583 sage: len(BinaryQF_reduced_representatives(-p)) 689 sage: QuadraticField(-p, 'a').class_number() 689 sage: BinaryQF_reduced_representatives(-23*9) [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 3*x^2 + 3*x*y + 18*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 6*x^2 - 3*x*y + 9*y^2, 6*x^2 + 3*x*y + 9*y^2, 8*x^2 + 7*x*y + 8*y^2] sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True) [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 8*x^2 + 7*x*y + 8*y^2] TESTS:: sage: BinaryQF_reduced_representatives(5) Traceback (most recent call last): ... ValueError: discriminant must be negative and congruent to 0 or 1 modulo 4 """ D = ZZ(D) if not ( D < 0 and (D % 4 in [0,1])): raise ValueError("discriminant must be negative and congruent to 0 or 1 modulo 4") # For a fundamental discriminant all forms are primitive so we need not check: if primitive_only: primitive_only = not is_fundamental_discriminant(D) form_list = [] from sage.arith.srange import xsrange # Only iterate over positive a and over b of the same # parity as D such that 4a^2 + D <= b^2 <= a^2 for a in xsrange(1,1+((-D)//3).isqrt()): a4 = 4*a s = D + a*a4 w = 1+(s-1).isqrt() if s > 0 else 0 if w%2 != D%2: w += 1 for b in xsrange(w,a+1,2): t = b*b-D if t % a4 == 0: c = t // a4 if (not primitive_only) or gcd([a,b,c])==1: if b>0 and a>b and c>a: form_list.append(BinaryQF([a,-b,c])) form_list.append(BinaryQF([a,b,c])) form_list.sort() return form_list
def streamline_plot(f_g, xrange, yrange, **options): r""" Return a streamline plot in a vector field. ``streamline_plot`` can take either one or two functions. Consider two variables `x` and `y`. If given two functions `(f(x,y), g(x,y))`, then this function plots streamlines in the vector field over the specified ranges with ``xrange`` being of `x`, denoted by ``xvar`` below, between ``xmin`` and ``xmax``, and ``yrange`` similarly (see below). :: streamline_plot((f, g), (xvar, xmin, xmax), (yvar, ymin, ymax)) Similarly, if given one function `f(x, y)`, then this function plots streamlines in the slope field `dy/dx = f(x,y)` over the specified ranges as given above. PLOT OPTIONS: - ``plot_points`` -- (default: 200) the minimal number of plot points - ``density`` -- float (default: 1.); controls the closeness of streamlines - ``start_points`` -- (optional) list of coordinates of starting points for the streamlines; coordinate pairs can be tuples or lists EXAMPLES: Plot some vector fields involving `\sin` and `\cos`:: sage: x, y = var('x y') sage: streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3)) sphinx_plot(g) :: sage: streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) sphinx_plot(g) We increase the density of the plot:: sage: streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi), density=2) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi), density=2) sphinx_plot(g) We ignore function values that are infinite or NaN:: sage: x, y = var('x y') sage: streamline_plot((-x/sqrt(x^2+y^2), -y/sqrt(x^2+y^2)), (x,-10,10), (y,-10,10)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((-x/sqrt(x**2+y**2), -y/sqrt(x**2+y**2)), (x,-10,10), (y,-10,10)) sphinx_plot(g) Extra options will get passed on to :func:`show()`, as long as they are valid:: sage: streamline_plot((x, y), (x,-2,2), (y,-2,2), xmax=10) Graphics object consisting of 1 graphics primitive sage: streamline_plot((x, y), (x,-2,2), (y,-2,2)).show(xmax=10) # These are equivalent .. PLOT:: x, y = var('x y') g = streamline_plot((x, y), (x,-2,2), (y,-2,2), xmax=10) sphinx_plot(g) We can also construct streamlines in a slope field:: sage: x, y = var('x y') sage: streamline_plot((x + y) / sqrt(x^2 + y^2), (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((x + y) / sqrt(x**2 + y**2), (x,-3,3), (y,-3,3)) sphinx_plot(g) We choose some particular points the streamlines pass through:: sage: pts = [[1, 1], [-2, 2], [1, -3/2]] sage: g = streamline_plot((x + y) / sqrt(x^2 + y^2), (x,-3,3), (y,-3,3), start_points=pts) sage: g += point(pts, color='red') sage: g Graphics object consisting of 2 graphics primitives .. PLOT:: x, y = var('x y') pts = [[1, 1], [-2, 2], [1, -3/2]] g = streamline_plot((x + y) / sqrt(x**2 + y**2), (x,-3,3), (y,-3,3), start_points=pts) g += point(pts, color='red') sphinx_plot(g) .. NOTE:: Streamlines currently pass close to ``start_points`` but do not necessarily pass directly through them. That is part of the behavior of matplotlib, not an error on your part. """ # Parse the function input if isinstance(f_g, (list, tuple)): (f, g) = f_g else: from sage.functions.all import sqrt from inspect import isfunction if isfunction(f_g): f = lambda x, y: 1 / sqrt(f_g(x, y)**2 + 1) g = lambda x, y: f_g(x, y) * f(x, y) else: f = 1 / sqrt(f_g**2 + 1) g = f_g * f from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid z, ranges = setup_for_eval_on_grid([f, g], [xrange, yrange], options['plot_points']) f, g = z # The density values must be floats if isinstance(options['density'], (list, tuple)): options['density'] = [float(x) for x in options['density']] else: options['density'] = float(options['density']) xpos_array, ypos_array, xvec_array, yvec_array = [], [], [], [] for x in xsrange(*ranges[0], include_endpoint=True): xpos_array.append(x) for y in xsrange(*ranges[1], include_endpoint=True): ypos_array.append(y) xvec_row, yvec_row = [], [] for x in xsrange(*ranges[0], include_endpoint=True): xvec_row.append(f(x, y)) yvec_row.append(g(x, y)) xvec_array.append(xvec_row) yvec_array.append(yvec_row) import numpy xpos_array = numpy.array(xpos_array, dtype=float) ypos_array = numpy.array(ypos_array, dtype=float) xvec_array = numpy.ma.masked_invalid(numpy.array(xvec_array, dtype=float)) yvec_array = numpy.ma.masked_invalid(numpy.array(yvec_array, dtype=float)) if 'start_points' in options: xstart_array, ystart_array = [], [] for point in options['start_points']: xstart_array.append(point[0]) ystart_array.append(point[1]) options['start_points'] = numpy.array([xstart_array, ystart_array]).T g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) g.add_primitive( StreamlinePlot(xpos_array, ypos_array, xvec_array, yvec_array, options)) return g
def density_plot(f, xrange, yrange, **options): r""" ``density_plot`` takes a function of two variables, `f(x,y)` and plots the height of the function over the specified ``xrange`` and ``yrange`` as demonstrated below. ``density_plot(f, (xmin,xmax), (ymin,ymax), ...)`` INPUT: - ``f`` -- a function of two variables - ``(xmin,xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin,ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 25); number of points to plot in each direction of the grid - ``cmap`` -- a colormap (default: ``'gray'``), the name of a predefined colormap, a list of colors or an instance of a matplotlib Colormap. Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()`` for available colormap names. - ``interpolation`` -- string (default: ``'catrom'``), the interpolation method to use: ``'bilinear'``, ``'bicubic'``, ``'spline16'``, ``'spline36'``, ``'quadric'``, ``'gaussian'``, ``'sinc'``, ``'bessel'``, ``'mitchell'``, ``'lanczos'``, ``'catrom'``, ``'hermite'``, ``'hanning'``, ``'hamming'``, ``'kaiser'`` EXAMPLES: Here we plot a simple function of two variables. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: x,y = var('x,y') sage: density_plot(sin(x) * sin(y), (x,-2,2), (y,-2,2)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = density_plot(sin(x) * sin(y), (x,-2,2), (y,-2,2)) sphinx_plot(g) Here we change the ranges and add some options; note that here ``f`` is callable (has variables declared), so we can use 2-tuple ranges:: sage: x,y = var('x,y') sage: f(x,y) = x^2 * cos(x*y) sage: density_plot(f, (x,-10,5), (y,-5,5), interpolation='sinc', plot_points=100) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') def f(x,y): return x**2 * cos(x*y) g = density_plot(f, (x,-10,5), (y,-5,5), interpolation='sinc', plot_points=100) sphinx_plot(g) An even more complicated plot:: sage: x,y = var('x,y') sage: density_plot(sin(x^2+y^2) * cos(x) * sin(y), (x,-4,4), (y,-4,4), cmap='jet', plot_points=100) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = density_plot(sin(x**2 + y**2)*cos(x)*sin(y), (x,-4,4), (y,-4,4), cmap='jet', plot_points=100) sphinx_plot(g) This should show a "spotlight" right on the origin:: sage: x,y = var('x,y') sage: density_plot(1/(x^10 + y^10), (x,-10,10), (y,-10,10)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = density_plot(1/(x**10 + y**10), (x,-10,10), (y,-10,10)) sphinx_plot(g) Some elliptic curves, but with symbolic endpoints. In the first example, the plot is rotated 90 degrees because we switch the variables `x`, `y`:: sage: density_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = density_plot(y**2 + 1 - x**3 - x, (y,-pi,pi), (x,-pi,pi)) sphinx_plot(g) :: sage: density_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = density_plot(y**2 + 1 - x**3 - x, (x,-pi,pi), (y,-pi,pi)) sphinx_plot(g) Extra options will get passed on to show(), as long as they are valid:: sage: density_plot(log(x) + log(y), (x,1,10), (y,1,10), dpi=20) Graphics object consisting of 1 graphics primitive .. PLOT:: x,y = var('x,y') g = density_plot(log(x) + log(y), (x,1,10), (y,1,10), dpi=20) sphinx_plot(g) :: sage: density_plot(log(x) + log(y), (x,1,10), (y,1,10)).show(dpi=20) # These are equivalent TESTS: Check that :trac:`15315` is fixed, i.e., density_plot respects the ``aspect_ratio`` parameter. Without the fix, it looks like a thin line of width a few mm. With the fix it should look like a nice fat layered image:: sage: density_plot((x*y)^(1/2), (x,0,3), (y,0,500), aspect_ratio=.01) Graphics object consisting of 1 graphics primitive Default ``aspect_ratio`` is ``"automatic"``, and that should work too:: sage: density_plot((x*y)^(1/2), (x,0,3), (y,0,500)) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid g, ranges = setup_for_eval_on_grid([f], [xrange, yrange], options['plot_points']) g = g[0] xrange, yrange = [r[:2] for r in ranges] xy_data_array = [[g(x,y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin','xmax'])) g.add_primitive(DensityPlot(xy_data_array, xrange, yrange, options)) return g
def _render_on_subplot(self, subplot): """ TESTS: A somewhat random plot, but fun to look at:: sage: x,y = var('x,y') sage: contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv') Graphics object consisting of 1 graphics primitive """ from sage.rings.integer import Integer options = self.options() fill = options['fill'] contours = options['contours'] if 'cmap' in options: cmap = get_cmap(options['cmap']) elif fill or contours is None: cmap = get_cmap('gray') else: if isinstance(contours, (int, Integer)): cmap = get_cmap([(i,i,i) for i in xsrange(0,1,1/contours)]) else: l = Integer(len(contours)) cmap = get_cmap([(i,i,i) for i in xsrange(0,1,1/l)]) x0,x1 = float(self.xrange[0]), float(self.xrange[1]) y0,y1 = float(self.yrange[0]), float(self.yrange[1]) if isinstance(contours, (int, Integer)): contours = int(contours) CSF=None if fill: if contours is None: CSF=subplot.contourf(self.xy_data_array, cmap=cmap, extent=(x0,x1,y0,y1), label=options['legend_label']) else: CSF=subplot.contourf(self.xy_data_array, contours, cmap=cmap, extent=(x0,x1,y0,y1),extend='both', label=options['legend_label']) linewidths = options.get('linewidths',None) if isinstance(linewidths, (int, Integer)): linewidths = int(linewidths) elif isinstance(linewidths, (list, tuple)): linewidths = tuple(int(x) for x in linewidths) from sage.plot.misc import get_matplotlib_linestyle linestyles = options.get('linestyles', None) if isinstance(linestyles, (list, tuple)): linestyles = [get_matplotlib_linestyle(l, 'long') for l in linestyles] else: linestyles = get_matplotlib_linestyle(linestyles, 'long') if contours is None: CS = subplot.contour(self.xy_data_array, cmap=cmap, extent=(x0,x1,y0,y1), linewidths=linewidths, linestyles=linestyles, label=options['legend_label']) else: CS = subplot.contour(self.xy_data_array, contours, cmap=cmap, extent=(x0,x1,y0,y1), linewidths=linewidths, linestyles=linestyles, label=options['legend_label']) if options.get('labels', False): label_options = options['label_options'] label_options['fontsize'] = int(label_options['fontsize']) if fill and label_options is None: label_options['inline']=False subplot.clabel(CS, **label_options) if options.get('colorbar', False): colorbar_options = options['colorbar_options'] from matplotlib import colorbar cax,kwds=colorbar.make_axes_gridspec(subplot,**colorbar_options) if CSF is None: cb=colorbar.Colorbar(cax,CS, **kwds) else: cb=colorbar.Colorbar(cax,CSF, **kwds) cb.add_lines(CS)
def bsgs(a, b, bounds, operation='*', identity=None, inverse=None, op=None): r""" Totally generic discrete baby-step giant-step function. Solves `na=b` (or `a^n=b`) with `lb\le n\le ub` where ``bounds==(lb,ub)``, raising an error if no such `n` exists. `a` and `b` must be elements of some group with given identity, inverse of ``x`` given by ``inverse(x)``, and group operation on ``x``, ``y`` by ``op(x,y)``. If operation is '*' or '+' then the other arguments are provided automatically; otherwise they must be provided by the caller. INPUT: - ``a`` - group element - ``b`` - group element - ``bounds`` - a 2-tuple of integers ``(lower,upper)`` with ``0<=lower<=upper`` - ``operation`` - string: '*', '+', 'other' - ``identity`` - the identity element of the group - ``inverse()`` - function of 1 argument ``x`` returning inverse of ``x`` - ``op()`` - function of 2 arguments ``x``, ``y`` returning ``x*y`` in group OUTPUT: An integer `n` such that `a^n = b` (or `na = b`). If no such `n` exists, this function raises a ValueError exception. NOTE: This is a generalization of discrete logarithm. One situation where this version is useful is to find the order of an element in a group where we only have bounds on the group order (see the elliptic curve example below). ALGORITHM: Baby step giant step. Time and space are soft `O(\sqrt{n})` where `n` is the difference between upper and lower bounds. EXAMPLES:: sage: b = Mod(2,37); a = b^20 sage: bsgs(b, a, (0,36)) 20 sage: p=next_prime(10^20) sage: a=Mod(2,p); b=a^(10^25) sage: bsgs(a, b, (10^25-10^6,10^25+10^6)) == 10^25 True sage: K = GF(3^6,'b') sage: a = K.gen() sage: b = a^210 sage: bsgs(a, b, (0,K.order()-1)) 210 sage: K.<z>=CyclotomicField(230) sage: w=z^500 sage: bsgs(z,w,(0,229)) 40 An additive example in an elliptic curve group:: sage: F.<a> = GF(37^5) sage: E = EllipticCurve(F, [1,1]) sage: P = E.lift_x(a); P (a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1) This will return a multiple of the order of P:: sage: bsgs(P,P.parent()(0),Hasse_bounds(F.order()),operation='+') 69327408 AUTHOR: - John Cremona (2008-03-15) """ Z = integer_ring.ZZ from operator import inv, mul, neg, add if operation in multiplication_names: identity = a.parent()(1) inverse = inv op = mul elif operation in addition_names: identity = a.parent()(0) inverse = neg op = add else: if identity is None or inverse is None or op is None: raise ValueError("identity, inverse and operation must be given") lb, ub = bounds if lb<0 or ub<lb: raise ValueError("bsgs() requires 0<=lb<=ub") if a.is_zero() and not b.is_zero(): raise ValueError("No solution in bsgs()") ran = 1 + ub - lb # the length of the interval c = op(inverse(b),multiple(a,lb,operation=operation)) if ran < 30: # use simple search for small ranges i = lb d = c # for i,d in multiples(a,ran,c,indexed=True,operation=operation): for i0 in range(ran): i = lb + i0 if identity == d: # identity == b^(-1)*a^i, so return i return Z(i) d = op(a,d) raise ValueError("No solution in bsgs()") m = ran.isqrt()+1 # we need sqrt(ran) rounded up table = dict() # will hold pairs (a^(lb+i),lb+i) for i in range(m) d=c for i0 in xsrange(m): i = lb + i0 if identity==d: # identity == b^(-1)*a^i, so return i return Z(i) table[d] = i d=op(d,a) c = op(c,inverse(d)) # this is now a**(-m) d=identity for i in xsrange(m): j = table.get(d) if j is not None: # then d == b*a**(-i*m) == a**j return Z(i*m + j) d=op(c,d) raise ValueError("Log of %s to the base %s does not exist in %s."%(b,a,bounds))
def bsgs(a, b, bounds, operation='*', identity=None, inverse=None, op=None): r""" Totally generic discrete baby-step giant-step function. Solves `na=b` (or `a^n=b`) with `lb\le n\le ub` where ``bounds==(lb,ub)``, raising an error if no such `n` exists. `a` and `b` must be elements of some group with given identity, inverse of ``x`` given by ``inverse(x)``, and group operation on ``x``, ``y`` by ``op(x,y)``. If operation is '*' or '+' then the other arguments are provided automatically; otherwise they must be provided by the caller. INPUT: - ``a`` - group element - ``b`` - group element - ``bounds`` - a 2-tuple of integers ``(lower,upper)`` with ``0<=lower<=upper`` - ``operation`` - string: '*', '+', 'other' - ``identity`` - the identity element of the group - ``inverse()`` - function of 1 argument ``x`` returning inverse of ``x`` - ``op()`` - function of 2 arguments ``x``, ``y`` returning ``x*y`` in group OUTPUT: An integer `n` such that `a^n = b` (or `na = b`). If no such `n` exists, this function raises a ValueError exception. NOTE: This is a generalization of discrete logarithm. One situation where this version is useful is to find the order of an element in a group where we only have bounds on the group order (see the elliptic curve example below). ALGORITHM: Baby step giant step. Time and space are soft `O(\sqrt{n})` where `n` is the difference between upper and lower bounds. EXAMPLES:: sage: from sage.groups.generic import bsgs sage: b = Mod(2,37); a = b^20 sage: bsgs(b, a, (0,36)) 20 sage: p=next_prime(10^20) sage: a=Mod(2,p); b=a^(10^25) sage: bsgs(a, b, (10^25-10^6,10^25+10^6)) == 10^25 True sage: K = GF(3^6,'b') sage: a = K.gen() sage: b = a^210 sage: bsgs(a, b, (0,K.order()-1)) 210 sage: K.<z>=CyclotomicField(230) sage: w=z^500 sage: bsgs(z,w,(0,229)) 40 An additive example in an elliptic curve group:: sage: F.<a> = GF(37^5) sage: E = EllipticCurve(F, [1,1]) sage: P = E.lift_x(a); P (a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1) This will return a multiple of the order of P:: sage: bsgs(P,P.parent()(0),Hasse_bounds(F.order()),operation='+') 69327408 AUTHOR: - John Cremona (2008-03-15) """ Z = integer_ring.ZZ from operator import inv, mul, neg, add if operation in multiplication_names: identity = a.parent()(1) inverse = inv op = mul elif operation in addition_names: identity = a.parent()(0) inverse = neg op = add else: if identity is None or inverse is None or op is None: raise ValueError("identity, inverse and operation must be given") lb, ub = bounds if lb < 0 or ub < lb: raise ValueError("bsgs() requires 0<=lb<=ub") if a.is_zero() and not b.is_zero(): raise ValueError("No solution in bsgs()") ran = 1 + ub - lb # the length of the interval c = op(inverse(b), multiple(a, lb, operation=operation)) if ran < 30: # use simple search for small ranges i = lb d = c # for i,d in multiples(a,ran,c,indexed=True,operation=operation): for i0 in range(ran): i = lb + i0 if identity == d: # identity == b^(-1)*a^i, so return i return Z(i) d = op(a, d) raise ValueError("No solution in bsgs()") m = ran.isqrt() + 1 # we need sqrt(ran) rounded up table = dict() # will hold pairs (a^(lb+i),lb+i) for i in range(m) d = c for i0 in xsrange(m): i = lb + i0 if identity == d: # identity == b^(-1)*a^i, so return i return Z(i) table[d] = i d = op(d, a) c = op(c, inverse(d)) # this is now a**(-m) d = identity for i in xsrange(m): j = table.get(d) if j is not None: # then d == b*a**(-i*m) == a**j return Z(i * m + j) d = op(c, d) raise ValueError("Log of %s to the base %s does not exist in %s." % (b, a, bounds))
def contour_plot(f, xrange, yrange, **options): r""" ``contour_plot`` takes a function of two variables, `f(x,y)` and plots contour lines of the function over the specified ``xrange`` and ``yrange`` as demonstrated below. ``contour_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid. For old computers, 25 is fine, but should not be used to verify specific intersection points. - ``fill`` -- bool (default: ``True``), whether to color in the area between contour lines - ``cmap`` -- a colormap (default: ``'gray'``), the name of a predefined colormap, a list of colors or an instance of a matplotlib Colormap. Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()`` for available colormap names. - ``contours`` -- integer or list of numbers (default: ``None``): If a list of numbers is given, then this specifies the contour levels to use. If an integer is given, then this many contour lines are used, but the exact levels are determined automatically. If ``None`` is passed (or the option is not given), then the number of contour lines is determined automatically, and is usually about 5. - ``linewidths`` -- integer or list of integer (default: None), if a single integer all levels will be of the width given, otherwise the levels will be plotted with the width in the order given. If the list is shorter than the number of contours, then the widths will be repeated cyclically. - ``linestyles`` -- string or list of strings (default: None), the style of the lines to be plotted, one of: ``"solid"``, ``"dashed"``, ``"dashdot"``, ``"dotted"``, respectively ``"-"``, ``"--"``, ``"-."``, ``":"``. If the list is shorter than the number of contours, then the styles will be repeated cyclically. - ``labels`` -- boolean (default: False) Show level labels or not. The following options are to adjust the style and placement of labels, they have no effect if no labels are shown. - ``label_fontsize`` -- integer (default: 9), the font size of the labels. - ``label_colors`` -- string or sequence of colors (default: None) If a string, gives the name of a single color with which to draw all labels. If a sequence, gives the colors of the labels. A color is a string giving the name of one or a 3-tuple of floats. - ``label_inline`` -- boolean (default: False if fill is True, otherwise True), controls whether the underlying contour is removed or not. - ``label_inline_spacing`` -- integer (default: 3), When inline, this is the amount of contour that is removed from each side, in pixels. - ``label_fmt`` -- a format string (default: "%1.2f"), this is used to get the label text from the level. This can also be a dictionary with the contour levels as keys and corresponding text string labels as values. It can also be any callable which returns a string when called with a numeric contour level. - ``colorbar`` -- boolean (default: False) Show a colorbar or not. The following options are to adjust the style and placement of colorbars. They have no effect if a colorbar is not shown. - ``colorbar_orientation`` -- string (default: 'vertical'), controls placement of the colorbar, can be either 'vertical' or 'horizontal' - ``colorbar_format`` -- a format string, this is used to format the colorbar labels. - ``colorbar_spacing`` -- string (default: 'proportional'). If 'proportional', make the contour divisions proportional to values. If 'uniform', space the colorbar divisions uniformly, without regard for numeric values. - ``legend_label`` -- the label for this item in the legend - ``region`` - (default: None) If region is given, it must be a function of two variables. Only segments of the surface where region(x,y) returns a number >0 will be included in the plot. EXAMPLES: Here we plot a simple function of two variables. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: x,y = var('x,y') sage: contour_plot(cos(x^2+y^2), (x, -4, 4), (y, -4, 4)) Graphics object consisting of 1 graphics primitive Here we change the ranges and add some options:: sage: x,y = var('x,y') sage: contour_plot((x^2)*cos(x*y), (x, -10, 5), (y, -5, 5), fill=False, plot_points=150) Graphics object consisting of 1 graphics primitive An even more complicated plot:: sage: x,y = var('x,y') sage: contour_plot(sin(x^2 + y^2)*cos(x)*sin(y), (x, -4, 4), (y, -4, 4),plot_points=150) Graphics object consisting of 1 graphics primitive Some elliptic curves, but with symbolic endpoints. In the first example, the plot is rotated 90 degrees because we switch the variables `x`, `y`:: sage: x,y = var('x,y') sage: contour_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi)) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive We can play with the contour levels:: sage: x,y = var('x,y') sage: f(x,y) = x^2 + y^2 sage: contour_plot(f, (-2, 2), (-2, 2)) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=2, cmap=[(1,0,0), (0,1,0), (0,0,1)]) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(0.1, 1.0, 1.2, 1.4), cmap='hsv') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(1.0,), fill=False) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,0,1]) Graphics object consisting of 1 graphics primitive We can change the style of the lines:: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linewidths=10) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linestyles='dashdot') Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed'],fill=False) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed']) sage: P Graphics object consisting of 1 graphics primitive sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['-',':']) sage: P Graphics object consisting of 1 graphics primitive We can add labels and play with them:: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv', labels=True) Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',\ ... labels=True, label_fmt="%1.0f", label_colors='black') sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt={-4:"low", 0:"medium", 4: "hi"}, label_colors='black') sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt=lambda x: "$z=%s$"%x, label_colors='black', label_inline=True, \ ... label_fontsize=12) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_fontsize=18) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline_spacing=1) sage: P Graphics object consisting of 1 graphics primitive :: sage: P= contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline=False) sage: P Graphics object consisting of 1 graphics primitive We can change the color of the labels if so desired:: sage: contour_plot(f, (-2,2), (-2,2), labels=True, label_colors='red') Graphics object consisting of 1 graphics primitive We can add a colorbar as well:: sage: f(x,y)=x^2-y^2 sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True,colorbar_orientation='horizontal') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True,colorbar_spacing='uniform') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[0,2,3,6],colorbar=True,colorbar_format='%.3f') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), labels=True,label_colors='red',contours=[0,2,3,6],colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), cmap='winter', contours=20, fill=False, colorbar=True) Graphics object consisting of 1 graphics primitive This should plot concentric circles centered at the origin:: sage: x,y = var('x,y') sage: contour_plot(x^2+y^2-2,(x,-1,1), (y,-1,1)) Graphics object consisting of 1 graphics primitive Extra options will get passed on to show(), as long as they are valid:: sage: f(x, y) = cos(x) + sin(y) sage: contour_plot(f, (0, pi), (0, pi), axes=True) Graphics object consisting of 1 graphics primitive One can also plot over a reduced region:: sage: contour_plot(x**2-y**2, (x,-2, 2), (y,-2, 2),region=x-y,plot_points=300) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (0, pi), (0, pi)).show(axes=True) # These are equivalent Note that with ``fill=False`` and grayscale contours, there is the possibility of confusion between the contours and the axes, so use ``fill=False`` together with ``axes=True`` with caution:: sage: contour_plot(f, (-pi, pi), (-pi, pi), fill=False, axes=True) Graphics object consisting of 1 graphics primitive TESTS: To check that :trac:`5221` is fixed, note that this has three curves, not two:: sage: x,y = var('x,y') sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,-2,0], fill=False) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid region = options.pop('region') ev = [f] if region is None else [f,region] F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], options['plot_points']) g = F[0] xrange,yrange=[r[:2] for r in ranges] xy_data_array = [[g(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] if region is not None: import numpy xy_data_array = numpy.ma.asarray(xy_data_array,dtype=float) m = F[1] mask = numpy.asarray([[m(x, y)<=0 for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)],dtype=bool) xy_data_array[mask] = numpy.ma.masked g = Graphics() # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. # Otherwise matplotlib complains. scale = options.get('scale', None) if isinstance(scale, (list, tuple)): scale = scale[0] if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options)) return g
def density_plot(f, xrange, yrange, **options): r""" ``density_plot`` takes a function of two variables, `f(x,y)` and plots the height of of the function over the specified ``xrange`` and ``yrange`` as demonstrated below. ``density_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 25); number of points to plot in each direction of the grid - ``cmap`` -- a colormap (type ``cmap_help()`` for more information). - ``interpolation`` -- string (default: ``'catrom'``), the interpolation method to use: ``'bilinear'``, ``'bicubic'``, ``'spline16'``, ``'spline36'``, ``'quadric'``, ``'gaussian'``, ``'sinc'``, ``'bessel'``, ``'mitchell'``, ``'lanczos'``, ``'catrom'``, ``'hermite'``, ``'hanning'``, ``'hamming'``, ``'kaiser'`` EXAMPLES: Here we plot a simple function of two variables. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: x,y = var('x,y') sage: density_plot(sin(x)*sin(y), (x, -2, 2), (y, -2, 2)) Graphics object consisting of 1 graphics primitive Here we change the ranges and add some options; note that here ``f`` is callable (has variables declared), so we can use 2-tuple ranges:: sage: x,y = var('x,y') sage: f(x,y) = x^2*cos(x*y) sage: density_plot(f, (x,-10,5), (y, -5,5), interpolation='sinc', plot_points=100) Graphics object consisting of 1 graphics primitive An even more complicated plot:: sage: x,y = var('x,y') sage: density_plot(sin(x^2 + y^2)*cos(x)*sin(y), (x, -4, 4), (y, -4, 4), cmap='jet', plot_points=100) Graphics object consisting of 1 graphics primitive This should show a "spotlight" right on the origin:: sage: x,y = var('x,y') sage: density_plot(1/(x^10+y^10), (x, -10, 10), (y, -10, 10)) Graphics object consisting of 1 graphics primitive Some elliptic curves, but with symbolic endpoints. In the first example, the plot is rotated 90 degrees because we switch the variables `x`, `y`:: sage: density_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi)) Graphics object consisting of 1 graphics primitive :: sage: density_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive Extra options will get passed on to show(), as long as they are valid:: sage: density_plot(log(x) + log(y), (x, 1, 10), (y, 1, 10), dpi=20) Graphics object consisting of 1 graphics primitive :: sage: density_plot(log(x) + log(y), (x, 1, 10), (y, 1, 10)).show(dpi=20) # These are equivalent TESTS: Check that :trac:`15315` is fixed, i.e., density_plot respects the ``aspect_ratio`` parameter. Without the fix, it looks like a thin line of width a few mm. With the fix it should look like a nice fat layered image:: sage: density_plot((x*y)^(1/2), (x,0,3), (y,0,500), aspect_ratio=.01) Graphics object consisting of 1 graphics primitive Default ``aspect_ratio`` is ``"automatic"``, and that should work too:: sage: density_plot((x*y)^(1/2), (x,0,3), (y,0,500)) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid g, ranges = setup_for_eval_on_grid([f], [xrange, yrange], options['plot_points']) g = g[0] xrange,yrange=[r[:2] for r in ranges] xy_data_array = [[g(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(DensityPlot(xy_data_array, xrange, yrange, options)) return g
def streamline_plot(f_g, xrange, yrange, **options): r""" Return a streamline plot in a vector field. ``streamline_plot`` can take either one or two functions. Consider two variables `x` and `y`. If given two functions `(f(x,y), g(x,y))`, then this function plots streamlines in the vector field over the specified ranges with ``xrange`` being of `x`, denoted by ``xvar`` below, between ``xmin`` and ``xmax``, and ``yrange`` similarly (see below). :: streamline_plot((f, g), (xvar, xmin, xmax), (yvar, ymin, ymax)) Similarly, if given one function `f(x, y)`, then this function plots streamlines in the slope field `dy/dx = f(x,y)` over the specified ranges as given above. PLOT OPTIONS: - ``plot_points`` -- (default: 200) the minimal number of plot points - ``density`` -- float (default: 1.); controls the closeness of streamlines - ``start_points`` -- (optional) list of coordinates of starting points for the streamlines; coordinate pairs can be tuples or lists EXAMPLES: Plot some vector fields involving `\sin` and `\cos`:: sage: x, y = var('x y') sage: streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3)) sphinx_plot(g) :: sage: streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi)) sphinx_plot(g) We increase the density of the plot:: sage: streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi), density=2) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((y, (cos(x)-2) * sin(x)), (x,-pi,pi), (y,-pi,pi), density=2) sphinx_plot(g) We ignore function values that are infinite or NaN:: sage: x, y = var('x y') sage: streamline_plot((-x/sqrt(x^2+y^2), -y/sqrt(x^2+y^2)), (x,-10,10), (y,-10,10)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((-x/sqrt(x**2+y**2), -y/sqrt(x**2+y**2)), (x,-10,10), (y,-10,10)) sphinx_plot(g) Extra options will get passed on to :func:`show()`, as long as they are valid:: sage: streamline_plot((x, y), (x,-2,2), (y,-2,2), xmax=10) Graphics object consisting of 1 graphics primitive sage: streamline_plot((x, y), (x,-2,2), (y,-2,2)).show(xmax=10) # These are equivalent .. PLOT:: x, y = var('x y') g = streamline_plot((x, y), (x,-2,2), (y,-2,2), xmax=10) sphinx_plot(g) We can also construct streamlines in a slope field:: sage: x, y = var('x y') sage: streamline_plot((x + y) / sqrt(x^2 + y^2), (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive .. PLOT:: x, y = var('x y') g = streamline_plot((x + y) / sqrt(x**2 + y**2), (x,-3,3), (y,-3,3)) sphinx_plot(g) We choose some particular points the streamlines pass through:: sage: pts = [[1, 1], [-2, 2], [1, -3/2]] sage: g = streamline_plot((x + y) / sqrt(x^2 + y^2), (x,-3,3), (y,-3,3), start_points=pts) sage: g += point(pts, color='red') sage: g Graphics object consisting of 2 graphics primitives .. PLOT:: x, y = var('x y') pts = [[1, 1], [-2, 2], [1, -3/2]] g = streamline_plot((x + y) / sqrt(x**2 + y**2), (x,-3,3), (y,-3,3), start_points=pts) g += point(pts, color='red') sphinx_plot(g) .. NOTE:: Streamlines currently pass close to ``start_points`` but do not necessarily pass directly through them. That is part of the behavior of matplotlib, not an error on your part. """ # Parse the function input if isinstance(f_g, (list, tuple)): (f,g) = f_g else: from sage.functions.all import sqrt from inspect import isfunction if isfunction(f_g): f = lambda x,y: 1 / sqrt(f_g(x, y)**2 + 1) g = lambda x,y: f_g(x, y) * f(x, y) else: f = 1 / sqrt(f_g**2 + 1) g = f_g * f from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid z, ranges = setup_for_eval_on_grid([f,g], [xrange,yrange], options['plot_points']) f, g = z # The density values must be floats if isinstance(options['density'], (list, tuple)): options['density'] = [float(x) for x in options['density']] else: options['density'] = float(options['density']) xpos_array, ypos_array, xvec_array, yvec_array = [], [], [], [] for x in xsrange(*ranges[0], include_endpoint=True): xpos_array.append(x) for y in xsrange(*ranges[1], include_endpoint=True): ypos_array.append(y) xvec_row, yvec_row = [], [] for x in xsrange(*ranges[0], include_endpoint=True): xvec_row.append(f(x, y)) yvec_row.append(g(x, y)) xvec_array.append(xvec_row) yvec_array.append(yvec_row) import numpy xpos_array = numpy.array(xpos_array, dtype=float) ypos_array = numpy.array(ypos_array, dtype=float) xvec_array = numpy.ma.masked_invalid(numpy.array(xvec_array, dtype=float)) yvec_array = numpy.ma.masked_invalid(numpy.array(yvec_array, dtype=float)) if 'start_points' in options: xstart_array, ystart_array = [], [] for point in options['start_points']: xstart_array.append(point[0]) ystart_array.append(point[1]) options['start_points'] = numpy.array([xstart_array, ystart_array]).T g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) g.add_primitive(StreamlinePlot(xpos_array, ypos_array, xvec_array, yvec_array, options)) return g