def herm_signature(self): from sage.all import exp, vector, matrix from sage.misc.functional import numerical_approx n = self._dimension alpha_exp = [ numerical_approx(exp(2 * 1j * pi * self._alpha[i]), 30) for i in xrange(self._dimension) ] beta_exp = [ numerical_approx(exp(2 * 1j * pi * self._beta[i]), 30) for i in xrange(self._dimension) ] v = vector([1] * n) * matrix( n, n, lambda i, j: 1 / (alpha_exp[j] + beta_exp[i])).inverse() d_val = [ 1 / (-beta_exp[i] / v[i]).real_part() for i in xrange(self._dimension) ] d_val.sort() k = 0 while (k < n) and (d_val[k] < 0): k += 1 return (k, n - k)
def herm_signature(self): from sage.all import exp, vector, matrix from sage.misc.functional import numerical_approx n = self._dimension alpha_exp = [numerical_approx(exp(2*1j*pi*self._alpha[i]),30) for i in xrange(self._dimension)] beta_exp = [numerical_approx(exp(2*1j*pi*self._beta[i]),30) for i in xrange(self._dimension)] v = vector([1]*n)*matrix(n,n, lambda i, j: 1/(alpha_exp[j] + beta_exp[i])).inverse() d_val = [1/(-beta_exp[i]/v[i]).real_part() for i in xrange(self._dimension)] d_val.sort() k = 0 while (k < n) and (d_val[k] < 0): k += 1 return(k, n-k)
def show(self, boundary=True, **options): r""" Plot ``self``. EXAMPLES:: sage: HyperbolicPlane().PD().get_point(0).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().KM().get_point((0,0)).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().HM().get_point((0,0,1)).show() Graphics3d Object """ p = self.coordinates() if p == infinity: raise NotImplementedError("can't draw the point infinity") opts = {'axes': False, 'aspect_ratio': 1} opts.update(self.graphics_options()) opts.update(options) from sage.plot.point import point from sage.misc.functional import numerical_approx if self._bdry: # It is a boundary point p = numerical_approx(p) pic = point((p, 0), **opts) if boundary: bd_pic = self._model.get_background_graphic(bd_min=p - 1, bd_max=p + 1) pic = bd_pic + pic else: # It is an interior point if p in RR: p = CC(p) elif hasattr(p, 'iteritems') or hasattr(p, '__iter__'): p = [numerical_approx(k) for k in p] else: p = numerical_approx(p) pic = point(p, **opts) if boundary: bd_pic = self.parent().get_background_graphic() pic = bd_pic + pic return pic
def show(self, boundary=True, **options): r""" Plot ``self``. EXAMPLES:: sage: HyperbolicPlane().PD().get_point(0).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().KM().get_point((0,0)).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().HM().get_point((0,0,1)).show() Graphics3d Object """ p = self.coordinates() if p == infinity: raise NotImplementedError("can't draw the point infinity") opts = {'axes': False, 'aspect_ratio': 1} opts.update(self.graphics_options()) opts.update(options) from sage.plot.point import point from sage.misc.functional import numerical_approx if self._bdry: # It is a boundary point p = numerical_approx(p) pic = point((p, 0), **opts) if boundary: bd_pic = self._model.get_background_graphic(bd_min=p - 1, bd_max=p + 1) pic = bd_pic + pic else: # It is an interior point if p in RR: p = CC(p) elif hasattr(p, 'items') or hasattr(p, '__iter__'): p = [numerical_approx(k) for k in p] else: p = numerical_approx(p) pic = point(p, **opts) if boundary: bd_pic = self.parent().get_background_graphic() pic = bd_pic + pic return pic
def __cmp__(self, other): """ Compare two coefficient lists of the same height. """ if other is not None: assert self.height == other.height, "comparing unequal heights" if self.height == 0: if other is None: val = Integer(0) else: val = other.val d = SR(self.val - val) if d.is_constant(): d = numerical_approx(d) if isinstance(d, ComplexNumber): p = (d.real_part(), d.imag_part()) else: p = (d, 0) if p > (0, 0): return 1 elif p < (0, 0): return -1 elif checkPos(self.val - val): return 1 elif checkPos(val - self.val): return -1 return 0 if other is None: keys = set() else: keys = set(six.iterkeys(other.val)) for k in sorted(keys.union(six.iterkeys(self.val)), reverse=True): if k not in keys: c = self.val[k].__cmp__(None) elif k not in self.val: c = -other.val[k].__cmp__(None) else: c = self.val[k].__cmp__(other.val[k]) if c != 0: return c return 0
def show(self, boundary=True, **options): r""" Plot ``self``. EXAMPLES:: sage: HyperbolicPlane().UHP().get_point(I).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().UHP().get_point(0).show() Graphics object consisting of 2 graphics primitives sage: HyperbolicPlane().UHP().get_point(infinity).show() Traceback (most recent call last): ... NotImplementedError: can't draw the point infinity """ p = self.coordinates() if p == infinity: raise NotImplementedError("can't draw the point infinity") opts = {'axes': False, 'aspect_ratio': 1} opts.update(self.graphics_options()) opts.update(options) from sage.misc.functional import numerical_approx p = numerical_approx(p + 0 * I) from sage.plot.point import point if self._bdry: pic = point((p, 0), **opts) if boundary: bd_pic = self.parent().get_background_graphic(bd_min=p - 1, bd_max=p + 1) pic = bd_pic + pic else: pic = point(p, **opts) if boundary: cent = real(p) bd_pic = self.parent().get_background_graphic(bd_min=cent - 1, bd_max=cent + 1) pic = bd_pic + pic return pic
def plot(self, chart=None, ambient_coords=None, mapping=None, prange=None, include_end_point=(True, True), end_point_offset=(0.001, 0.001), parameters=None, color='red', style='-', label_axes=True, **kwds): r""" Plot the current curve in a Cartesian graph based on the coordinates of some ambient chart. The curve is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The ambient chart's domain must overlap with the curve's codomain or with the codomain of the composite curve `\Phi\circ c`, where `c` is the current curve and `\Phi` some manifold differential map (argument ``mapping`` below). INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, the default chart of the codomain of the curve (or of the curve composed with `\Phi`) is used - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi` (instance of :class:`~sage.manifolds.differentiable.diff_map.DiffMap`) providing the link between the curve and the ambient chart ``chart`` (cf. above); if ``None``, the ambient chart is supposed to be defined on the codomain of the curve. - ``prange`` -- (default: ``None``) range of the curve parameter for the plot; if ``None``, the entire parameter range declared during the curve construction is considered (with -Infinity replaced by ``-max_range`` and +Infinity by ``max_range``) - ``include_end_point`` -- (default: ``(True, True)``) determines whether the end points of ``prange`` are included in the plot - ``end_point_offset`` -- (default: ``(0.001, 0.001)``) offsets from the end points when they are not included in the plot: if ``include_end_point[0] == False``, the minimal value of the curve parameter used for the plot is ``prange[0] + end_point_offset[0]``, while if ``include_end_point[1] == False``, the maximal value is ``prange[1] - end_point_offset[1]``. - ``max_range`` -- (default: 8) numerical value substituted to +Infinity if the latter is the upper bound of the parameter range; similarly ``-max_range`` is the numerical valued substituted for -Infinity - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of the curve - ``color`` -- (default: 'red') color of the drawn curve - ``style`` -- (default: '-') color of the drawn curve; NB: ``style`` is effective only for 2D plots - ``thickness`` -- (default: 1) thickness of the drawn curve - ``plot_points`` -- (default: 75) number of points to plot the curve - ``label_axes`` -- (default: ``True``) boolean determining whether the labels of the coordinate axes of ``chart`` shall be added to the graph; can be set to ``False`` if the graph is 3D and must be superposed with another graph. - ``aspect_ratio`` -- (default: ``'automatic'``) aspect ratio of the plot; the default value (``'automatic'``) applies only for 2D plots; for 3D plots, the default value is ``1`` instead OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Plot of the lemniscate of Gerono:: sage: R2 = Manifold(2, 'R^2') sage: X.<x,y> = R2.chart() sage: R.<t> = RealLine() sage: c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c') sage: c.plot() # 2D plot Graphics object consisting of 1 graphics primitive .. PLOT:: R2 = Manifold(2, 'R^2') X = R2.chart('x y') t = RealLine().canonical_coordinate() c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c') g = c.plot() sphinx_plot(g) Plot for a subinterval of the curve's domain:: sage: c.plot(prange=(0,pi)) Graphics object consisting of 1 graphics primitive .. PLOT:: R2 = Manifold(2, 'R^2') X = R2.chart('x y') t = RealLine().canonical_coordinate() c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c') g = c.plot(prange=(0,pi)) sphinx_plot(g) Plot with various options:: sage: c.plot(color='green', style=':', thickness=3, aspect_ratio=1) Graphics object consisting of 1 graphics primitive .. PLOT:: R2 = Manifold(2, 'R^2') X = R2.chart('x y') t = RealLine().canonical_coordinate() c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c') g = c.plot(color='green', style=':', thickness=3, aspect_ratio=1) sphinx_plot(g) Plot via a mapping to another manifold: loxodrome of a sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='F') sage: F.display() F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: c = S2.curve([2*atan(exp(-t/10)), t], (t, -oo, +oo), name='c') sage: graph_c = c.plot(mapping=F, max_range=40, ....: plot_points=200, thickness=2, label_axes=False) # 3D plot sage: graph_S2 = XS.plot(X3, mapping=F, number_values=11, color='black') # plot of the sphere sage: show(graph_c + graph_S2) # the loxodrome + the sphere .. PLOT:: S2 = Manifold(2, 'S^2') U = S2.open_subset('U') XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') th, ph = XS[:] R3 = Manifold(3, 'R^3') X3 = R3.chart('x y z') F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)]}, name='F') t = RealLine().canonical_coordinate() c = S2.curve([2*atan(exp(-t/10)), t], (t, -oo, +oo), name='c') graph_c = c.plot(mapping=F, max_range=40, plot_points=200, thickness=2, label_axes=False) graph_S2 = XS.plot(X3, mapping=F, number_values=11, color='black') sphinx_plot(graph_c + graph_S2) Example of use of the argument ``parameters``: we define a curve with some symbolic parameters ``a`` and ``b``:: sage: a, b = var('a b') sage: c = R2.curve([a*cos(t) + b, a*sin(t)], (t, 0, 2*pi), name='c') To make a plot, we set specific values for ``a`` and ``b`` by means of the Python dictionary ``parameters``:: sage: c.plot(parameters={a: 2, b: -3}, aspect_ratio=1) Graphics object consisting of 1 graphics primitive .. PLOT:: R2 = Manifold(2, 'R^2') X = R2.chart('x y') t = RealLine().canonical_coordinate() a, b = var('a b') c = R2.curve([a*cos(t) + b, a*sin(t)], (t, 0, 2*pi), name='c') g = c.plot(parameters={a: 2, b: -3}, aspect_ratio=1) sphinx_plot(g) """ from sage.rings.infinity import Infinity from sage.misc.functional import numerical_approx from sage.manifolds.chart import RealChart # # Get the @options from kwds # thickness = kwds.pop('thickness') plot_points = kwds.pop('plot_points') max_range = kwds.pop('max_range') aspect_ratio = kwds.pop('aspect_ratio') # # The "effective" curve to be plotted # if mapping is None: eff_curve = self else: eff_curve = mapping.restrict(self.codomain()) * self # # The chart w.r.t. which the curve is plotted # if chart is None: chart = eff_curve._codomain.default_chart() elif not isinstance(chart, RealChart): raise TypeError("{} is not a real chart".format(chart)) # # Coordinates of the above chart w.r.t. which the curve is plotted # if ambient_coords is None: ambient_coords = chart[:] # all chart coordinates are used n_pc = len(ambient_coords) if n_pc != 2 and n_pc !=3: raise ValueError("the number of coordinates involved in the " + "plot must be either 2 or 3, not {}".format(n_pc)) # indices of plot coordinates ind_pc = [chart[:].index(pc) for pc in ambient_coords] # # Parameter range for the plot # if prange is None: prange = (self._domain.lower_bound(), self._domain.upper_bound()) elif not isinstance(prange, (tuple, list)): raise TypeError("{} is neither a tuple nor a list".format(prange)) elif len(prange) != 2: raise ValueError("the argument prange must be a tuple/list " + "of 2 elements") tmin = prange[0] tmax = prange[1] if tmin == -Infinity: tmin = -max_range elif not include_end_point[0]: tmin = tmin + end_point_offset[0] if tmax == Infinity: tmax = max_range elif not include_end_point[1]: tmax = tmax - end_point_offset[1] tmin = numerical_approx(tmin) tmax = numerical_approx(tmax) # # The coordinate expression of the effective curve # canon_chart = self._domain.canonical_chart() transf = None for chart_pair in eff_curve._coord_expression: if chart_pair == (canon_chart, chart): transf = eff_curve._coord_expression[chart_pair] break else: # Search for a subchart for chart_pair in eff_curve._coord_expression: for schart in chart._subcharts: if chart_pair == (canon_chart, schart): transf = eff_curve._coord_expression[chart_pair] if transf is None: raise ValueError("No expression has been found for " + "{} in terms of {}".format(self, chart)) # # List of points for the plot curve # plot_curve = [] dt = (tmax - tmin) / (plot_points - 1) t = tmin if parameters is None: for i in range(plot_points): x = transf(t, simplify=False) plot_curve.append( [numerical_approx(x[j]) for j in ind_pc] ) t += dt else: for i in range(plot_points): x = transf(t, simplify=False) plot_curve.append( [numerical_approx( x[j].substitute(parameters) ) for j in ind_pc] ) t += dt return self._graphics(plot_curve, ambient_coords, thickness=thickness, aspect_ratio=aspect_ratio, color= color, style=style, label_axes=label_axes)
def lyap_exp_CY(beta1, beta2, nb_vectors=None, nb_experiments=10, nb_iterations=10**4, verbose=False, output_file=None, return_error=False): r""" Compute the Lyapunov exponents of the geodesic flow in the hypergeometric function space. The input parameters yield the eigenvalues around infinity, $e^{2i\pi\beta_1}, e^{2i\pi\beta_2}, e^{-2i\pi\beta_1}, e^{-2i\pi\beta_1}$. We compute the coefficient of the characteristic polynomial of such a matrix. And call the C function which associate to these coefficient the companion matrices of the polynomial. We use Levelt theorem for computing monodromy matrices. See Theorem 3.2.3 in [Beu]. INPUT: - ``beta1``, ``beta2`` -- parameters of the eigenvalues - ``nb_vectors`` -- the number of vectors to use - ``nb_experiments`` -- number of experimets - ``nb_iterations`` -- the number of iterations of the induction to perform - ``output_file`` -- place where we print the results - ``verbose`` -- do we print the result with an extensive number of information or not OUTPUT: A list of nb_vectors lyapunov exponents by default. If return_error is True, a 4-tuple consisting of : 1. a list of nb_vectors lyapunov exponents 2. a list of nb_vectors of their statistical error 3. an integer of their sum 4. an integer of the statistical error of their sum """ from sage.all import exp from sage.misc.functional import numerical_approx import time import lyapunov_exponents # the cython bindings from math import sqrt if output_file is None: from sys import stdout output_file = stdout closed = True elif isinstance(output_file, str): output_file = open(output_file, "w") closed = False beta1 = numerical_approx(beta1) beta2 = numerical_approx(beta2) b1 = numerical_approx(exp(2 * 1j * pi * beta1)) b2 = numerical_approx(exp(2 * 1j * pi * beta2)) a = (b1**2 * b2 + b1 * b2**2 + b1 + b2) / (b1 * b2) b = (-b1**2 * b2**2 - b1**2 - 2 * b1 * b2 - b2**2 - 1) / (b1 * b2) if nb_vectors <> None and nb_vectors <= 0: raise ValueError("the number of vectors must be positive") if nb_experiments <= 0: raise ValueError("the number of experiments must be positive") if nb_iterations <= 0: raise ValueError("the number of iterations must be positive") #recall that the lyapunov exponents are symmetric if nb_vectors == None: nb_vectors = 2 t0 = time.time() res = lyapunov_exponents.lyapunov_exponents([0] * 4, [0] * 4, 4, nb_vectors, nb_experiments, nb_iterations, [a, b]) t1 = time.time() res_final = [] std_final = [] s_m, s_d = 0, 0 if verbose: from math import floor, log output_file.write("sample of %d experiments\n" % nb_experiments) output_file.write("%d iterations (~2**%d)\n" % (nb_iterations, floor(log(nb_iterations) / log(2)))) output_file.write("ellapsed time %s\n" % time.strftime("%H:%M:%S", time.gmtime(t1 - t0))) for i in xrange(nb_vectors): m, d = mean_and_std_dev(res[i]) s_m += m s_d += d**2 if verbose: output_file.write( "theta%d : %f (std. dev. = %f, conf. rad. 0.01 = %f)\n" % (i, m, d, 2.576 * d / sqrt(nb_experiments))) res_final.append(m) std_final.append(2.576 * d / sqrt(nb_experiments)) s_d = sqrt(s_d) s_d_final = 2.576 * s_d / sqrt(nb_experiments) if verbose: output_file.write( "sum_theta : %f (std. dev. = %f, conf. rad. 0.01 = %f)\n\n" % (s_m, s_d, 2.576 * s_d / sqrt(nb_experiments))) if not closed: output_file.close() print "file closed" if return_error: return (res_final, std_final, s_m, s_d_final) else: return res_final
def compute_percolation_probability(range_p, d, n, stop): r""" EXAMPLES:: sage: from slabbe.bond_percolation import compute_percolation_probability sage: compute_percolation_probability(srange(0,0.8,0.1), d=2, n=5, stop=100) # random d = 2, n = number of samples = 5 stop counting at = 100 p=0.0000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.1000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.2000, Theta=0.000, if |C|< 100 then max|C|=5 p=0.3000, Theta=0.000, if |C|< 100 then max|C|=6 p=0.4000, Theta=0.000, if |C|< 100 then max|C|=31 p=0.5000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.6000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.7000, Theta=1.00, if |C|< 100 then max|C|=-Infinity :: sage: range_p = srange(0,0.8,0.1) sage: compute_percolation_probability(range_p, d=2, n=5, stop=100) # not tested d = 2, n = number of samples = 5 stop counting at = 100 p=0.0000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.1000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.2000, Theta=0.000, if |C|< 100 then max|C|=5 p=0.3000, Theta=0.000, if |C|< 100 then max|C|=6 p=0.4000, Theta=0.000, if |C|< 100 then max|C|=31 p=0.5000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.6000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.7000, Theta=1.00, if |C|< 100 then max|C|=-Infinity :: sage: range_p = srange(0.45,0.55,0.01) sage: compute_percolation_probability(range_p, d=2, n=10, stop=1000) # not tested d = 2, n = number of samples = 10 stop counting at = 1000 p=0.4500, Theta=0.000, if |C|< 1000 then max|C|=378 p=0.4600, Theta=0.000, if |C|< 1000 then max|C|=475 p=0.4700, Theta=0.000, if |C|< 1000 then max|C|=514 p=0.4800, Theta=0.100, if |C|< 1000 then max|C|=655 p=0.4900, Theta=0.700, if |C|< 1000 then max|C|=274 p=0.5000, Theta=0.700, if |C|< 1000 then max|C|=975 p=0.5100, Theta=0.700, if |C|< 1000 then max|C|=16 p=0.5200, Theta=0.700, if |C|< 1000 then max|C|=125 p=0.5300, Theta=0.900, if |C|< 1000 then max|C|=4 p=0.5400, Theta=0.700, if |C|< 1000 then max|C|=6 :: sage: range_p = srange(0.475,0.485,0.001) sage: compute_percolation_probability(range_p, d=2, n=10, stop=1000) # not tested d = 2, n = number of samples = 10 stop counting at = 1000 p=0.4750, Theta=0.200, if |C|< 1000 then max|C|=718 p=0.4760, Theta=0.200, if |C|< 1000 then max|C|=844 p=0.4770, Theta=0.200, if |C|< 1000 then max|C|=566 p=0.4780, Theta=0.500, if |C|< 1000 then max|C|=257 p=0.4790, Theta=0.200, if |C|< 1000 then max|C|=566 p=0.4800, Theta=0.300, if |C|< 1000 then max|C|=544 p=0.4810, Theta=0.300, if |C|< 1000 then max|C|=778 p=0.4820, Theta=0.500, if |C|< 1000 then max|C|=983 p=0.4830, Theta=0.300, if |C|< 1000 then max|C|=473 p=0.4840, Theta=0.500, if |C|< 1000 then max|C|=411 :: sage: range_p = srange(0.47,0.48,0.001) sage: compute_percolation_probability(range_p, d=2, n=20, stop=2000) # not tested d = 2, n = number of samples = 20 stop counting at = 2000 p=0.4700, Theta=0.0500, if |C|< 2000 then max|C|=1666 p=0.4710, Theta=0.100, if |C|< 2000 then max|C|=1665 p=0.4720, Theta=0.000, if |C|< 2000 then max|C|=1798 p=0.4730, Theta=0.0500, if |C|< 2000 then max|C|=1717 p=0.4740, Theta=0.150, if |C|< 2000 then max|C|=1924 p=0.4750, Theta=0.150, if |C|< 2000 then max|C|=1893 p=0.4760, Theta=0.150, if |C|< 2000 then max|C|=1458 p=0.4770, Theta=0.150, if |C|< 2000 then max|C|=1573 p=0.4780, Theta=0.200, if |C|< 2000 then max|C|=1762 p=0.4790, Theta=0.250, if |C|< 2000 then max|C|=951 """ print "d = %s, n = number of samples = %s" % (d, n) print "stop counting at = %s" % stop for p in range_p: p = numerical_approx(p, digits=4) S = BondPercolationSamples(p, d, n) L = [a for a in S.cluster_cardinality(stop) if not isinstance(a, str)] Y = max(L) if L else -Infinity theta = S.percolation_probability(stop) print "p=%s, Theta=%s, if |C|< %s then max|C|=%s" % (p, theta, stop, Y)
def plot(self, chart=None, ambient_coords=None, mapping=None, prange=None, include_end_point=(True, True), end_point_offset=(0.001, 0.001), max_value=8, parameters=None, color='red', style='-', thickness=1, plot_points=75, label_axes=True, aspect_ratio='automatic'): r""" Plot the current curve (``self``) in a Cartesian graph based on the coordinates of some ambient chart. The curve is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The ambient chart's domain must overlap with the curve's codomain or with the codomain of the composite curve `\Phi\circ c`, where `c` is ``self`` and `\Phi` some manifold differential mapping (argument ``mapping`` below). INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, the default chart of the codomain of the curve (or of the curve composed with `\Phi`) is used - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi` (instance of :class:`~sage.geometry.manifolds.diffmapping.DiffMapping`) providing the link between ``self`` and the ambient chart ``chart`` (cf. above); if ``None``, the ambient chart is supposed to be defined on the codomain of the curve ``self``. - ``prange`` -- (default: ``None``) range of the curve parameter for the plot; if ``None``, the entire parameter range declared during the curve construction is considered (with -Infinity replaced by ``-max_value`` and +Infinity by ``max_value``) - ``include_end_point`` -- (default: ``(True, True)``) determines whether the end points of ``prange`` are included in the plot - ``end_point_offset`` -- (default: ``(0.001, 0.001)``) offsets from the end points when they are not included in the plot: if ``include_end_point[0] == False``, the minimal value of the curve parameter used for the plot is ``prange[0] + end_point_offset[0]``, while if ``include_end_point[1] == False``, the maximal value is ``prange[1] - end_point_offset[1]``. - ``max_value`` -- (default: 8) numerical value substituted to +Infinity if the latter is the upper bound of the parameter range; similarly ``-max_value`` is the numerical valued substituted for -Infinity - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of ``self`` - ``color`` -- (default: 'red') color of the drawn curve - ``style`` -- (default: '-') color of the drawn curve; NB: ``style`` is effective only for 2D plots - ``thickness`` -- (default: 1) thickness of the drawn curve - ``plot_points`` -- (default: 75) number of points to plot the curve - ``label_axes`` -- (default: ``True``) boolean determining whether the labels of the coordinate axes of ``chart`` shall be added to the graph; can be set to ``False`` if the graph is 3D and must be superposed with another graph. - ``aspect_ratio`` -- (default: 'automatic') aspect ratio of the plot; the default value ('automatic') applies only for 2D plots; for 3D plots, the default value is ``1`` instead. OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Plot of the lemniscate of Gerono:: sage: R2 = Manifold(2, 'R^2') sage: X.<x,y> = R2.chart() sage: R.<t> = RealLine() sage: c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c') sage: c.plot() # 2D plot Graphics object consisting of 1 graphics primitive Plot for a subinterval of the curve's domain:: sage: c.plot(prange=(0,pi)) Graphics object consisting of 1 graphics primitive Plot with various options:: sage: c.plot(color='green', style=':', thickness=3, aspect_ratio=1) Graphics object consisting of 1 graphics primitive Plot via a mapping to another manifold: loxodrome of a sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_mapping(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='F') sage: F.display() F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: c = S2.curve([2*atan(exp(-t/10)), t], (t, -oo, +oo), name='c') sage: graph_c = c.plot(mapping=F, max_value=40, ....: plot_points=200, thickness=2, label_axes=False) # 3D plot sage: graph_S2 = XS.plot(X3, mapping=F, nb_values=11, color='black') # plot of the sphere sage: show(graph_c + graph_S2) # the loxodrome + the sphere Example of use of the argument ``parameters``: we define a curve with some symbolic parameters ``a`` and ``b``:: sage: a, b = var('a b') sage: c = R2.curve([a*cos(t) + b, a*sin(t)], (t, 0, 2*pi), name='c') To make a plot, we set spectific values for ``a`` and ``b`` by means of the Python dictionary ``parameters``:: sage: c.plot(parameters={a: 2, b: -3}, aspect_ratio=1) Graphics object consisting of 1 graphics primitive """ from sage.rings.infinity import Infinity from sage.misc.functional import numerical_approx from sage.plot.graphics import Graphics from sage.plot.line import line from sage.geometry.manifolds.chart import Chart from sage.geometry.manifolds.utilities import set_axes_labels # # The "effective" curve to be plotted # if mapping is None: eff_curve = self else: eff_curve = mapping.restrict(self.codomain()) * self # # The chart w.r.t. which the curve is plotted # if chart is None: chart = eff_curve._codomain.default_chart() elif not isinstance(chart, Chart): raise TypeError("{} is not a chart".format(chart)) # # Coordinates of the above chart w.r.t. which the curve is plotted # if ambient_coords is None: ambient_coords = chart[:] # all chart coordinates are used n_pc = len(ambient_coords) if n_pc != 2 and n_pc !=3: raise ValueError("The number of coordinates involved in the " + "plot must be either 2 or 3, not {}".format(n_pc)) ind_pc = [chart[:].index(pc) for pc in ambient_coords] # indices of plot # coordinates # # Parameter range for the plot # if prange is None: prange = (self._domain.lower_bound(), self._domain.upper_bound()) elif not isinstance(prange, (tuple, list)): raise TypeError("{} is neither a tuple nor a list".format(prange)) elif len(prange) != 2: raise ValueError("the argument prange must be a tuple/list " + "of 2 elements") tmin = prange[0] tmax = prange[1] if tmin == -Infinity: tmin = -max_value elif not include_end_point[0]: tmin = tmin + end_point_offset[0] if tmax == Infinity: tmax = max_value elif not include_end_point[1]: tmax = tmax - end_point_offset[1] tmin = numerical_approx(tmin) tmax = numerical_approx(tmax) # # The coordinate expression of the effective curve # canon_chart = self._domain.canonical_chart() transf = None for chart_pair in eff_curve._coord_expression: if chart_pair == (canon_chart, chart): transf = eff_curve._coord_expression[chart_pair] break else: # Search for a subchart for chart_pair in eff_curve._coord_expression: for schart in chart._subcharts: if chart_pair == (canon_chart, schart): transf = eff_curve._coord_expression[chart_pair] if transf is None: raise ValueError("No expression has been found for " + "{} in terms of {}".format(self, format)) # # List of points for the plot curve # plot_curve = [] dt = (tmax - tmin) / (plot_points - 1) t = tmin if parameters is None: for i in range(plot_points): x = transf(t, simplify=False) plot_curve.append( [numerical_approx(x[j]) for j in ind_pc] ) t += dt else: for i in range(plot_points): x = transf(t, simplify=False) plot_curve.append( [numerical_approx( x[j].substitute(parameters) ) for j in ind_pc] ) t += dt # # The plot # resu = Graphics() resu += line(plot_curve, color=color, linestyle=style, thickness=thickness) if n_pc==2: # 2D graphic resu.set_aspect_ratio(aspect_ratio) if label_axes: # We update the dictionary _extra_kwds (options to be passed # to show()), instead of using the method # Graphics.axes_labels() since the latter is not robust w.r.t. # graph addition resu._extra_kwds['axes_labels'] = [r'$'+latex(pc)+r'$' for pc in ambient_coords] else: # 3D graphic if aspect_ratio == 'automatic': aspect_ratio = 1 resu.aspect_ratio(aspect_ratio) if label_axes: labels = [str(pc) for pc in ambient_coords] resu = set_axes_labels(resu, *labels) return resu
def plot(self, chart=None, ambient_coords=None, mapping=None, chart_domain=None, fixed_coords=None, ranges=None, max_value=8, nb_values=None, steps=None, scale=1, color='blue', parameters=None, label_axes=True, **extra_options): r""" Plot the vector field in a Cartesian graph based on the coordinates of some ambient chart. The vector field is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The vector field's base points `p` (or their images `\Phi(p)` by some differentiable mapping `\Phi`) must lie in the ambient chart's domain. INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, the default chart of the vector field's domain is used - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi` (instance of :class:`~sage.geometry.manifolds.diffmapping.DiffMapping`) providing the link between the vector field's domain and the ambient chart ``chart``; if ``None``, the identity mapping is assumed - ``chart_domain`` -- (default: ``None``) chart on the vector field's domain to define the points at which vector arrows are to be plotted; if ``None``, the default chart of the vector field's domain is used - ``fixed_coords`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` that are kept fixed and with values the value of these coordinates; if ``None``, all the coordinates of ``chart_domain`` are used - ``ranges`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values tuples ``(x_min,x_max)`` specifying the coordinate range for the plot; if ``None``, the entire coordinate range declared during the construction of ``chart_domain`` is considered (with ``-Infinity`` replaced by ``-max_value`` and ``+Infinity`` by ``max_value``) - ``max_value`` -- (default: 8) numerical value substituted to ``+Infinity`` if the latter is the upper bound of the range of a coordinate for which the plot is performed over the entire coordinate range (i.e. for which no specific plot range has been set in ``ranges``); similarly ``-max_value`` is the numerical valued substituted for ``-Infinity`` - ``nb_values`` -- (default: ``None``) either an integer or a dictionary with keys the coordinates of ``chart_domain`` to be used and values the number of values of the coordinate for sampling the part of the vector field's domain involved in the plot ; if ``nb_values`` is a single integer, it represents the number of values for all coordinates; if ``nb_values`` is ``None``, it is set to 9 for a 2D plot and to 5 for a 3D plot - ``steps`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values the step between each constant value of the coordinate; if ``None``, the step is computed from the coordinate range (specified in ``ranges``) and ``nb_values``. On the contrary, if the step is provided for some coordinate, the corresponding number of values is deduced from it and the coordinate range. - ``scale`` -- (default: 1) value by which the lengths of the arrows representing the vectors is multiplied - ``color`` -- (default: 'blue') color of the arrows representing the vectors - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of the vector field (see example below) - ``label_axes`` -- (default: ``True``) boolean determining whether the labels of the coordinate axes of ``chart`` shall be added to the graph; can be set to ``False`` if the graph is 3D and must be superposed with another graph. - ``**extra_options`` -- extra options for the arrow plot, like ``linestyle``, ``width`` or ``arrowsize`` (see :func:`~sage.plot.arrow.arrow2d` and :func:`~sage.plot.plot3d.shapes.arrow3d` for details) OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Plot of a vector field on a 2-dimensional manifold:: sage: Manifold._clear_cache_() # for doctests only sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = -y, x ; v.display() v = -y d/dx + x d/dy sage: v.plot() Graphics object consisting of 80 graphics primitives Plot with various options:: sage: v.plot(scale=0.5, color='green', linestyle='--', width=1, arrowsize=6) Graphics object consisting of 80 graphics primitives sage: v.plot(max_value=4, nb_values=5, scale=0.5) Graphics object consisting of 24 graphics primitives Plots along a line of fixed coordinate:: sage: v.plot(fixed_coords={x: -2}) Graphics object consisting of 9 graphics primitives sage: v.plot(fixed_coords={y: 1}) Graphics object consisting of 9 graphics primitives Let us now consider a vector field on a 4-dimensional manifold:: sage: Manifold._clear_cache_() # for doctests only sage: M = Manifold(4, 'M') sage: X.<t,x,y,z> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = (t/8)^2, -t*y/4, t*x/4, t*z/4 ; v.display() v = 1/64*t^2 d/dt - 1/4*t*y d/dx + 1/4*t*x d/dy + 1/4*t*z d/dz We cannot make a 4D plot directly:: sage: v.plot() Traceback (most recent call last): ... ValueError: the number of ambient coordinates must be either 2 or 3, not 4 Rather, we have to select some coordinates for the plot, via the argument ``ambient_coords``. For instance, for a 3D plot:: sage: v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1}) Graphics3d Object sage: v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0}, ....: ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, nb_values=4) Graphics3d Object or, for a 2D plot:: sage: v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0}) Graphics object consisting of 80 graphics primitives sage: v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0}) Graphics object consisting of 72 graphics primitives An example of plot via a differential mapping: plot of a vector field tangent to a 2-sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') # the open set covered by spherical coord. sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_mapping(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='F') sage: F.display() # the standard embedding of S^2 into R^3 F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: v = XS.frame()[1] ; v vector field 'd/dph' on the open subset 'U' of the 2-dimensional manifold 'S^2' sage: graph_v = v.plot(chart=X3, mapping=F, label_axes=False) sage: graph_S2 = XS.plot(chart=X3, mapping=F, nb_values=9) sage: show(graph_v + graph_S2) """ from sage.rings.infinity import Infinity from sage.misc.functional import numerical_approx from sage.misc.latex import latex from sage.plot.graphics import Graphics from sage.geometry.manifolds.chart import Chart from sage.geometry.manifolds.utilities import set_axes_labels # # 1/ Treatment of input parameters # ----------------------------- if chart is None: chart = self._domain.default_chart() elif not isinstance(chart, Chart): raise TypeError("{} is not a chart".format(chart)) if chart_domain is None: chart_domain = self._domain.default_chart() elif not isinstance(chart_domain, Chart): raise TypeError("{} is not a chart".format(chart_domain)) elif not chart_domain.domain().is_subset(self._domain): raise ValueError("The domain of {} is not ".format(chart_domain) + "included in the domain of {}".format(self)) if fixed_coords is None: coords = chart_domain._xx else: fixed_coord_list = fixed_coords.keys() coords = [] for coord in chart_domain._xx: if coord not in fixed_coord_list: coords.append(coord) coords = tuple(coords) if ambient_coords is None: ambient_coords = chart[:] elif not isinstance(ambient_coords, tuple): ambient_coords = tuple(ambient_coords) nca = len(ambient_coords) if nca != 2 and nca != 3: raise ValueError("the number of ambient coordinates must be " + "either 2 or 3, not {}".format(nca)) if ranges is None: ranges = {} ranges0 = {} for coord in coords: if coord in ranges: ranges0[coord] = (numerical_approx(ranges[coord][0]), numerical_approx(ranges[coord][1])) else: bounds = chart_domain._bounds[chart_domain[:].index(coord)] if bounds[0][0] == -Infinity: xmin = numerical_approx(-max_value) elif bounds[0][1]: xmin = numerical_approx(bounds[0][0]) else: xmin = numerical_approx(bounds[0][0] + 1.e-3) if bounds[1][0] == Infinity: xmax = numerical_approx(max_value) elif bounds[1][1]: xmax = numerical_approx(bounds[1][0]) else: xmax = numerical_approx(bounds[1][0] - 1.e-3) ranges0[coord] = (xmin, xmax) ranges = ranges0 if nb_values is None: if nca == 2: # 2D plot nb_values = 9 else: # 3D plot nb_values = 5 if not isinstance(nb_values, dict): nb_values0 = {} for coord in coords: nb_values0[coord] = nb_values nb_values = nb_values0 if steps is None: steps = {} for coord in coords: if coord not in steps: steps[coord] = (ranges[coord][1] - ranges[coord][0])/ \ (nb_values[coord]-1) else: nb_values[coord] = 1 + int( (ranges[coord][1] - ranges[coord][0]) / steps[coord]) # # 2/ Plots # ----- dom = chart_domain.domain() nc = len(chart_domain[:]) ncp = len(coords) xx = [0] * nc if fixed_coords is not None: if len(fixed_coords) != nc - ncp: raise ValueError("Bad number of fixed coordinates.") for fc, val in fixed_coords.iteritems(): xx[chart_domain[:].index(fc)] = val index_p = [chart_domain[:].index(cd) for cd in coords] resu = Graphics() ind = [0] * ncp ind_max = [0] * ncp ind_max[0] = nb_values[coords[0]] xmin = [ranges[cd][0] for cd in coords] step_tab = [steps[cd] for cd in coords] while ind != ind_max: for i in range(ncp): xx[index_p[i]] = xmin[i] + ind[i] * step_tab[i] if chart_domain.valid_coordinates(*xx, tolerance=1e-13, parameters=parameters): point = dom(xx, chart=chart_domain) resu += self.at(point).plot(chart=chart, ambient_coords=ambient_coords, mapping=mapping, scale=scale, color=color, print_label=False, parameters=parameters, **extra_options) # Next index: ret = 1 for pos in range(ncp - 1, -1, -1): imax = nb_values[coords[pos]] - 1 if ind[pos] != imax: ind[pos] += ret ret = 0 elif ret == 1: if pos == 0: ind[pos] = imax + 1 # end point reached else: ind[pos] = 0 ret = 1 if label_axes: if nca == 2: # 2D graphic # We update the dictionary _extra_kwds (options to be passed # to show()), instead of using the method # Graphics.axes_labels() since the latter is not robust w.r.t. # graph addition resu._extra_kwds['axes_labels'] = [ r'$' + latex(ac) + r'$' for ac in ambient_coords ] else: # 3D graphic labels = [str(ac) for ac in ambient_coords] resu = set_axes_labels(resu, *labels) return resu
def isogeny_degree4k_strategy(self, Q, k, method, stop=0): ''' INPUT: * self the point defining the kernel of the isogeny, of degree 4**k * Q a point that we want to evaluate * k such that the isogeny is of degree 4**k * method a string defining the method to use : withKernel4, withKernel4k or withoutKernel OUTPUT: * phiQ the image of Q * phiQ4k a point generating the dual isogeny kernel (if method = 'kernel4k') * listOfCurves the list of 4-isogenous curvesq (if method = 'kernel4') REMARKS: * self needs to be such that [4**(k-1)] self has x-coordinate != +/- 1. ''' l = k phiP4k = self curve_prime = copy(self.curve) image_points = [Q] + [self] listOfCurves_a = [] if method == 'kernel4k': Q4k = self.dual_kernel_point(k) #evaluate Q4k give the kernel of the dual isogeny image_points += [Q4k] Queue1 = deque() Queue1.append([k, self]) PRINTCOUNTER = 0 DEC = 0 i = 0 F = self.curve list1 = copy(image_points) while len(Queue1) != 0 and l > stop: [h, P] = Queue1.pop() if h == 1: Queue2 = deque() while len(Queue1) != 0: [h, Q] = Queue1.popleft() [Q] = P.isogeny_degree4([Q]) Queue2.append([h - 1, Q]) Queue1 = Queue2 list1 = P.isogeny_degree4(list1) PRINTCOUNTER += 1 if numerical_approx(100 * PRINTCOUNTER / k) > DEC: DEC += 10 if k > 50: print('%d\% of the (big) step' % floor(100 * PRINTCOUNTER / k)) F = list1[0].curve if method == 'kernel4': listOfCurves_a.append(F) l -= 1 elif self.curve.strategy[i] > 0 and self.curve.strategy[i] < h: Queue1.append([h, P]) P = 4**(self.curve.strategy[i]) * P Queue1.append([h - self.curve.strategy[i], P]) i += 1 else: return false #output phiQ = list1[0] if method == 'kernel4k': # the point defining the dual is the 3rd one of image_points evaluated by the 4-isogenies phiQ4k = list1[2] else: phiQ4k = None if method != 'kernel4': listOfCurves_a = None return [phiQ, phiQ4k, listOfCurves_a]
def plot(self, chart=None, ambient_coords=None, mapping=None, chart_domain=None, fixed_coords=None, ranges=None, number_values=None, steps=None, parameters=None, label_axes=True, **extra_options): r""" Plot the vector field in a Cartesian graph based on the coordinates of some ambient chart. The vector field is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The vector field's base points `p` (or their images `\Phi(p)` by some differentiable mapping `\Phi`) must lie in the ambient chart's domain. INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, the default chart of the vector field's domain is used - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- :class:`~sage.manifolds.differentiable.diff_map.DiffMap` (default: ``None``); differentiable map `\Phi` providing the link between the vector field's domain and the ambient chart ``chart``; if ``None``, the identity map is assumed - ``chart_domain`` -- (default: ``None``) chart on the vector field's domain to define the points at which vector arrows are to be plotted; if ``None``, the default chart of the vector field's domain is used - ``fixed_coords`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` that are kept fixed and with values the value of these coordinates; if ``None``, all the coordinates of ``chart_domain`` are used - ``ranges`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values tuples ``(x_min, x_max)`` specifying the coordinate range for the plot; if ``None``, the entire coordinate range declared during the construction of ``chart_domain`` is considered (with ``-Infinity`` replaced by ``-max_range`` and ``+Infinity`` by ``max_range``) - ``number_values`` -- (default: ``None``) either an integer or a dictionary with keys the coordinates of ``chart_domain`` to be used and values the number of values of the coordinate for sampling the part of the vector field's domain involved in the plot ; if ``number_values`` is a single integer, it represents the number of values for all coordinates; if ``number_values`` is ``None``, it is set to 9 for a 2D plot and to 5 for a 3D plot - ``steps`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values the step between each constant value of the coordinate; if ``None``, the step is computed from the coordinate range (specified in ``ranges``) and ``number_values``; on the contrary, if the step is provided for some coordinate, the corresponding number of values is deduced from it and the coordinate range - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of the vector field (see example below) - ``label_axes`` -- (default: ``True``) boolean determining whether the labels of the coordinate axes of ``chart`` shall be added to the graph; can be set to ``False`` if the graph is 3D and must be superposed with another graph - ``color`` -- (default: 'blue') color of the arrows representing the vectors - ``max_range`` -- (default: 8) numerical value substituted to ``+Infinity`` if the latter is the upper bound of the range of a coordinate for which the plot is performed over the entire coordinate range (i.e. for which no specific plot range has been set in ``ranges``); similarly ``-max_range`` is the numerical valued substituted for ``-Infinity`` - ``scale`` -- (default: 1) value by which the lengths of the arrows representing the vectors is multiplied - ``**extra_options`` -- extra options for the arrow plot, like ``linestyle``, ``width`` or ``arrowsize`` (see :func:`~sage.plot.arrow.arrow2d` and :func:`~sage.plot.plot3d.shapes.arrow3d` for details) OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Plot of a vector field on a 2-dimensional manifold:: sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = -y, x ; v.display() v = -y d/dx + x d/dy sage: v.plot() Graphics object consisting of 80 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot() sphinx_plot(g) Plot with various options:: sage: v.plot(scale=0.5, color='green', linestyle='--', width=1, ....: arrowsize=6) Graphics object consisting of 80 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(scale=0.5, color='green', linestyle='--', width=1, arrowsize=6) sphinx_plot(g) :: sage: v.plot(max_range=4, number_values=5, scale=0.5) Graphics object consisting of 24 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(max_range=4, number_values=5, scale=0.5) sphinx_plot(g) Plot using parallel computation:: sage: Parallelism().set(nproc=2) sage: v.plot(scale=0.5, number_values=10, linestyle='--', width=1, ....: arrowsize=6) Graphics object consisting of 100 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(scale=0.5, number_values=10, linestyle='--', width=1, arrowsize=6) sphinx_plot(g) :: sage: Parallelism().set(nproc=1) # switch off parallelization Plots along a line of fixed coordinate:: sage: v.plot(fixed_coords={x: -2}) Graphics object consisting of 9 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(fixed_coords={x: -2}) sphinx_plot(g) :: sage: v.plot(fixed_coords={y: 1}) Graphics object consisting of 9 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(fixed_coords={y: 1}) sphinx_plot(g) Let us now consider a vector field on a 4-dimensional manifold:: sage: M = Manifold(4, 'M') sage: X.<t,x,y,z> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = (t/8)^2, -t*y/4, t*x/4, t*z/4 ; v.display() v = 1/64*t^2 d/dt - 1/4*t*y d/dx + 1/4*t*x d/dy + 1/4*t*z d/dz We cannot make a 4D plot directly:: sage: v.plot() Traceback (most recent call last): ... ValueError: the number of ambient coordinates must be either 2 or 3, not 4 Rather, we have to select some coordinates for the plot, via the argument ``ambient_coords``. For instance, for a 3D plot:: sage: v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1}, # long time ....: number_values=4) Graphics3d Object .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z') ; t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 sphinx_plot(v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1}, number_values=4)) :: sage: v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0}, # long time ....: ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, ....: number_values=4) Graphics3d Object .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 sphinx_plot(v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0}, ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, number_values=4)) or, for a 2D plot:: sage: v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0}) # long time Graphics object consisting of 80 graphics primitives .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 g = v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0}) sphinx_plot(g) :: sage: v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0}) # long time Graphics object consisting of 72 graphics primitives .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 g = v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0}) sphinx_plot(g) An example of plot via a differential mapping: plot of a vector field tangent to a 2-sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') # the open set covered by spherical coord. sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='F') sage: F.display() # the standard embedding of S^2 into R^3 F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: v = XS.frame()[1] ; v # the coordinate vector d/dphi Vector field d/dph on the Open subset U of the 2-dimensional differentiable manifold S^2 sage: graph_v = v.plot(chart=X3, mapping=F, label_axes=False) sage: graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) sage: graph_v + graph_S2 Graphics3d Object .. PLOT:: S2 = Manifold(2, 'S^2') U = S2.open_subset('U') XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') th, ph = XS[:] R3 = Manifold(3, 'R^3') X3 = R3.chart('x y z') F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)]}, name='F') v = XS.frame()[1] graph_v = v.plot(chart=X3, mapping=F, label_axes=False) graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) sphinx_plot(graph_v + graph_S2) Note that the default values of some arguments of the method ``plot`` are stored in the dictionary ``plot.options``:: sage: v.plot.options # random (dictionary output) {'color': 'blue', 'max_range': 8, 'scale': 1} so that they can be adjusted by the user:: sage: v.plot.options['color'] = 'red' From now on, all plots of vector fields will use red as the default color. To restore the original default options, it suffices to type:: sage: v.plot.reset() """ from sage.rings.infinity import Infinity from sage.misc.functional import numerical_approx from sage.misc.latex import latex from sage.plot.graphics import Graphics from sage.manifolds.chart import RealChart from sage.manifolds.utilities import set_axes_labels from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism # # 1/ Treatment of input parameters # ----------------------------- max_range = extra_options.pop("max_range") scale = extra_options.pop("scale") color = extra_options.pop("color") if chart is None: chart = self._domain.default_chart() elif not isinstance(chart, RealChart): raise TypeError("{} is not a chart on a real ".format(chart) + "manifold") if chart_domain is None: chart_domain = self._domain.default_chart() elif not isinstance(chart_domain, RealChart): raise TypeError("{} is not a chart on a ".format(chart_domain) + "real manifold") elif not chart_domain.domain().is_subset(self._domain): raise ValueError("the domain of {} is not ".format(chart_domain) + "included in the domain of {}".format(self)) coords_full = tuple(chart_domain[:]) # all coordinates of chart_domain if fixed_coords is None: coords = coords_full else: fixed_coord_list = fixed_coords.keys() coords = [] for coord in coords_full: if coord not in fixed_coord_list: coords.append(coord) coords = tuple(coords) if ambient_coords is None: ambient_coords = chart[:] elif not isinstance(ambient_coords, tuple): ambient_coords = tuple(ambient_coords) nca = len(ambient_coords) if nca != 2 and nca != 3: raise ValueError("the number of ambient coordinates must be " + "either 2 or 3, not {}".format(nca)) if ranges is None: ranges = {} ranges0 = {} for coord in coords: if coord in ranges: ranges0[coord] = (numerical_approx(ranges[coord][0]), numerical_approx(ranges[coord][1])) else: bounds = chart_domain._bounds[coords_full.index(coord)] xmin0 = bounds[0][0] xmax0 = bounds[1][0] if xmin0 == -Infinity: xmin = numerical_approx(-max_range) elif bounds[0][1]: xmin = numerical_approx(xmin0) else: xmin = numerical_approx(xmin0 + 1.e-3) if xmax0 == Infinity: xmax = numerical_approx(max_range) elif bounds[1][1]: xmax = numerical_approx(xmax0) else: xmax = numerical_approx(xmax0 - 1.e-3) ranges0[coord] = (xmin, xmax) ranges = ranges0 if number_values is None: if nca == 2: # 2D plot number_values = 9 else: # 3D plot number_values = 5 if not isinstance(number_values, dict): number_values0 = {} for coord in coords: number_values0[coord] = number_values number_values = number_values0 if steps is None: steps = {} for coord in coords: if coord not in steps: steps[coord] = (ranges[coord][1] - ranges[coord][0])/ \ (number_values[coord]-1) else: number_values[coord] = 1 + int( (ranges[coord][1] - ranges[coord][0]) / steps[coord]) # # 2/ Plots # ----- dom = chart_domain.domain() vector = self.restrict(dom) if vector.parent().destination_map() is dom.identity_map(): if mapping is not None: vector = mapping.pushforward(vector) mapping = None nc = len(coords_full) ncp = len(coords) xx = [0] * nc if fixed_coords is not None: if len(fixed_coords) != nc - ncp: raise ValueError("bad number of fixed coordinates") for fc, val in fixed_coords.items(): xx[coords_full.index(fc)] = val ind_coord = [] for coord in coords: ind_coord.append(coords_full.index(coord)) resu = Graphics() ind = [0] * ncp ind_max = [0] * ncp ind_max[0] = number_values[coords[0]] xmin = [ranges[cd][0] for cd in coords] step_tab = [steps[cd] for cd in coords] nproc = Parallelism().get('tensor') if nproc != 1 and nca == 2: # parallel plot construct : Only for 2D plot (at moment) ! # creation of the list of parameters list_xx = [] while ind != ind_max: for i in range(ncp): xx[ind_coord[i]] = xmin[i] + ind[i] * step_tab[i] if chart_domain.valid_coordinates(*xx, tolerance=1e-13, parameters=parameters): # needed a xx*1 to copy the list by value list_xx.append(xx * 1) # Next index: ret = 1 for pos in range(ncp - 1, -1, -1): imax = number_values[coords[pos]] - 1 if ind[pos] != imax: ind[pos] += ret ret = 0 elif ret == 1: if pos == 0: ind[pos] = imax + 1 # end point reached else: ind[pos] = 0 ret = 1 lol = lambda lst, sz: [ lst[i:i + sz] for i in range(0, len(lst), sz) ] ind_step = max(1, int(len(list_xx) / nproc / 2)) local_list = lol(list_xx, ind_step) # definition of the list of input parameters listParalInput = [ (vector, dom, ind_part, chart_domain, chart, ambient_coords, mapping, scale, color, parameters, extra_options) for ind_part in local_list ] # definition of the parallel function @parallel(p_iter='multiprocessing', ncpus=nproc) def add_point_plot(vector, dom, xx_list, chart_domain, chart, ambient_coords, mapping, scale, color, parameters, extra_options): count = 0 for xx in xx_list: point = dom(xx, chart=chart_domain) part = vector.at(point).plot(chart=chart, ambient_coords=ambient_coords, mapping=mapping, scale=scale, color=color, print_label=False, parameters=parameters, **extra_options) if count == 0: local_resu = part else: local_resu += part count += 1 return local_resu # parallel execution and reconstruction of the plot for ii, val in add_point_plot(listParalInput): resu += val else: # sequential plot while ind != ind_max: for i in range(ncp): xx[ind_coord[i]] = xmin[i] + ind[i] * step_tab[i] if chart_domain.valid_coordinates(*xx, tolerance=1e-13, parameters=parameters): point = dom(xx, chart=chart_domain) resu += vector.at(point).plot( chart=chart, ambient_coords=ambient_coords, mapping=mapping, scale=scale, color=color, print_label=False, parameters=parameters, **extra_options) # Next index: ret = 1 for pos in range(ncp - 1, -1, -1): imax = number_values[coords[pos]] - 1 if ind[pos] != imax: ind[pos] += ret ret = 0 elif ret == 1: if pos == 0: ind[pos] = imax + 1 # end point reached else: ind[pos] = 0 ret = 1 if label_axes: if nca == 2: # 2D graphic # We update the dictionary _extra_kwds (options to be passed # to show()), instead of using the method # Graphics.axes_labels() since the latter is not robust w.r.t. # graph addition resu._extra_kwds['axes_labels'] = [ r'$' + latex(ac) + r'$' for ac in ambient_coords ] else: # 3D graphic labels = [str(ac) for ac in ambient_coords] resu = set_axes_labels(resu, *labels) return resu
def plot(self, chart=None, ambient_coords=None, mapping=None, color='blue', print_label=True, label=None, label_color=None, fontsize=10, label_offset=0.1, parameters=None, **extra_options): r""" Plot the vector in a Cartesian graph based on the coordinates of some ambient chart. The vector is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The vector's base point `p` (or its image `\Phi(p)` by some differentiable mapping `\Phi`) must lie in the ambient chart's domain. If `\Phi` is different from the identity mapping, the vector actually depicted is `\mathrm{d}\Phi_p(v)`, where `v` is the current vector (``self``) (see the example of a vector tangent to the 2-sphere below, where `\Phi: S^2 \to \RR^3`). INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, it is set to the default chart of the open set containing the point at which the vector (or the vector image via the differential `\mathrm{d}\Phi_p` of ``mapping``) is defined - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- (default: ``None``) :class:`~sage.manifolds.differentiable.diff_map.DiffMap`; differentiable mapping `\Phi` providing the link between the point `p` at which the vector is defined and the ambient chart ``chart``: the domain of ``chart`` must contain `\Phi(p)`; if ``None``, the identity mapping is assumed - ``scale`` -- (default: 1) value by which the length of the arrow representing the vector is multiplied - ``color`` -- (default: 'blue') color of the arrow representing the vector - ``print_label`` -- (boolean; default: ``True``) determines whether a label is printed next to the arrow representing the vector - ``label`` -- (string; default: ``None``) label printed next to the arrow representing the vector; if ``None``, the vector's symbol is used, if any - ``label_color`` -- (default: ``None``) color to print the label; if ``None``, the value of ``color`` is used - ``fontsize`` -- (default: 10) size of the font used to print the label - ``label_offset`` -- (default: 0.1) determines the separation between the vector arrow and the label - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of ``self`` (see example below) - ``**extra_options`` -- extra options for the arrow plot, like ``linestyle``, ``width`` or ``arrowsize`` (see :func:`~sage.plot.arrow.arrow2d` and :func:`~sage.plot.plot3d.shapes.arrow3d` for details) OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Vector tangent to a 2-dimensional manifold:: sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: p = M((2,2), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((2, 1), name='v') ; v Tangent vector v at Point p on the 2-dimensional differentiable manifold M Plot of the vector alone (arrow + label):: sage: v.plot() Graphics object consisting of 2 graphics primitives Plot atop of the chart grid:: sage: X.plot() + v.plot() Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot() sphinx_plot(g) Plots with various options:: sage: X.plot() + v.plot(color='green', scale=2, label='V') Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(color='green', scale=2, label='V') sphinx_plot(g) :: sage: X.plot() + v.plot(print_label=False) Graphics object consisting of 19 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(print_label=False) sphinx_plot(g) :: sage: X.plot() + v.plot(color='green', label_color='black', ....: fontsize=20, label_offset=0.2) Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(color='green', label_color='black', fontsize=20, label_offset=0.2) sphinx_plot(g) :: sage: X.plot() + v.plot(linestyle=':', width=4, arrowsize=8, ....: fontsize=20) Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(linestyle=':', width=4, arrowsize=8, fontsize=20) sphinx_plot(g) Plot with specific values of some free parameters:: sage: var('a b') (a, b) sage: v = Tp((1+a, -b^2), name='v') ; v.display() v = (a + 1) d/dx - b^2 d/dy sage: X.plot() + v.plot(parameters={a: -2, b: 3}) Graphics object consisting of 20 graphics primitives Special case of the zero vector:: sage: v = Tp.zero() ; v Tangent vector zero at Point p on the 2-dimensional differentiable manifold M sage: X.plot() + v.plot() Graphics object consisting of 19 graphics primitives Vector tangent to a 4-dimensional manifold:: sage: M = Manifold(4, 'M') sage: X.<t,x,y,z> = M.chart() sage: p = M((0,1,2,3), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((5,4,3,2), name='v') ; v Tangent vector v at Point p on the 4-dimensional differentiable manifold M We cannot make a 4D plot directly:: sage: v.plot() Traceback (most recent call last): ... ValueError: the number of coordinates involved in the plot must be either 2 or 3, not 4 Rather, we have to select some chart coordinates for the plot, via the argument ``ambient_coords``. For instance, for a 2-dimensional plot in terms of the coordinates `(x, y)`:: sage: v.plot(ambient_coords=(x,y)) Graphics object consisting of 2 graphics primitives .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] p = M((0,1,2,3), name='p'); Tp = M.tangent_space(p) v = Tp((5,4,3,2), name='v') g = X.plot(ambient_coords=(x,y)) + v.plot(ambient_coords=(x,y)) sphinx_plot(g) This plot involves only the components `v^x` and `v^y` of `v`. Similarly, for a 3-dimensional plot in terms of the coordinates `(t, x, y)`:: sage: g = v.plot(ambient_coords=(t,x,z)) sage: print(g) Graphics3d Object This plot involves only the components `v^t`, `v^x` and `v^z` of `v`. A nice 3D view atop the coordinate grid is obtained via:: sage: (X.plot(ambient_coords=(t,x,z)) # long time ....: + v.plot(ambient_coords=(t,x,z), ....: label_offset=0.5, width=6)) Graphics3d Object .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] p = M((0,1,2,3), name='p'); Tp = M.tangent_space(p) v = Tp((5,4,3,2), name='v') g = X.plot(ambient_coords=(t,x,z)) + v.plot(ambient_coords=(t,x,z), label_offset=0.5, width=6) sphinx_plot(g) An example of plot via a differential mapping: plot of a vector tangent to a 2-sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') # the open set covered by spherical coord. sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), ....: cos(th)]}, name='F') sage: F.display() # the standard embedding of S^2 into R^3 F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: p = U.point((pi/4, 7*pi/4), name='p') sage: v = XS.frame()[1].at(p) ; v # the coordinate vector d/dphi at p Tangent vector d/dph at Point p on the 2-dimensional differentiable manifold S^2 sage: graph_v = v.plot(mapping=F) sage: graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) # long time sage: graph_v + graph_S2 # long time Graphics3d Object .. PLOT:: S2 = Manifold(2, 'S^2') U = S2.open_subset('U') XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') th, ph = XS[:] R3 = Manifold(3, 'R^3') X3 = R3.chart('x y z') F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)]}, name='F') p = U.point((pi/4, 7*pi/4), name='p') v = XS.frame()[1].at(p) graph_v = v.plot(mapping=F) graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) sphinx_plot(graph_v + graph_S2) """ from sage.plot.arrow import arrow2d from sage.plot.text import text from sage.plot.graphics import Graphics from sage.plot.plot3d.shapes import arrow3d from sage.plot.plot3d.shapes2 import text3d from sage.misc.functional import numerical_approx from sage.manifolds.differentiable.chart import DiffChart scale = extra_options.pop("scale") # # The "effective" vector to be plotted # if mapping is None: eff_vector = self base_point = self._point else: #!# check # For efficiency, the method FiniteRankFreeModuleMorphism._call_() # is called instead of FiniteRankFreeModuleMorphism.__call__() eff_vector = mapping.differential(self._point)._call_(self) base_point = mapping(self._point) # # The chart w.r.t. which the vector is plotted # if chart is None: chart = base_point.parent().default_chart() elif not isinstance(chart, DiffChart): raise TypeError("{} is not a chart".format(chart)) # # Coordinates of the above chart w.r.t. which the vector is plotted # if ambient_coords is None: ambient_coords = chart[:] # all chart coordinates are used n_pc = len(ambient_coords) if n_pc != 2 and n_pc != 3: raise ValueError("the number of coordinates involved in the " + "plot must be either 2 or 3, not {}".format(n_pc)) # indices coordinates involved in the plot: ind_pc = [chart[:].index(pc) for pc in ambient_coords] # # Components of the vector w.r.t. the chart frame # basis = chart.frame().at(base_point) vcomp = eff_vector.comp(basis=basis)[:] xp = base_point.coord(chart=chart) # # The arrow # resu = Graphics() if parameters is None: coord_tail = [numerical_approx(xp[i]) for i in ind_pc] coord_head = [ numerical_approx(xp[i] + scale * vcomp[i]) for i in ind_pc ] else: coord_tail = [ numerical_approx(xp[i].substitute(parameters)) for i in ind_pc ] coord_head = [ numerical_approx( (xp[i] + scale * vcomp[i]).substitute(parameters)) for i in ind_pc ] if coord_head != coord_tail: if n_pc == 2: resu += arrow2d(tailpoint=coord_tail, headpoint=coord_head, color=color, **extra_options) else: resu += arrow3d(coord_tail, coord_head, color=color, **extra_options) # # The label # if print_label: if label is None: if n_pc == 2 and self._latex_name is not None: label = r'$' + self._latex_name + r'$' if n_pc == 3 and self._name is not None: label = self._name if label is not None: xlab = [xh + label_offset for xh in coord_head] if label_color is None: label_color = color if n_pc == 2: resu += text(label, xlab, fontsize=fontsize, color=label_color) else: resu += text3d(label, xlab, fontsize=fontsize, color=label_color) return resu
def compute_percolation_probability(range_p, d, n, stop): r""" EXAMPLES:: sage: from slabbe.bond_percolation import compute_percolation_probability sage: compute_percolation_probability(srange(0,0.8,0.1), d=2, n=5, stop=100) # random d = 2, n = number of samples = 5 stop counting at = 100 p=0.0000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.1000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.2000, Theta=0.000, if |C|< 100 then max|C|=5 p=0.3000, Theta=0.000, if |C|< 100 then max|C|=6 p=0.4000, Theta=0.000, if |C|< 100 then max|C|=31 p=0.5000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.6000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.7000, Theta=1.00, if |C|< 100 then max|C|=-Infinity :: sage: range_p = srange(0,0.8,0.1) sage: compute_percolation_probability(range_p, d=2, n=5, stop=100) # not tested d = 2, n = number of samples = 5 stop counting at = 100 p=0.0000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.1000, Theta=0.000, if |C|< 100 then max|C|=1 p=0.2000, Theta=0.000, if |C|< 100 then max|C|=5 p=0.3000, Theta=0.000, if |C|< 100 then max|C|=6 p=0.4000, Theta=0.000, if |C|< 100 then max|C|=31 p=0.5000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.6000, Theta=1.00, if |C|< 100 then max|C|=-Infinity p=0.7000, Theta=1.00, if |C|< 100 then max|C|=-Infinity :: sage: range_p = srange(0.45,0.55,0.01) sage: compute_percolation_probability(range_p, d=2, n=10, stop=1000) # not tested d = 2, n = number of samples = 10 stop counting at = 1000 p=0.4500, Theta=0.000, if |C|< 1000 then max|C|=378 p=0.4600, Theta=0.000, if |C|< 1000 then max|C|=475 p=0.4700, Theta=0.000, if |C|< 1000 then max|C|=514 p=0.4800, Theta=0.100, if |C|< 1000 then max|C|=655 p=0.4900, Theta=0.700, if |C|< 1000 then max|C|=274 p=0.5000, Theta=0.700, if |C|< 1000 then max|C|=975 p=0.5100, Theta=0.700, if |C|< 1000 then max|C|=16 p=0.5200, Theta=0.700, if |C|< 1000 then max|C|=125 p=0.5300, Theta=0.900, if |C|< 1000 then max|C|=4 p=0.5400, Theta=0.700, if |C|< 1000 then max|C|=6 :: sage: range_p = srange(0.475,0.485,0.001) sage: compute_percolation_probability(range_p, d=2, n=10, stop=1000) # not tested d = 2, n = number of samples = 10 stop counting at = 1000 p=0.4750, Theta=0.200, if |C|< 1000 then max|C|=718 p=0.4760, Theta=0.200, if |C|< 1000 then max|C|=844 p=0.4770, Theta=0.200, if |C|< 1000 then max|C|=566 p=0.4780, Theta=0.500, if |C|< 1000 then max|C|=257 p=0.4790, Theta=0.200, if |C|< 1000 then max|C|=566 p=0.4800, Theta=0.300, if |C|< 1000 then max|C|=544 p=0.4810, Theta=0.300, if |C|< 1000 then max|C|=778 p=0.4820, Theta=0.500, if |C|< 1000 then max|C|=983 p=0.4830, Theta=0.300, if |C|< 1000 then max|C|=473 p=0.4840, Theta=0.500, if |C|< 1000 then max|C|=411 :: sage: range_p = srange(0.47,0.48,0.001) sage: compute_percolation_probability(range_p, d=2, n=20, stop=2000) # not tested d = 2, n = number of samples = 20 stop counting at = 2000 p=0.4700, Theta=0.0500, if |C|< 2000 then max|C|=1666 p=0.4710, Theta=0.100, if |C|< 2000 then max|C|=1665 p=0.4720, Theta=0.000, if |C|< 2000 then max|C|=1798 p=0.4730, Theta=0.0500, if |C|< 2000 then max|C|=1717 p=0.4740, Theta=0.150, if |C|< 2000 then max|C|=1924 p=0.4750, Theta=0.150, if |C|< 2000 then max|C|=1893 p=0.4760, Theta=0.150, if |C|< 2000 then max|C|=1458 p=0.4770, Theta=0.150, if |C|< 2000 then max|C|=1573 p=0.4780, Theta=0.200, if |C|< 2000 then max|C|=1762 p=0.4790, Theta=0.250, if |C|< 2000 then max|C|=951 """ print("d = %s, n = number of samples = %s" % (d, n)) print("stop counting at = %s" % stop) for p in range_p: p = numerical_approx(p, digits=4) S = BondPercolationSamples(p,d,n) L = [a for a in S.cluster_cardinality(stop) if not isinstance(a, str)] Y = max(L) if L else -Infinity theta = S.percolation_probability(stop) print("p=%s, Theta=%s, if |C|< %s then max|C|=%s" % (p, theta, stop, Y))
def lyap_exp_CY(beta1, beta2, nb_vectors=None, nb_experiments=10, nb_iterations=10**4, verbose=False, output_file=None, return_error=False): r""" Compute the Lyapunov exponents of the geodesic flow in the hypergeometric function space. The input parameters yield the eigenvalues around infinity, $e^{2i\pi\beta_1}, e^{2i\pi\beta_2}, e^{-2i\pi\beta_1}, e^{-2i\pi\beta_1}$. We compute the coefficient of the characteristic polynomial of such a matrix. And call the C function which associate to these coefficient the companion matrices of the polynomial. We use Levelt theorem for computing monodromy matrices. See Theorem 3.2.3 in [Beu]. INPUT: - ``beta1``, ``beta2`` -- parameters of the eigenvalues - ``nb_vectors`` -- the number of vectors to use - ``nb_experiments`` -- number of experimets - ``nb_iterations`` -- the number of iterations of the induction to perform - ``output_file`` -- place where we print the results - ``verbose`` -- do we print the result with an extensive number of information or not OUTPUT: A list of nb_vectors lyapunov exponents by default. If return_error is True, a 4-tuple consisting of : 1. a list of nb_vectors lyapunov exponents 2. a list of nb_vectors of their statistical error 3. an integer of their sum 4. an integer of the statistical error of their sum """ from sage.all import exp from sage.misc.functional import numerical_approx import time import lyapunov_exponents # the cython bindings from math import sqrt if output_file is None: from sys import stdout output_file = stdout closed = True elif isinstance(output_file, str): output_file = open(output_file, "w") closed = False beta1 = numerical_approx(beta1) beta2 = numerical_approx(beta2) b1 = numerical_approx(exp(2*1j*pi*beta1)) b2 = numerical_approx(exp(2*1j*pi*beta2)) a = (b1**2*b2 + b1*b2**2 + b1 + b2)/(b1*b2) b = (-b1**2*b2**2 - b1**2 - 2*b1*b2 - b2**2 - 1)/(b1*b2) if nb_vectors <> None and nb_vectors <= 0: raise ValueError("the number of vectors must be positive") if nb_experiments <= 0: raise ValueError("the number of experiments must be positive") if nb_iterations <= 0: raise ValueError("the number of iterations must be positive") #recall that the lyapunov exponents are symmetric if nb_vectors == None: nb_vectors = 2 t0 = time.time() res = lyapunov_exponents.lyapunov_exponents([0]*4, [0]*4, 4, nb_vectors, nb_experiments, nb_iterations, [a, b]) t1 = time.time() res_final = [] std_final = [] s_m, s_d = 0, 0 if verbose: from math import floor, log output_file.write("sample of %d experiments\n"%nb_experiments) output_file.write("%d iterations (~2**%d)\n"%(nb_iterations, floor(log(nb_iterations) / log(2)))) output_file.write("ellapsed time %s\n"%time.strftime("%H:%M:%S",time.gmtime(t1-t0))) for i in xrange(nb_vectors): m,d = mean_and_std_dev(res[i]) s_m += m s_d += d**2 if verbose: output_file.write("theta%d : %f (std. dev. = %f, conf. rad. 0.01 = %f)\n"%( i,m,d, 2.576*d/sqrt(nb_experiments))) res_final.append(m) std_final.append(2.576*d/sqrt(nb_experiments)) s_d = sqrt(s_d) s_d_final = 2.576*s_d/sqrt(nb_experiments) if verbose: output_file.write("sum_theta : %f (std. dev. = %f, conf. rad. 0.01 = %f)\n\n"%( s_m,s_d, 2.576*s_d/sqrt(nb_experiments))) if not closed : output_file.close() print "file closed" if return_error: return (res_final, std_final, s_m, s_d_final) else: return res_final
def plot(self, chart=None, ambient_coords=None, mapping=None, chart_domain=None, fixed_coords=None, ranges=None, max_value=8, nb_values=None, steps=None,scale=1, color='blue', parameters=None, label_axes=True, **extra_options): r""" Plot the vector field in a Cartesian graph based on the coordinates of some ambient chart. The vector field is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The vector field's base points `p` (or their images `\Phi(p)` by some differentiable mapping `\Phi`) must lie in the ambient chart's domain. INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, the default chart of the vector field's domain is used - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi` (instance of :class:`~sage.geometry.manifolds.diffmapping.DiffMapping`) providing the link between the vector field's domain and the ambient chart ``chart``; if ``None``, the identity mapping is assumed - ``chart_domain`` -- (default: ``None``) chart on the vector field's domain to define the points at which vector arrows are to be plotted; if ``None``, the default chart of the vector field's domain is used - ``fixed_coords`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` that are kept fixed and with values the value of these coordinates; if ``None``, all the coordinates of ``chart_domain`` are used - ``ranges`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values tuples ``(x_min,x_max)`` specifying the coordinate range for the plot; if ``None``, the entire coordinate range declared during the construction of ``chart_domain`` is considered (with ``-Infinity`` replaced by ``-max_value`` and ``+Infinity`` by ``max_value``) - ``max_value`` -- (default: 8) numerical value substituted to ``+Infinity`` if the latter is the upper bound of the range of a coordinate for which the plot is performed over the entire coordinate range (i.e. for which no specific plot range has been set in ``ranges``); similarly ``-max_value`` is the numerical valued substituted for ``-Infinity`` - ``nb_values`` -- (default: ``None``) either an integer or a dictionary with keys the coordinates of ``chart_domain`` to be used and values the number of values of the coordinate for sampling the part of the vector field's domain involved in the plot ; if ``nb_values`` is a single integer, it represents the number of values for all coordinates; if ``nb_values`` is ``None``, it is set to 9 for a 2D plot and to 5 for a 3D plot - ``steps`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values the step between each constant value of the coordinate; if ``None``, the step is computed from the coordinate range (specified in ``ranges``) and ``nb_values``. On the contrary, if the step is provided for some coordinate, the corresponding number of values is deduced from it and the coordinate range. - ``scale`` -- (default: 1) value by which the lengths of the arrows representing the vectors is multiplied - ``color`` -- (default: 'blue') color of the arrows representing the vectors - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of the vector field (see example below) - ``label_axes`` -- (default: ``True``) boolean determining whether the labels of the coordinate axes of ``chart`` shall be added to the graph; can be set to ``False`` if the graph is 3D and must be superposed with another graph. - ``**extra_options`` -- extra options for the arrow plot, like ``linestyle``, ``width`` or ``arrowsize`` (see :func:`~sage.plot.arrow.arrow2d` and :func:`~sage.plot.plot3d.shapes.arrow3d` for details) OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Plot of a vector field on a 2-dimensional manifold:: sage: Manifold._clear_cache_() # for doctests only sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = -y, x ; v.display() v = -y d/dx + x d/dy sage: v.plot() Graphics object consisting of 80 graphics primitives Plot with various options:: sage: v.plot(scale=0.5, color='green', linestyle='--', width=1, arrowsize=6) Graphics object consisting of 80 graphics primitives sage: v.plot(max_value=4, nb_values=5, scale=0.5) Graphics object consisting of 24 graphics primitives Plots along a line of fixed coordinate:: sage: v.plot(fixed_coords={x: -2}) Graphics object consisting of 9 graphics primitives sage: v.plot(fixed_coords={y: 1}) Graphics object consisting of 9 graphics primitives Let us now consider a vector field on a 4-dimensional manifold:: sage: Manifold._clear_cache_() # for doctests only sage: M = Manifold(4, 'M') sage: X.<t,x,y,z> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = (t/8)^2, -t*y/4, t*x/4, t*z/4 ; v.display() v = 1/64*t^2 d/dt - 1/4*t*y d/dx + 1/4*t*x d/dy + 1/4*t*z d/dz We cannot make a 4D plot directly:: sage: v.plot() Traceback (most recent call last): ... ValueError: the number of ambient coordinates must be either 2 or 3, not 4 Rather, we have to select some coordinates for the plot, via the argument ``ambient_coords``. For instance, for a 3D plot:: sage: v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1}) Graphics3d Object sage: v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0}, ....: ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, nb_values=4) Graphics3d Object or, for a 2D plot:: sage: v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0}) Graphics object consisting of 80 graphics primitives sage: v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0}) Graphics object consisting of 72 graphics primitives An example of plot via a differential mapping: plot of a vector field tangent to a 2-sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') # the open set covered by spherical coord. sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_mapping(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='F') sage: F.display() # the standard embedding of S^2 into R^3 F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: v = XS.frame()[1] ; v vector field 'd/dph' on the open subset 'U' of the 2-dimensional manifold 'S^2' sage: graph_v = v.plot(chart=X3, mapping=F, label_axes=False) sage: graph_S2 = XS.plot(chart=X3, mapping=F, nb_values=9) sage: show(graph_v + graph_S2) """ from sage.rings.infinity import Infinity from sage.misc.functional import numerical_approx from sage.misc.latex import latex from sage.plot.graphics import Graphics from sage.geometry.manifolds.chart import Chart from sage.geometry.manifolds.utilities import set_axes_labels # # 1/ Treatment of input parameters # ----------------------------- if chart is None: chart = self._domain.default_chart() elif not isinstance(chart, Chart): raise TypeError("{} is not a chart".format(chart)) if chart_domain is None: chart_domain = self._domain.default_chart() elif not isinstance(chart_domain, Chart): raise TypeError("{} is not a chart".format(chart_domain)) elif not chart_domain.domain().is_subset(self._domain): raise ValueError("The domain of {} is not ".format(chart_domain) + "included in the domain of {}".format(self)) if fixed_coords is None: coords = chart_domain._xx else: fixed_coord_list = fixed_coords.keys() coords = [] for coord in chart_domain._xx: if coord not in fixed_coord_list: coords.append(coord) coords = tuple(coords) if ambient_coords is None: ambient_coords = chart[:] elif not isinstance(ambient_coords, tuple): ambient_coords = tuple(ambient_coords) nca = len(ambient_coords) if nca != 2 and nca !=3: raise ValueError("the number of ambient coordinates must be " + "either 2 or 3, not {}".format(nca)) if ranges is None: ranges = {} ranges0 = {} for coord in coords: if coord in ranges: ranges0[coord] = (numerical_approx(ranges[coord][0]), numerical_approx(ranges[coord][1])) else: bounds = chart_domain._bounds[chart_domain[:].index(coord)] if bounds[0][0] == -Infinity: xmin = numerical_approx(-max_value) elif bounds[0][1]: xmin = numerical_approx(bounds[0][0]) else: xmin = numerical_approx(bounds[0][0] + 1.e-3) if bounds[1][0] == Infinity: xmax = numerical_approx(max_value) elif bounds[1][1]: xmax = numerical_approx(bounds[1][0]) else: xmax = numerical_approx(bounds[1][0] - 1.e-3) ranges0[coord] = (xmin, xmax) ranges = ranges0 if nb_values is None: if nca == 2: # 2D plot nb_values = 9 else: # 3D plot nb_values = 5 if not isinstance(nb_values, dict): nb_values0 = {} for coord in coords: nb_values0[coord] = nb_values nb_values = nb_values0 if steps is None: steps = {} for coord in coords: if coord not in steps: steps[coord] = (ranges[coord][1] - ranges[coord][0])/ \ (nb_values[coord]-1) else: nb_values[coord] = 1 + int( (ranges[coord][1] - ranges[coord][0])/ steps[coord]) # # 2/ Plots # ----- dom = chart_domain.domain() nc = len(chart_domain[:]) ncp = len(coords) xx = [0] * nc if fixed_coords is not None: if len(fixed_coords) != nc - ncp: raise ValueError("Bad number of fixed coordinates.") for fc, val in fixed_coords.iteritems(): xx[chart_domain[:].index(fc)] = val index_p = [chart_domain[:].index(cd) for cd in coords] resu = Graphics() ind = [0] * ncp ind_max = [0] * ncp ind_max[0] = nb_values[coords[0]] xmin = [ranges[cd][0] for cd in coords] step_tab = [steps[cd] for cd in coords] while ind != ind_max: for i in range(ncp): xx[index_p[i]] = xmin[i] + ind[i]*step_tab[i] if chart_domain.valid_coordinates(*xx, tolerance=1e-13, parameters=parameters): point = dom(xx, chart=chart_domain) resu += self.at(point).plot(chart=chart, ambient_coords=ambient_coords, mapping=mapping, scale=scale, color=color, print_label=False, parameters=parameters, **extra_options) # Next index: ret = 1 for pos in range(ncp-1,-1,-1): imax = nb_values[coords[pos]] - 1 if ind[pos] != imax: ind[pos] += ret ret = 0 elif ret == 1: if pos == 0: ind[pos] = imax + 1 # end point reached else: ind[pos] = 0 ret = 1 if label_axes: if nca==2: # 2D graphic # We update the dictionary _extra_kwds (options to be passed # to show()), instead of using the method # Graphics.axes_labels() since the latter is not robust w.r.t. # graph addition resu._extra_kwds['axes_labels'] = [r'$'+latex(ac)+r'$' for ac in ambient_coords] else: # 3D graphic labels = [str(ac) for ac in ambient_coords] resu = set_axes_labels(resu, *labels) return resu
def plot(self, chart=None, ambient_coords=None, mapping=None, chart_domain=None, fixed_coords=None, ranges=None, number_values=None, steps=None, parameters=None, label_axes=True, **extra_options): r""" Plot the vector field in a Cartesian graph based on the coordinates of some ambient chart. The vector field is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The vector field's base points `p` (or their images `\Phi(p)` by some differentiable mapping `\Phi`) must lie in the ambient chart's domain. INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, the default chart of the vector field's domain is used - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- :class:`~sage.manifolds.differentiable.diff_map.DiffMap` (default: ``None``); differentiable map `\Phi` providing the link between the vector field's domain and the ambient chart ``chart``; if ``None``, the identity map is assumed - ``chart_domain`` -- (default: ``None``) chart on the vector field's domain to define the points at which vector arrows are to be plotted; if ``None``, the default chart of the vector field's domain is used - ``fixed_coords`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` that are kept fixed and with values the value of these coordinates; if ``None``, all the coordinates of ``chart_domain`` are used - ``ranges`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values tuples ``(x_min, x_max)`` specifying the coordinate range for the plot; if ``None``, the entire coordinate range declared during the construction of ``chart_domain`` is considered (with ``-Infinity`` replaced by ``-max_range`` and ``+Infinity`` by ``max_range``) - ``number_values`` -- (default: ``None``) either an integer or a dictionary with keys the coordinates of ``chart_domain`` to be used and values the number of values of the coordinate for sampling the part of the vector field's domain involved in the plot ; if ``number_values`` is a single integer, it represents the number of values for all coordinates; if ``number_values`` is ``None``, it is set to 9 for a 2D plot and to 5 for a 3D plot - ``steps`` -- (default: ``None``) dictionary with keys the coordinates of ``chart_domain`` to be used and values the step between each constant value of the coordinate; if ``None``, the step is computed from the coordinate range (specified in ``ranges``) and ``number_values``; on the contrary, if the step is provided for some coordinate, the corresponding number of values is deduced from it and the coordinate range - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of the vector field (see example below) - ``label_axes`` -- (default: ``True``) boolean determining whether the labels of the coordinate axes of ``chart`` shall be added to the graph; can be set to ``False`` if the graph is 3D and must be superposed with another graph - ``color`` -- (default: 'blue') color of the arrows representing the vectors - ``max_range`` -- (default: 8) numerical value substituted to ``+Infinity`` if the latter is the upper bound of the range of a coordinate for which the plot is performed over the entire coordinate range (i.e. for which no specific plot range has been set in ``ranges``); similarly ``-max_range`` is the numerical valued substituted for ``-Infinity`` - ``scale`` -- (default: 1) value by which the lengths of the arrows representing the vectors is multiplied - ``**extra_options`` -- extra options for the arrow plot, like ``linestyle``, ``width`` or ``arrowsize`` (see :func:`~sage.plot.arrow.arrow2d` and :func:`~sage.plot.plot3d.shapes.arrow3d` for details) OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Plot of a vector field on a 2-dimensional manifold:: sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = -y, x ; v.display() v = -y d/dx + x d/dy sage: v.plot() Graphics object consisting of 80 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot() sphinx_plot(g) Plot with various options:: sage: v.plot(scale=0.5, color='green', linestyle='--', width=1, ....: arrowsize=6) Graphics object consisting of 80 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(scale=0.5, color='green', linestyle='--', width=1, arrowsize=6) sphinx_plot(g) :: sage: v.plot(max_range=4, number_values=5, scale=0.5) Graphics object consisting of 24 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(max_range=4, number_values=5, scale=0.5) sphinx_plot(g) Plot using parallel computation:: sage: Parallelism().set(nproc=2) sage: v.plot(scale=0.5, number_values=10, linestyle='--', width=1, ....: arrowsize=6) Graphics object consisting of 100 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(scale=0.5, number_values=10, linestyle='--', width=1, arrowsize=6) sphinx_plot(g) :: sage: Parallelism().set(nproc=1) # switch off parallelization Plots along a line of fixed coordinate:: sage: v.plot(fixed_coords={x: -2}) Graphics object consisting of 9 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(fixed_coords={x: -2}) sphinx_plot(g) :: sage: v.plot(fixed_coords={y: 1}) Graphics object consisting of 9 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] v = M.vector_field(name='v'); v[:] = -y, x g = v.plot(fixed_coords={y: 1}) sphinx_plot(g) Let us now consider a vector field on a 4-dimensional manifold:: sage: M = Manifold(4, 'M') sage: X.<t,x,y,z> = M.chart() sage: v = M.vector_field(name='v') sage: v[:] = (t/8)^2, -t*y/4, t*x/4, t*z/4 ; v.display() v = 1/64*t^2 d/dt - 1/4*t*y d/dx + 1/4*t*x d/dy + 1/4*t*z d/dz We cannot make a 4D plot directly:: sage: v.plot() Traceback (most recent call last): ... ValueError: the number of ambient coordinates must be either 2 or 3, not 4 Rather, we have to select some coordinates for the plot, via the argument ``ambient_coords``. For instance, for a 3D plot:: sage: v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1}, # long time ....: number_values=4) Graphics3d Object .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z') ; t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 sphinx_plot(v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1}, number_values=4)) :: sage: v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0}, # long time ....: ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, ....: number_values=4) Graphics3d Object .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 sphinx_plot(v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0}, ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, number_values=4)) or, for a 2D plot:: sage: v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0}) # long time Graphics object consisting of 80 graphics primitives .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 g = v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0}) sphinx_plot(g) :: sage: v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0}) # long time Graphics object consisting of 72 graphics primitives .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] v = M.vector_field(name='v') v[:] = v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4 g = v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0}) sphinx_plot(g) An example of plot via a differential mapping: plot of a vector field tangent to a 2-sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') # the open set covered by spherical coord. sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), cos(th)]}, name='F') sage: F.display() # the standard embedding of S^2 into R^3 F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: v = XS.frame()[1] ; v # the coordinate vector d/dphi Vector field d/dph on the Open subset U of the 2-dimensional differentiable manifold S^2 sage: graph_v = v.plot(chart=X3, mapping=F, label_axes=False) sage: graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) sage: graph_v + graph_S2 Graphics3d Object .. PLOT:: S2 = Manifold(2, 'S^2') U = S2.open_subset('U') XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') th, ph = XS[:] R3 = Manifold(3, 'R^3') X3 = R3.chart('x y z') F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)]}, name='F') v = XS.frame()[1] graph_v = v.plot(chart=X3, mapping=F, label_axes=False) graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) sphinx_plot(graph_v + graph_S2) Note that the default values of some arguments of the method ``plot`` are stored in the dictionary ``plot.options``:: sage: v.plot.options # random (dictionary output) {'color': 'blue', 'max_range': 8, 'scale': 1} so that they can be adjusted by the user:: sage: v.plot.options['color'] = 'red' From now on, all plots of vector fields will use red as the default color. To restore the original default options, it suffices to type:: sage: v.plot.reset() """ from sage.rings.infinity import Infinity from sage.misc.functional import numerical_approx from sage.misc.latex import latex from sage.plot.graphics import Graphics from sage.manifolds.chart import RealChart from sage.manifolds.utilities import set_axes_labels from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism # # 1/ Treatment of input parameters # ----------------------------- max_range = extra_options.pop("max_range") scale = extra_options.pop("scale") color = extra_options.pop("color") if chart is None: chart = self._domain.default_chart() elif not isinstance(chart, RealChart): raise TypeError("{} is not a chart on a real ".format(chart) + "manifold") if chart_domain is None: chart_domain = self._domain.default_chart() elif not isinstance(chart_domain, RealChart): raise TypeError("{} is not a chart on a ".format(chart_domain) + "real manifold") elif not chart_domain.domain().is_subset(self._domain): raise ValueError("the domain of {} is not ".format(chart_domain) + "included in the domain of {}".format(self)) coords_full = tuple(chart_domain[:]) # all coordinates of chart_domain if fixed_coords is None: coords = coords_full else: fixed_coord_list = fixed_coords.keys() coords = [] for coord in coords_full: if coord not in fixed_coord_list: coords.append(coord) coords = tuple(coords) if ambient_coords is None: ambient_coords = chart[:] elif not isinstance(ambient_coords, tuple): ambient_coords = tuple(ambient_coords) nca = len(ambient_coords) if nca != 2 and nca !=3: raise ValueError("the number of ambient coordinates must be " + "either 2 or 3, not {}".format(nca)) if ranges is None: ranges = {} ranges0 = {} for coord in coords: if coord in ranges: ranges0[coord] = (numerical_approx(ranges[coord][0]), numerical_approx(ranges[coord][1])) else: bounds = chart_domain._bounds[coords_full.index(coord)] xmin0 = bounds[0][0] xmax0 = bounds[1][0] if xmin0 == -Infinity: xmin = numerical_approx(-max_range) elif bounds[0][1]: xmin = numerical_approx(xmin0) else: xmin = numerical_approx(xmin0 + 1.e-3) if xmax0 == Infinity: xmax = numerical_approx(max_range) elif bounds[1][1]: xmax = numerical_approx(xmax0) else: xmax = numerical_approx(xmax0 - 1.e-3) ranges0[coord] = (xmin, xmax) ranges = ranges0 if number_values is None: if nca == 2: # 2D plot number_values = 9 else: # 3D plot number_values = 5 if not isinstance(number_values, dict): number_values0 = {} for coord in coords: number_values0[coord] = number_values number_values = number_values0 if steps is None: steps = {} for coord in coords: if coord not in steps: steps[coord] = (ranges[coord][1] - ranges[coord][0])/ \ (number_values[coord]-1) else: number_values[coord] = 1 + int( (ranges[coord][1] - ranges[coord][0])/ steps[coord]) # # 2/ Plots # ----- dom = chart_domain.domain() vector = self.restrict(dom) if vector.parent().destination_map() is dom.identity_map(): if mapping is not None: vector = mapping.pushforward(vector) mapping = None nc = len(coords_full) ncp = len(coords) xx = [0] * nc if fixed_coords is not None: if len(fixed_coords) != nc - ncp: raise ValueError("bad number of fixed coordinates") for fc, val in fixed_coords.items(): xx[coords_full.index(fc)] = val ind_coord = [] for coord in coords: ind_coord.append(coords_full.index(coord)) resu = Graphics() ind = [0] * ncp ind_max = [0] * ncp ind_max[0] = number_values[coords[0]] xmin = [ranges[cd][0] for cd in coords] step_tab = [steps[cd] for cd in coords] nproc = Parallelism().get('tensor') if nproc != 1 and nca == 2: # parallel plot construct : Only for 2D plot (at moment) ! # creation of the list of parameters list_xx = [] while ind != ind_max: for i in range(ncp): xx[ind_coord[i]] = xmin[i] + ind[i]*step_tab[i] if chart_domain.valid_coordinates(*xx, tolerance=1e-13, parameters=parameters): # needed a xx*1 to copy the list by value list_xx.append(xx*1) # Next index: ret = 1 for pos in range(ncp-1,-1,-1): imax = number_values[coords[pos]] - 1 if ind[pos] != imax: ind[pos] += ret ret = 0 elif ret == 1: if pos == 0: ind[pos] = imax + 1 # end point reached else: ind[pos] = 0 ret = 1 lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] ind_step = max(1, int(len(list_xx)/nproc/2)) local_list = lol(list_xx,ind_step) # definition of the list of input parameters listParalInput = [(vector, dom, ind_part, chart_domain, chart, ambient_coords, mapping, scale, color, parameters, extra_options) for ind_part in local_list] # definition of the parallel function @parallel(p_iter='multiprocessing', ncpus=nproc) def add_point_plot(vector, dom, xx_list, chart_domain, chart, ambient_coords, mapping, scale, color, parameters, extra_options): count = 0 for xx in xx_list: point = dom(xx, chart=chart_domain) part = vector.at(point).plot(chart=chart, ambient_coords=ambient_coords, mapping=mapping,scale=scale, color=color, print_label=False, parameters=parameters, **extra_options) if count == 0: local_resu = part else: local_resu += part count += 1 return local_resu # parallel execution and reconstruction of the plot for ii, val in add_point_plot(listParalInput): resu += val else: # sequential plot while ind != ind_max: for i in range(ncp): xx[ind_coord[i]] = xmin[i] + ind[i]*step_tab[i] if chart_domain.valid_coordinates(*xx, tolerance=1e-13, parameters=parameters): point = dom(xx, chart=chart_domain) resu += vector.at(point).plot(chart=chart, ambient_coords=ambient_coords, mapping=mapping, scale=scale, color=color, print_label=False, parameters=parameters, **extra_options) # Next index: ret = 1 for pos in range(ncp-1, -1, -1): imax = number_values[coords[pos]] - 1 if ind[pos] != imax: ind[pos] += ret ret = 0 elif ret == 1: if pos == 0: ind[pos] = imax + 1 # end point reached else: ind[pos] = 0 ret = 1 if label_axes: if nca == 2: # 2D graphic # We update the dictionary _extra_kwds (options to be passed # to show()), instead of using the method # Graphics.axes_labels() since the latter is not robust w.r.t. # graph addition resu._extra_kwds['axes_labels'] = [r'$'+latex(ac)+r'$' for ac in ambient_coords] else: # 3D graphic labels = [str(ac) for ac in ambient_coords] resu = set_axes_labels(resu, *labels) return resu
def my_rounded(number, s): m = my_log(number) return numerical_approx(number, digits=m - s + 1)
def percolation_probability(self, stop): return numerical_approx(self.ntimes_over_size(stop) / self._n, digits=3)
def my_rounded(number, s): m = floor_log(number) return numerical_approx(number, digits=m-s+1)
def plot(self, chart=None, ambient_coords=None, mapping=None, color='blue', print_label=True, label=None, label_color=None, fontsize=10, label_offset=0.1, parameters=None, **extra_options): r""" Plot the vector in a Cartesian graph based on the coordinates of some ambient chart. The vector is drawn in terms of two (2D graphics) or three (3D graphics) coordinates of a given chart, called hereafter the *ambient chart*. The vector's base point `p` (or its image `\Phi(p)` by some differentiable mapping `\Phi`) must lie in the ambient chart's domain. If `\Phi` is different from the identity mapping, the vector actually depicted is `\mathrm{d}\Phi_p(v)`, where `v` is the current vector (``self``) (see the example of a vector tangent to the 2-sphere below, where `\Phi: S^2 \to \RR^3`). INPUT: - ``chart`` -- (default: ``None``) the ambient chart (see above); if ``None``, it is set to the default chart of the open set containing the point at which the vector (or the vector image via the differential `\mathrm{d}\Phi_p` of ``mapping``) is defined - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3 coordinates of the ambient chart in terms of which the plot is performed; if ``None``, all the coordinates of the ambient chart are considered - ``mapping`` -- (default: ``None``) :class:`~sage.manifolds.differentiable.diff_map.DiffMap`; differentiable mapping `\Phi` providing the link between the point `p` at which the vector is defined and the ambient chart ``chart``: the domain of ``chart`` must contain `\Phi(p)`; if ``None``, the identity mapping is assumed - ``scale`` -- (default: 1) value by which the length of the arrow representing the vector is multiplied - ``color`` -- (default: 'blue') color of the arrow representing the vector - ``print_label`` -- (boolean; default: ``True``) determines whether a label is printed next to the arrow representing the vector - ``label`` -- (string; default: ``None``) label printed next to the arrow representing the vector; if ``None``, the vector's symbol is used, if any - ``label_color`` -- (default: ``None``) color to print the label; if ``None``, the value of ``color`` is used - ``fontsize`` -- (default: 10) size of the font used to print the label - ``label_offset`` -- (default: 0.1) determines the separation between the vector arrow and the label - ``parameters`` -- (default: ``None``) dictionary giving the numerical values of the parameters that may appear in the coordinate expression of ``self`` (see example below) - ``**extra_options`` -- extra options for the arrow plot, like ``linestyle``, ``width`` or ``arrowsize`` (see :func:`~sage.plot.arrow.arrow2d` and :func:`~sage.plot.plot3d.shapes.arrow3d` for details) OUTPUT: - a graphic object, either an instance of :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on 2 coordinates of ``chart``) or an instance of :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e. based on 3 coordinates of ``chart``) EXAMPLES: Vector tangent to a 2-dimensional manifold:: sage: M = Manifold(2, 'M') sage: X.<x,y> = M.chart() sage: p = M((2,2), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((2, 1), name='v') ; v Tangent vector v at Point p on the 2-dimensional differentiable manifold M Plot of the vector alone (arrow + label):: sage: v.plot() Graphics object consisting of 2 graphics primitives Plot atop of the chart grid:: sage: X.plot() + v.plot() Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot() sphinx_plot(g) Plots with various options:: sage: X.plot() + v.plot(color='green', scale=2, label='V') Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(color='green', scale=2, label='V') sphinx_plot(g) :: sage: X.plot() + v.plot(print_label=False) Graphics object consisting of 19 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(print_label=False) sphinx_plot(g) :: sage: X.plot() + v.plot(color='green', label_color='black', ....: fontsize=20, label_offset=0.2) Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(color='green', label_color='black', fontsize=20, label_offset=0.2) sphinx_plot(g) :: sage: X.plot() + v.plot(linestyle=':', width=4, arrowsize=8, ....: fontsize=20) Graphics object consisting of 20 graphics primitives .. PLOT:: M = Manifold(2, 'M') X = M.chart('x y'); x, y = X[:] p = M((2,2), name='p'); Tp = M.tangent_space(p) v = Tp((2, 1), name='v') g = X.plot() + v.plot(linestyle=':', width=4, arrowsize=8, fontsize=20) sphinx_plot(g) Plot with specific values of some free parameters:: sage: var('a b') (a, b) sage: v = Tp((1+a, -b^2), name='v') ; v.display() v = (a + 1) d/dx - b^2 d/dy sage: X.plot() + v.plot(parameters={a: -2, b: 3}) Graphics object consisting of 20 graphics primitives Special case of the zero vector:: sage: v = Tp.zero() ; v Tangent vector zero at Point p on the 2-dimensional differentiable manifold M sage: X.plot() + v.plot() Graphics object consisting of 19 graphics primitives Vector tangent to a 4-dimensional manifold:: sage: M = Manifold(4, 'M') sage: X.<t,x,y,z> = M.chart() sage: p = M((0,1,2,3), name='p') sage: Tp = M.tangent_space(p) sage: v = Tp((5,4,3,2), name='v') ; v Tangent vector v at Point p on the 4-dimensional differentiable manifold M We cannot make a 4D plot directly:: sage: v.plot() Traceback (most recent call last): ... ValueError: the number of coordinates involved in the plot must be either 2 or 3, not 4 Rather, we have to select some chart coordinates for the plot, via the argument ``ambient_coords``. For instance, for a 2-dimensional plot in terms of the coordinates `(x, y)`:: sage: v.plot(ambient_coords=(x,y)) Graphics object consisting of 2 graphics primitives .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] p = M((0,1,2,3), name='p'); Tp = M.tangent_space(p) v = Tp((5,4,3,2), name='v') g = X.plot(ambient_coords=(x,y)) + v.plot(ambient_coords=(x,y)) sphinx_plot(g) This plot involves only the components `v^x` and `v^y` of `v`. Similarly, for a 3-dimensional plot in terms of the coordinates `(t, x, y)`:: sage: g = v.plot(ambient_coords=(t,x,z)) sage: print(g) Graphics3d Object This plot involves only the components `v^t`, `v^x` and `v^z` of `v`. A nice 3D view atop the coordinate grid is obtained via:: sage: (X.plot(ambient_coords=(t,x,z)) # long time ....: + v.plot(ambient_coords=(t,x,z), ....: label_offset=0.5, width=6)) Graphics3d Object .. PLOT:: M = Manifold(4, 'M') X = M.chart('t x y z'); t,x,y,z = X[:] p = M((0,1,2,3), name='p'); Tp = M.tangent_space(p) v = Tp((5,4,3,2), name='v') g = X.plot(ambient_coords=(t,x,z)) + v.plot(ambient_coords=(t,x,z), label_offset=0.5, width=6) sphinx_plot(g) An example of plot via a differential mapping: plot of a vector tangent to a 2-sphere viewed in `\RR^3`:: sage: S2 = Manifold(2, 'S^2') sage: U = S2.open_subset('U') # the open set covered by spherical coord. sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') sage: R3 = Manifold(3, 'R^3') sage: X3.<x,y,z> = R3.chart() sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), ....: sin(th)*sin(ph), ....: cos(th)]}, name='F') sage: F.display() # the standard embedding of S^2 into R^3 F: S^2 --> R^3 on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th)) sage: p = U.point((pi/4, 7*pi/4), name='p') sage: v = XS.frame()[1].at(p) ; v # the coordinate vector d/dphi at p Tangent vector d/dph at Point p on the 2-dimensional differentiable manifold S^2 sage: graph_v = v.plot(mapping=F) sage: graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) # long time sage: graph_v + graph_S2 # long time Graphics3d Object .. PLOT:: S2 = Manifold(2, 'S^2') U = S2.open_subset('U') XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi') th, ph = XS[:] R3 = Manifold(3, 'R^3') X3 = R3.chart('x y z') F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)]}, name='F') p = U.point((pi/4, 7*pi/4), name='p') v = XS.frame()[1].at(p) graph_v = v.plot(mapping=F) graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9) sphinx_plot(graph_v + graph_S2) """ from sage.plot.arrow import arrow2d from sage.plot.text import text from sage.plot.graphics import Graphics from sage.plot.plot3d.shapes import arrow3d from sage.plot.plot3d.shapes2 import text3d from sage.misc.functional import numerical_approx from sage.manifolds.differentiable.chart import DiffChart scale = extra_options.pop("scale") # # The "effective" vector to be plotted # if mapping is None: eff_vector = self base_point = self._point else: #!# check # For efficiency, the method FiniteRankFreeModuleMorphism._call_() # is called instead of FiniteRankFreeModuleMorphism.__call__() eff_vector = mapping.differential(self._point)._call_(self) base_point = mapping(self._point) # # The chart w.r.t. which the vector is plotted # if chart is None: chart = base_point.parent().default_chart() elif not isinstance(chart, DiffChart): raise TypeError("{} is not a chart".format(chart)) # # Coordinates of the above chart w.r.t. which the vector is plotted # if ambient_coords is None: ambient_coords = chart[:] # all chart coordinates are used n_pc = len(ambient_coords) if n_pc != 2 and n_pc !=3: raise ValueError("the number of coordinates involved in the " + "plot must be either 2 or 3, not {}".format(n_pc)) # indices coordinates involved in the plot: ind_pc = [chart[:].index(pc) for pc in ambient_coords] # # Components of the vector w.r.t. the chart frame # basis = chart.frame().at(base_point) vcomp = eff_vector.comp(basis=basis)[:] xp = base_point.coord(chart=chart) # # The arrow # resu = Graphics() if parameters is None: coord_tail = [numerical_approx(xp[i]) for i in ind_pc] coord_head = [numerical_approx(xp[i] + scale*vcomp[i]) for i in ind_pc] else: coord_tail = [numerical_approx(xp[i].substitute(parameters)) for i in ind_pc] coord_head = [numerical_approx( (xp[i] + scale*vcomp[i]).substitute(parameters)) for i in ind_pc] if coord_head != coord_tail: if n_pc == 2: resu += arrow2d(tailpoint=coord_tail, headpoint=coord_head, color=color, **extra_options) else: resu += arrow3d(coord_tail, coord_head, color=color, **extra_options) # # The label # if print_label: if label is None: if n_pc == 2 and self._latex_name is not None: label = r'$' + self._latex_name + r'$' if n_pc == 3 and self._name is not None: label = self._name if label is not None: xlab = [xh + label_offset for xh in coord_head] if label_color is None: label_color = color if n_pc == 2: resu += text(label, xlab, fontsize=fontsize, color=label_color) else: resu += text3d(label, xlab, fontsize=fontsize, color=label_color) return resu