def legend_3d(hyperplane_arrangement, hyperplane_colors, length): r""" Create plot of a 3d legend for an arrangement of planes in 3-space. The ``length`` parameter determines whether short or long labels are used in the legend. INPUT: - ``hyperplane_arrangement`` -- a hyperplane arrangement - ``hyperplane_colors`` -- list of colors - ``length`` -- either ``'short'`` or ``'long'`` OUTPUT: - A graphics object. EXAMPLES:: sage: a = hyperplane_arrangements.semiorder(3) sage: from sage.geometry.hyperplane_arrangement.plot import legend_3d sage: legend_3d(a, colors.values()[:6],length='long') sage: b = hyperplane_arrangements.semiorder(4) sage: c = b.essentialization() sage: legend_3d(c, colors.values()[:12], length='long') sage: legend_3d(c, colors.values()[:12], length='short') sage: p = legend_3d(c, colors.values()[:12], length='short') sage: p.set_legend_options(ncol=4) sage: type(p) <class 'sage.plot.graphics.Graphics'> """ if hyperplane_arrangement.dimension() != 3: raise ValueError('arrangements must be in 3-space') hyps = hyperplane_arrangement.hyperplanes() N = len(hyperplane_arrangement) if length == 'short': labels = [' ' + str(i) for i in range(N)] else: labels = [ ' ' + hyps[i]._repr_linear(include_zero=False) for i in range(N) ] p = Graphics() for i in range(N): p += line([(0, 0), (0, 0)], color=hyperplane_colors[i], thickness=8, legend_label=labels[i], axes=False) p.set_legend_options(title='Hyperplanes', loc='center', labelspacing=0.4, fancybox=True, font_size='x-large', ncol=2) p.legend(True) return p
def plot_n_cylinders(self, n, labels=True): r""" EXAMPLES:: sage: from slabbe.markov_transformation import markov_transformations sage: T = markov_transformations.Selmer() sage: G = T.plot_n_cylinders(3) TESTS:: sage: G = T.plot_n_cylinders(0) """ from sage.plot.graphics import Graphics from sage.plot.polygon import polygon from sage.plot.text import text M3to2 = projection_matrix(3, 2) G = Graphics() for w, cyl in self.n_cylinders_iterator(n): columns = cyl.columns() G += polygon((M3to2 * col / col.norm(1) for col in columns), fill=False) if labels: sum_cols = sum(columns) G += text("{}".format(w), M3to2 * sum_cols / sum_cols.norm(1)) return G
def plot(self, **options): r""" Plot this cylinder in coordinates used by a graphical surface. This plots this cylinder as a union of subpolygons. Only the intersections with polygons visible in the graphical surface are shown. Parameters other than `graphical_surface` are passed to `polygon2d` which is called to render the polygons. Parameters ---------- graphical_surface : a GraphicalSurface If not provided or `None`, the plot method uses the default graphical surface for the surface. """ if "graphical_surface" in options and options[ "graphical_surface"] is not None: gs = options["graphical_surface"] assert gs.get_surface( ) == self._s, "Graphical surface for the wrong surface." del options["graphical_surface"] else: gs = self._s.graphical_surface() plt = Graphics() for l, p in self.polygons(): if gs.is_visible(l): gp = gs.graphical_polygon(l) t = gp.transformation() pp = t(p) poly = polygon2d(pp.vertices(), **options) plt += poly.plot() return plt
def plot_error_function(model_filename, N, x0, Tfrac=0.8): """ Plot the estimated error of the linearized ODE as a function of time. INPUT: - ``model_filename`` -- string containing the model in text format - ``N`` -- truncation order - ``x0`` -- initial point, a list - ``Tfrac`` -- (optional, default: `0.8`): fraction of the convergence radius, to specify the plotting range in the time axis NOTE: This function calls ``error_function`` for the error computations. """ from sage.plot.graphics import Graphics from sage.plot.plot import plot from sage.plot.line import line [Ts, eps] = error_function(model_filename, N, x0) P = Graphics() P = plot(eps, 0, Ts * Tfrac, axes_labels=["$t$", r"$\mathcal{E}(t)$"]) P += line([[Ts, 0], [Ts, eps(t=Ts * Tfrac)]], linestyle='dotted', color='black') return P
def plot(self, **kwargs): """ Plot this Newton polygon. .. NOTE:: All usual rendering options (color, thickness, etc.) are available. EXAMPLES: sage: from sage.geometry.newton_polygon import NewtonPolygon sage: NP = NewtonPolygon([ (0,0), (1,1), (2,6) ]) sage: polygon = NP.plot() """ vertices = self.vertices() if len(vertices) == 0: from sage.plot.graphics import Graphics return Graphics() else: from sage.plot.line import line (xstart, ystart) = vertices[0] (xend, yend) = vertices[-1] if self.last_slope() is Infinity: return line([(xstart, ystart+1), (xstart,ystart+0.5)], linestyle="--", **kwargs) \ + line([(xstart, ystart+0.5)] + vertices + [(xend, yend+0.5)], **kwargs) \ + line([(xend, yend+0.5), (xend, yend+1)], linestyle="--", **kwargs) else: return line([(xstart, ystart+1), (xstart,ystart+0.5)], linestyle="--", **kwargs) \ + line([(xstart, ystart+0.5)] + vertices + [(xend+0.5, yend + 0.5*self.last_slope())], **kwargs) \ + line([(xend+0.5, yend + 0.5*self.last_slope()), (xend+1, yend+self.last_slope())], linestyle="--", **kwargs)
def plot2d(self, depth=None): # FIXME: refactor this before publishing from sage.plot.line import line from sage.plot.graphics import Graphics if self._n != 2: raise ValueError("Can only 2d plot fans.") if depth == None: depth = self._depth if not self.is_finite() and depth == infinity: raise ValueError( "For infinite algebras you must specify the depth.") colors = dict([(0, 'red'), (1, 'green')]) G = Graphics() for i in range(2): orbit = self.ith_orbit(i, depth=depth) for j in orbit: G += line([(0, 0), vector(orbit[j])], color=colors[i], thickness=0.5, zorder=2 * j + 1) G.set_aspect_ratio(1) G._show_axes = False return G
def circle_image(A, B): G = Graphics() G += circle((0, 0), 1, color='grey') from collections import defaultdict tmp = defaultdict(int) for a in A: for j in range(a): if gcd(j, a) == 1: rational = Rational(j) / Rational(a) tmp[(rational.numerator(), rational.denominator())] += 1 for b in B: for j in range(b): if gcd(j, b) == 1: rational = Rational(j) / Rational(b) tmp[(rational.numerator(), rational.denominator())] -= 1 C = ComplexField() for val in tmp: if tmp[val] > 0: G += text(str(tmp[val]), exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color="green") if tmp[val] < 0: G += text(str(abs(tmp[val])), exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color="blue") return G
def bar_chart(datalist, **options): """ A bar chart of (currently) one list of numerical data. Support for more data lists in progress. EXAMPLES: A bar_chart with blue bars:: sage: bar_chart([1,2,3,4]) Graphics object consisting of 1 graphics primitive A bar_chart with thinner bars:: sage: bar_chart([x^2 for x in range(1,20)], width=0.2) Graphics object consisting of 1 graphics primitive A bar_chart with negative values and red bars:: sage: bar_chart([-3,5,-6,11], rgbcolor=(1,0,0)) Graphics object consisting of 1 graphics primitive A bar chart with a legend (it's possible, not necessarily useful):: sage: bar_chart([-1,1,-1,1], legend_label='wave') Graphics object consisting of 1 graphics primitive Extra options will get passed on to show(), as long as they are valid:: sage: bar_chart([-2,8,-7,3], rgbcolor=(1,0,0), axes=False) Graphics object consisting of 1 graphics primitive sage: bar_chart([-2,8,-7,3], rgbcolor=(1,0,0)).show(axes=False) # These are equivalent """ dl = len(datalist) #if dl > 1: # print "WARNING, currently only 1 data set allowed" # datalist = datalist[0] if dl == 3: datalist = datalist + [0] #bardata = [] #cnt = 1 #for pnts in datalist: #ind = [i+cnt/dl for i in range(len(pnts))] #bardata.append([ind, pnts, xrange, yrange]) #cnt += 1 g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) #TODO: improve below for multiple data sets! #cnt = 1 #for ind, pnts, xrange, yrange in bardata: #options={'rgbcolor':hue(cnt/dl),'width':0.5/dl} # g._bar_chart(ind, pnts, xrange, yrange, options=options) # cnt += 1 #else: ind = list(range(len(datalist))) g.add_primitive(BarChart(ind, datalist, options=options)) if options['legend_label']: g.legend(True) return g
def piecewise_constant_image(A, B): # Jumps up and down going around circle, not used v = circle_drops(A, B) G = Graphics() w = ((Rational(i) / len(v), j) for i, j in enumerate(v)) for p0, p1 in w: G += line([(p0, p1), (p0 + Rational(1) / len(w), p1)]) return G
def piecewise_linear_image(A,B): # Jumps up and down going around circle, not used v = circle_drops(A,B) G = Graphics() w = [(Rational(i)/len(v), j) for i,j in enumerate(v)] for pt in w: G += line([(pt[0],pt[1]),(pt[0]+Rational(1)/len(w),pt[1])]) return G
def bezier_path(self): """ Return ``self`` as a Bezier path. This is needed to concatenate arcs, in order to create hyperbolic polygons. EXAMPLES:: sage: from sage.plot.arc import Arc sage: op = {'alpha':1,'thickness':1,'rgbcolor':'blue','zorder':0, ....: 'linestyle':'--'} sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path() Graphics object consisting of 1 graphics primitive sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red") sage: b = a[0].bezier_path() sage: b[0] Bezier path from (1.133..., 0.8237...) to (-0.2655..., 0.9911...) """ from sage.plot.bezier_path import BezierPath from sage.plot.graphics import Graphics from matplotlib.path import Path import numpy as np ma = self._matplotlib_arc() def theta_stretch(theta, scale): theta = np.deg2rad(theta) x = np.cos(theta) y = np.sin(theta) return np.rad2deg(np.arctan2(scale * y, x)) theta1 = theta_stretch(ma.theta1, ma.width / ma.height) theta2 = theta_stretch(ma.theta2, ma.width / ma.height) pa = ma pa._path = Path.arc(theta1, theta2) transform = pa.get_transform().get_matrix() cA, cC, cE = transform[0] cB, cD, cF = transform[1] points = [] for u in pa._path.vertices: x, y = list(u) points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)] cutlist = [points[0:4]] N = 4 while N < len(points): cutlist += [points[N:N + 3]] N += 3 g = Graphics() opt = self.options() opt['fill'] = False g.add_primitive(BezierPath(cutlist, opt)) return g
def plot(self, pointsize=20): from sage.plot.graphics import Graphics G = Graphics() m = len(self._scales) for i, (name, scale) in enumerate(zip(self._scale_names, self._scales)): G += list_plot(scale, color=hue(1. * i / m), legend_label=name, pointsize=pointsize) return G
def plot3d(self, depth=None): # FIXME: refactor this before publishing from sage.plot.graphics import Graphics from sage.plot.point import point from sage.misc.flatten import flatten from sage.plot.plot3d.shapes2 import sphere if self._n != 3: raise ValueError("Can only 3d plot fans.") if depth == None: depth = self._depth if not self.is_finite() and depth == infinity: raise ValueError( "For infinite algebras you must specify the depth.") colors = dict([(0, 'red'), (1, 'green'), (2, 'blue'), (3, 'cyan')]) G = Graphics() roots = self.d_vectors(depth=depth) compatible = [] while roots: x = roots.pop() for y in roots: if self.compatibility_degree(x, y) == 0: compatible.append((x, y)) for (u, v) in compatible: G += _arc3d((_normalize(vector(u)), _normalize(vector(v))), thickness=0.5, color='black') for i in range(3): orbit = self.ith_orbit(i, depth=depth) for j in orbit: G += point(_normalize(vector(orbit[j])), color=colors[i], size=10, zorder=len(G.all)) if self.is_affine(): tube_vectors = map(vector, flatten(self.affine_tubes())) tube_vectors = map(_normalize, tube_vectors) for v in tube_vectors: G += point(v, color=colors[3], size=10, zorder=len(G.all)) G += _arc3d((tube_vectors[0], tube_vectors[1]), thickness=5, color='gray', zorder=0) G += sphere((0, 0, 0), opacity=0.1, zorder=0) G._extra_kwds['frame'] = False G._extra_kwds['aspect_ratio'] = 1 return G
def plot_n_matrices_eigenvectors(self, n, side='right', color_index=0, draw_line=False): r""" INPUT: - ``n`` -- integer, length - ``side`` -- ``'left'`` or ``'right'``, drawing left or right eigenvectors - ``color_index`` -- 0 for first letter, -1 for last letter - ``draw_line`` -- boolean EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.ARP() sage: G = ARP.plot_n_matrices_eigenvectors(2) """ from sage.plot.graphics import Graphics from sage.plot.point import point from sage.plot.line import line from sage.plot.text import text from sage.plot.colors import hue from sage.modules.free_module_element import vector from matrices import M3to2 R = self.n_matrices_eigenvectors(n) L = [(w, M3to2*(a/sum(a)), M3to2*(b/sum(b))) for (w,a,b) in R] G = Graphics() alphabet = self._language._alphabet color_ = dict( (letter, hue(i/float(len(alphabet)))) for i,letter in enumerate(alphabet)) for letter in alphabet: L_filtered = [(w,p1,p2) for (w,p1,p2) in L if w[color_index] == letter] words,rights,lefts = zip(*L_filtered) if side == 'right': G += point(rights, color=color_[letter], legend_label=letter) elif side == 'left': G += point(lefts, color=color_[letter], legend_label=letter) else: raise ValueError("side(=%s) should be left or right" % side) if draw_line: for (a,b) in L: G += line([a,b], color='black', linestyle=":") G += line([M3to2*vector(a) for a in [(1,0,0), (0,1,0), (0,0,1), (1,0,0)]]) title = "%s eigenvectors, colored by letter w[%s] of cylinder w" % (side, color_index) G += text(title, (0.5, 1.05), axis_coords=True) G.axes(False) return G
def plot(self, geosub, color=None): r""" EXAMPLES:: sage: from EkEkstar import kFace, kPatch, GeoSub sage: sub = {1:[1,2], 2:[1,3], 3:[1]} sage: geosub = GeoSub(sub,2, dual=True) sage: P = kPatch([kFace((0,0,0),(1,2),dual=True), ....: kFace((0,0,1),(1,3),dual=True), ....: kFace((0,1,0),(2,1),dual=True), ....: kFace((0,0,0),(3,1),dual=True)]) sage: _ = P.plot(geosub) """ G = Graphics() for face, m in self: G += face._plot(geosub, color) G.set_aspect_ratio(1) return G
def plot_n_cylinders(self, n, labels=True): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: C = cocycles.Sorted_ARP() sage: G = C.plot_n_cylinders(3) """ from sage.plot.graphics import Graphics from sage.plot.polygon import polygon from sage.plot.text import text from matrices import M3to2 G = Graphics() for w,cyl in self.n_cylinders_iterator(n): columns = cyl.columns() G += polygon((M3to2*col/col.norm(1) for col in columns), fill=False) if labels: sum_cols = sum(columns) G += text("{}".format(w), M3to2*sum_cols/sum_cols.norm(1)) return G
def plot(self, m, pointsize=100, thickness=3, axes=False): r""" Return 2d graphics object contained in the primal box [-m,m]^d. INPUT: - ``pointsize``, integer (default:``100``), - ``thickness``, integer (default:``3``), - ``axes``, bool (default:``False``), EXAMPLES:: sage: from slabbe import BondPercolationSample sage: S = BondPercolationSample(0.5,2) sage: S.plot(2) # optional long It works in 3d!!:: sage: S = BondPercolationSample(0.5,3) sage: S.plot(3, pointsize=10, thickness=1) # optional long Graphics3d Object """ s = "" s += "\\begin{tikzpicture}\n" s += "[inner sep=0pt,thick,\n" s += "reddot/.style={fill=red,draw=red,circle,minimum size=5pt}]\n" s += "\\clip %s rectangle %s;\n" % ((-m - .4, -m - .4), (m + .4, m + .4)) G = Graphics() for u in self.cluster_in_box(m + 1): G += point(u, color='blue', size=pointsize) for (u, v) in self.edges_in_box(m + 1): G += line((u, v), thickness=thickness, alpha=0.8) G += text("p=%.3f" % self._p, (0.5, 1.03), axis_coords=True, color='black') G += circle((0, 0), 0.5, color='red', thickness=thickness) if self._dimension == 2: G.axes(axes) return G
def bezier_path(self): """ Return ``self`` as a Bezier path. This is needed to concatenate arcs, in order to create hyperbolic polygons. EXAMPLES:: sage: from sage.plot.arc import Arc sage: op = {'alpha':1,'thickness':1,'rgbcolor':'blue','zorder':0, ....: 'linestyle':'--'} sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path() Graphics object consisting of 1 graphics primitive sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red") sage: b = a[0].bezier_path() sage: b[0] Bezier path from (1.618..., 0.5877...) to (-0.5176..., 0.9659...) """ from sage.plot.bezier_path import BezierPath from sage.plot.graphics import Graphics ma = self._matplotlib_arc() transform = ma.get_transform().get_matrix() cA, cC, cE = transform[0] cB, cD, cF = transform[1] points = [] for u in ma._path.vertices: x, y = list(u) points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)] cutlist = [points[0:4]] N = 4 while N < len(points): cutlist += [points[N:N + 3]] N += 3 g = Graphics() opt = self.options() opt['fill'] = False g.add_primitive(BezierPath(cutlist, opt)) return g
def render_2d(projection, point_opts={}, line_opts={}, polygon_opts={}): """ Return 2d rendering of the projection of a polyhedron into 2-dimensional ambient space. EXAMPLES:: sage: p1 = Polyhedron(vertices=[[1,1]], rays=[[1,1]]) sage: q1 = p1.projection() sage: p2 = Polyhedron(vertices=[[1,0], [0,1], [0,0]]) sage: q2 = p2.projection() sage: p3 = Polyhedron(vertices=[[1,2]]) sage: q3 = p3.projection() sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]]) sage: q4 = p4.projection() sage: q1.show() + q2.show() + q3.show() + q4.show() sage: from sage.geometry.polyhedron.plot import render_2d sage: q = render_2d(p1.projection()) sage: q._objects [Point set defined by 1 point(s), Arrow from (1.0,1.0) to (2.0,2.0), Polygon defined by 3 points] """ if is_Polyhedron(projection): projection = Projection(projection) from sage.plot.graphics import Graphics plt = Graphics() if isinstance(point_opts, dict): point_opts.setdefault('zorder', 2) point_opts.setdefault('pointsize', 10) plt += projection.render_points_2d(**point_opts) if isinstance(line_opts, dict): line_opts.setdefault('zorder', 1) plt += projection.render_outline_2d(**line_opts) if isinstance(polygon_opts, dict): polygon_opts.setdefault('zorder', 0) plt += projection.render_fill_2d(**polygon_opts) return plt
def make_chart(self, region=None): G = Graphics() smin, smax, nmin, nmax = [], [], [], [] for d in self.chartgens(region): x, y = self.get_coords(d) G += point2d((x, y), zorder=10) smin.append(y) smax.append(y) nmin.append(x) nmax.append(x) smin = [ min(smin), ] smax = [ max(smax), ] nmin = [ min(nmin), ] nmax = [ max(nmax), ] for l in self.chartlines(): try: pts, lineargs = self.lineinfo[l["line"]] x1, y1 = self.get_coords(self.geninfo(int(l["src"]))) x2, y2 = self.get_coords(self.geninfo(int(l["tar"]))) G += line2d([(x1, y1), (x2, y2)], **lineargs) except: print("line not understood:", l) off = 0.5 G.ymin(smin[0] - off) G.ymax(smax[0] + off) G.xmin(nmin[0] - off) G.xmax(nmax[0] + off) return G
def circle_image(A, B): G = Graphics() G += circle((0, 0), 1, color='black', thickness=3) G += circle( (0, 0), 1.4, color='black', alpha=0 ) # This adds an invisible framing circle to the plot, which protects the aspect ratio from being skewed. from collections import defaultdict tmp = defaultdict(int) for a in A: for j in range(a): if gcd(j, a) == 1: rational = Rational(j) / Rational(a) tmp[(rational.numerator(), rational.denominator())] += 1 for b in B: for j in range(b): if gcd(j, b) == 1: rational = Rational(j) / Rational(b) tmp[(rational.numerator(), rational.denominator())] -= 1 C = ComplexField() color1 = (41 / 255, 95 / 255, 45 / 255) color2 = (0 / 255, 0 / 255, 150 / 255) for val in tmp: if tmp[val] > 0: G += text(str(tmp[val]), exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color=color1) if tmp[val] < 0: G += text(str(abs(tmp[val])), exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color=color2) return G
def plot(self, edge_labels=False, polygon_labels=False): from sage.plot.graphics import Graphics surface = self._surface polygons = surface.polygons() if not polygons.is_finite(): raise NotImplementedError( "no implementation for surface built from infinitely many polygons" ) if self._pos is None: self._set_positions() if self._edge_labels is None: self._set_edge_labels() G = Graphics() for a in polygons.keys(): p = polygons[a] v = p.vertices(translation=self._pos[a]) G += p.plot(translation=self._pos[a]) if edge_labels: for i in xrange(p.num_edges()): G += text(str(self._edge_labels[a, i]), (v[i] + v[(i + 1) % p.num_edges()]) / 2, color='black') # then we possibly plot the lable inside each polygon if len(self._index_set) != 1: for a in polygons.keys(): p = polygons[a] if polygon_labels: m = sum( p.vertices(translation=self._pos[a])) / p.num_edges() G += text(str(a), m, color='black') return G
def arc(center, r1, r2=None, angle=0.0, sector=(0.0, 2 * pi), **options): r""" An arc (that is a portion of a circle or an ellipse) Type ``arc.options`` to see all options. INPUT: - ``center`` - 2-tuple of real numbers - position of the center. - ``r1``, ``r2`` - positive real numbers - radii of the ellipse. If only ``r1`` is set, then the two radii are supposed to be equal and this function returns an arc of circle. - ``angle`` - real number - angle between the horizontal and the axis that corresponds to ``r1``. - ``sector`` - 2-tuple (default: (0,2*pi))- angles sector in which the arc will be drawn. OPTIONS: - ``alpha`` - float (default: 1) - transparency - ``thickness`` - float (default: 1) - thickness of the arc - ``color``, ``rgbcolor`` - string or 2-tuple (default: 'blue') - the color of the arc - ``linestyle`` - string (default: ``'solid'``) - The style of the line, which is one of ``'dashed'``, ``'dotted'``, ``'solid'``, ``'dashdot'``, or ``'--'``, ``':'``, ``'-'``, ``'-.'``, respectively. EXAMPLES: Plot an arc of circle centered at (0,0) with radius 1 in the sector `(\pi/4,3*\pi/4)`:: sage: arc((0,0), 1, sector=(pi/4,3*pi/4)) Graphics object consisting of 1 graphics primitive Plot an arc of an ellipse between the angles 0 and `\pi/2`:: sage: arc((2,3), 2, 1, sector=(0,pi/2)) Graphics object consisting of 1 graphics primitive Plot an arc of a rotated ellipse between the angles 0 and `\pi/2`:: sage: arc((2,3), 2, 1, angle=pi/5, sector=(0,pi/2)) Graphics object consisting of 1 graphics primitive Plot an arc of an ellipse in red with a dashed linestyle:: sage: arc((0,0), 2, 1, 0, (0,pi/2), linestyle="dashed", color="red") Graphics object consisting of 1 graphics primitive sage: arc((0,0), 2, 1, 0, (0,pi/2), linestyle="--", color="red") Graphics object consisting of 1 graphics primitive The default aspect ratio for arcs is 1.0:: sage: arc((0,0), 1, sector=(pi/4,3*pi/4)).aspect_ratio() 1.0 It is not possible to draw arcs in 3D:: sage: A = arc((0,0,0), 1) Traceback (most recent call last): ... NotImplementedError """ from sage.plot.all import Graphics # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. # Otherwise matplotlib complains. scale = options.get('scale', None) if isinstance(scale, (list, tuple)): scale = scale[0] if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' if len(center) == 2: if r2 is None: r2 = r1 g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) if len(sector) != 2: raise ValueError("the sector must consist of two angles") g.add_primitive( Arc(center[0], center[1], r1, r2, angle, sector[0], sector[1], options)) return g elif len(center) == 3: raise NotImplementedError
def plot(self, color='sign'): """ Return a plot of ``self``. INPUT: - ``color`` -- can be any of the following: * ``4`` - use 4 colors: black, red, blue, and green with each corresponding to up, right, down, and left respectively * ``2`` - use 2 colors: red for horizontal, blue for vertical arrows * ``'sign'`` - use red for right and down arrows, blue for left and up arrows * a list of 4 colors for each direction * a function which takes a direction and a boolean corresponding to the sign EXAMPLES:: sage: M = SixVertexModel(2, boundary_conditions='ice') sage: print(M[0].plot().description()) Arrow from (-1.0,0.0) to (0.0,0.0) Arrow from (-1.0,1.0) to (0.0,1.0) Arrow from (0.0,0.0) to (0.0,-1.0) Arrow from (0.0,0.0) to (1.0,0.0) Arrow from (0.0,1.0) to (0.0,0.0) Arrow from (0.0,1.0) to (0.0,2.0) Arrow from (1.0,0.0) to (1.0,-1.0) Arrow from (1.0,0.0) to (1.0,1.0) Arrow from (1.0,1.0) to (0.0,1.0) Arrow from (1.0,1.0) to (1.0,2.0) Arrow from (2.0,0.0) to (1.0,0.0) Arrow from (2.0,1.0) to (1.0,1.0) """ from sage.plot.graphics import Graphics from sage.plot.arrow import arrow if color == 4: color_list = ['black', 'red', 'blue', 'green'] cfunc = lambda d,pm: color_list[d] elif color == 2: cfunc = lambda d,pm: 'red' if d % 2 == 0 else 'blue' elif color == 1 or color is None: cfunc = lambda d,pm: 'black' elif color == 'sign': cfunc = lambda d,pm: 'red' if pm else 'blue' # RD are True elif isinstance(color, (list, tuple)): cfunc = lambda d,pm: color[d] else: cfunc = color G = Graphics() for j,row in enumerate(reversed(self)): for i,entry in enumerate(row): if entry == 0: # LR G += arrow((i,j+1), (i,j), color=cfunc(2, True)) G += arrow((i,j), (i+1,j), color=cfunc(1, True)) if j == 0: G += arrow((i,j-1), (i,j), color=cfunc(0, False)) if i == 0: G += arrow((i,j), (i-1,j), color=cfunc(3, False)) elif entry == 1: # LU G += arrow((i,j), (i,j+1), color=cfunc(0, False)) G += arrow((i+1,j), (i,j), color=cfunc(3, False)) if j == 0: G += arrow((i,j-1), (i,j), color=cfunc(0, False)) if i == 0: G += arrow((i,j), (i-1,j), color=cfunc(3, False)) elif entry == 2: # LD G += arrow((i,j+1), (i,j), color=cfunc(2, True)) G += arrow((i+1,j), (i,j), color=cfunc(3, False)) if j == 0: G += arrow((i,j), (i,j-1), color=cfunc(2, True)) if i == 0: G += arrow((i,j), (i-1,j), color=cfunc(3, False)) elif entry == 3: # UD G += arrow((i,j), (i,j+1), color=cfunc(0, False)) G += arrow((i+1,j), (i,j), color=cfunc(3, False)) if j == 0: G += arrow((i,j), (i,j-1), color=cfunc(2, True)) if i == 0: G += arrow((i-1,j), (i,j), color=cfunc(1, True)) elif entry == 4: # UR G += arrow((i,j), (i,j+1), color=cfunc(0, False)) G += arrow((i,j), (i+1,j), color=cfunc(1, True)) if j == 0: G += arrow((i,j-1), (i,j), color=cfunc(0, False)) if i == 0: G += arrow((i-1,j), (i,j), color=cfunc(1, True)) elif entry == 5: # RD G += arrow((i,j+1), (i,j), color=cfunc(2, True)) G += arrow((i,j), (i+1,j), color=cfunc(1, True)) if j == 0: G += arrow((i,j), (i,j-1), color=cfunc(2, True)) if i == 0: G += arrow((i-1,j), (i,j), color=cfunc(1, True)) G.axes(False) return G
def _graphics(self, plot_curve, ambient_coords, thickness=1, aspect_ratio='automatic', color='red', style='-', label_axes=True): r""" Plot a 2D or 3D curve in a Cartesian graph with axes labeled by the ambient coordinates; it is invoked by the methods :meth:`plot` of :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve`, and its subclasses (:class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve`, :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve`, and :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic`). TESTS:: sage: M = Manifold(2, 'R^2') sage: X.<x,y> = M.chart() sage: R.<t> = RealLine() sage: c = M.curve([cos(t), sin(t)], (t, 0, 2*pi), name='c') sage: graph = c._graphics([[1,2], [3,4]], [x,y]) sage: graph._objects[0].xdata == [1,3] True sage: graph._objects[0].ydata == [2,4] True sage: graph._objects[0]._options['thickness'] == 1 True sage: graph._extra_kwds['aspect_ratio'] == 'automatic' True sage: graph._objects[0]._options['rgbcolor'] == 'red' True sage: graph._objects[0]._options['linestyle'] == '-' True sage: l = [r'$'+latex(x)+r'$', r'$'+latex(y)+r'$'] sage: graph._extra_kwds['axes_labels'] == l True """ from sage.plot.graphics import Graphics from sage.plot.line import line from sage.manifolds.utilities import set_axes_labels # # The plot # n_pc = len(ambient_coords) 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 plot(self): r""" Return a graphical object of the Fully Packed Loop EXAMPLES: Here is the fully packed loop for .. MATH:: \begin{pmatrix} 0&1&1 \\ 1&-1&1 \\ 0&1&0 \end{pmatrix}: .. PLOT:: :width: 200 px A = AlternatingSignMatrix([[0, 1, 0], [1, -1, 1], [0, 1, 0]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) Here is how Sage represents this:: sage: A = AlternatingSignMatrix([[0, 1, 0], [1, -1, 1], [0, 1, 0]]) sage: fpl = FullyPackedLoop(A) sage: print(fpl.plot().description()) Line defined by 2 points: [(-1.0, 1.0), (0.0, 1.0)] Line defined by 2 points: [(0.0, 0.0), (0.0, -1.0)] Line defined by 2 points: [(0.0, 0.0), (1.0, 0.0)] Line defined by 2 points: [(0.0, 2.0), (0.0, 3.0)] Line defined by 2 points: [(0.0, 2.0), (0.0, 3.0)] Line defined by 2 points: [(0.0, 2.0), (1.0, 2.0)] Line defined by 2 points: [(1.0, 1.0), (0.0, 1.0)] Line defined by 2 points: [(1.0, 1.0), (2.0, 1.0)] Line defined by 2 points: [(2.0, 0.0), (1.0, 0.0)] Line defined by 2 points: [(2.0, 0.0), (2.0, -1.0)] Line defined by 2 points: [(2.0, 2.0), (1.0, 2.0)] Line defined by 2 points: [(2.0, 2.0), (2.0, 3.0)] Line defined by 2 points: [(2.0, 2.0), (2.0, 3.0)] Line defined by 2 points: [(3.0, 1.0), (2.0, 1.0)] Line defined by 2 points: [(3.0, 1.0), (2.0, 1.0)] Here are the other 3 by 3 Alternating Sign Matrices and their corresponding fully packed loops: .. MATH:: A = \begin{pmatrix} 1&0&0 \\ 0&1&0 \\ 0&0&1 \\ \end{pmatrix} gives: .. PLOT:: :width: 200 px A = AlternatingSignMatrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) .. MATH:: A = \begin{pmatrix} 1&0&0 \\ 0&0&1 \\ 0&1&0 \\ \end{pmatrix} gives: .. PLOT:: :width: 200 px A = AlternatingSignMatrix([[1, 0, 0], [0, 0, 1], [0, 1, 0]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) .. MATH:: A = \begin{pmatrix} 0&1&0\\ 1&0&0\\ 0&0&1\\ \end{pmatrix} gives: .. PLOT:: :width: 200 px A = AlternatingSignMatrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) .. MATH:: A = \begin{pmatrix} 0&1&0\\ 0&0&1\\ 1&0&0\\ \end{pmatrix} gives: .. PLOT:: :width: 200 px A = AlternatingSignMatrix([[0, 1, 0], [0, 0, 1], [1, 0, 0]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) .. MATH:: A = \begin{pmatrix} 0&0&1\\ 1&0&0\\ 0&1&0\\ \end{pmatrix} gives: .. PLOT:: :width: 200 px A = AlternatingSignMatrix([[0, 0, 1], [1, 0, 0], [0, 1, 0]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) .. MATH:: A = \begin{pmatrix} 0&0&1\\ 0&1&0\\ 1&0&0\\ \end{pmatrix} gives: .. PLOT:: :width: 200 px A = AlternatingSignMatrix([[0, 0, 1], [0, 1, 0], [1, 0, 0]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) EXAMPLES:: sage: A = AlternatingSignMatrix([[0, 1, 0, 0], [1, -1, 0, 1], \ [0, 1, 0, 0],[0, 0, 1, 0]]) sage: fpl = FullyPackedLoop(A) sage: print(fpl.plot().description()) Line defined by 2 points: [(-1.0, 0.0), (0.0, 0.0)] Line defined by 2 points: [(-1.0, 2.0), (0.0, 2.0)] Line defined by 2 points: [(0.0, 1.0), (0.0, 0.0)] Line defined by 2 points: [(0.0, 1.0), (1.0, 1.0)] Line defined by 2 points: [(0.0, 3.0), (0.0, 4.0)] Line defined by 2 points: [(0.0, 3.0), (0.0, 4.0)] Line defined by 2 points: [(0.0, 3.0), (1.0, 3.0)] Line defined by 2 points: [(1.0, 0.0), (1.0, -1.0)] Line defined by 2 points: [(1.0, 0.0), (2.0, 0.0)] Line defined by 2 points: [(1.0, 2.0), (0.0, 2.0)] Line defined by 2 points: [(1.0, 2.0), (2.0, 2.0)] Line defined by 2 points: [(2.0, 1.0), (1.0, 1.0)] Line defined by 2 points: [(2.0, 1.0), (2.0, 2.0)] Line defined by 2 points: [(2.0, 3.0), (1.0, 3.0)] Line defined by 2 points: [(2.0, 3.0), (2.0, 4.0)] Line defined by 2 points: [(2.0, 3.0), (2.0, 4.0)] Line defined by 2 points: [(3.0, 0.0), (2.0, 0.0)] Line defined by 2 points: [(3.0, 0.0), (3.0, -1.0)] Line defined by 2 points: [(3.0, 2.0), (3.0, 1.0)] Line defined by 2 points: [(3.0, 2.0), (3.0, 3.0)] Line defined by 2 points: [(4.0, 1.0), (3.0, 1.0)] Line defined by 2 points: [(4.0, 1.0), (3.0, 1.0)] Line defined by 2 points: [(4.0, 3.0), (3.0, 3.0)] Line defined by 2 points: [(4.0, 3.0), (3.0, 3.0)] Here is the plot: .. PLOT:: :width: 300 px A = AlternatingSignMatrix([[0, 1, 0, 0], [1, -1, 0, 1], [0, 1, 0, 0],[0, 0, 1, 0]]) fpl = FullyPackedLoop(A) p = fpl.plot() sphinx_plot(p) """ G = Graphics() n=len(self._six_vertex_model)-1 for j,row in enumerate(reversed(self._six_vertex_model)): for i,entry in enumerate(row): if i == 0 and (i+j+n+1) % 2 ==0: G+= line([(i-1,j),(i,j)]) if i == n and (i+j+n+1) % 2 ==0: G+= line([(i+1,j),(i,j)]) if j == 0 and (i+j+n) % 2 ==0: G+= line([(i,j),(i,j-1)]) if j == n and (i+j+n) % 2 ==0: G+= line([(i,j),(i,j+1)]) if entry == 0: # LR if (i+j+n) % 2==0: G += line([(i,j), (i+1,j)]) else: G += line([(i,j),(i,j+1)]) elif entry == 1: # LU if (i+j+n) % 2 ==0: G += line([(i,j), (i,j+1)]) else: G += line([(i+1,j), (i,j)]) elif entry == 2: # LD if (i+j+n) % 2 == 0: pass else: G += line([(i,j+1), (i,j)]) G += line([(i+1,j), (i,j)]) elif entry == 3: # UD if (i+j+n) % 2 == 0: G += line([(i,j), (i,j+1)]) else: G += line([(i+1,j), (i,j)]) elif entry == 4: # UR if (i+j+n) % 2 ==0: G += line([(i,j), (i,j+1)]) G += line([(i,j), (i+1,j)]) else: pass elif entry == 5: # RD if (i+j+n) % 2 ==0: G += line([(i,j), (i+1,j)]) else: G += line([(i,j+1), (i,j)]) G.axes(False) return G
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 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 .. 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.plot.graphics import Graphics from sage.plot.line import line from sage.manifolds.chart import RealChart from sage.manifolds.utilities import set_axes_labels # # 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 # # 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(hyperplane_arrangement, **kwds): r""" Return a plot of the hyperplane arrangement. If the arrangement is in 4 dimensions but inessential, a plot of the essentialization is returned. .. NOTE:: This function is available as the :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.plot` method of hyperplane arrangements. You should not call this function directly, only through the method. INPUT: - ``hyperplane_arrangement`` -- the hyperplane arrangement to plot - ``**kwds`` -- plot options: see :mod:`sage.geometry.hyperplane_arrangement.plot`. OUTPUT: A graphics object of the plot. EXAMPLES:: sage: B = hyperplane_arrangements.semiorder(4) sage: B.plot() Displaying the essentialization. Graphics3d Object """ N = len(hyperplane_arrangement) dim = hyperplane_arrangement.dimension() if hyperplane_arrangement.base_ring().characteristic() != 0: raise NotImplementedError('must be a field of characteristic 0') elif dim == 4: if not hyperplane_arrangement.is_essential(): print('Displaying the essentialization.') hyperplane_arrangement = hyperplane_arrangement.essentialization() elif dim not in [1, 2, 3]: # revise to handle 4d return # silently # handle extra keywords if 'hyperplane_colors' in kwds: hyp_colors = kwds.pop('hyperplane_colors') if not isinstance(hyp_colors, list): # we assume its a single color then hyp_colors = [hyp_colors] * N else: HSV_tuples = [(i * 1.0 / N, 0.8, 0.9) for i in range(N)] hyp_colors = [hsv_to_rgb(*x) for x in HSV_tuples] if 'hyperplane_labels' in kwds: hyp_labels = kwds.pop('hyperplane_labels') has_hyp_label = True if not isinstance(hyp_labels, list): # we assume its a boolean then hyp_labels = [hyp_labels] * N relabeled = [] for i in range(N): if hyp_labels[i] in [True, 'long']: relabeled.append(True) else: relabeled.append(str(i)) hyp_labels = relabeled else: has_hyp_label = False if 'label_colors' in kwds: label_colors = kwds.pop('label_colors') has_label_color = True if not isinstance(label_colors, list): # we assume its a single color then label_colors = [label_colors] * N else: has_label_color = False if 'label_fontsize' in kwds: label_fontsize = kwds.pop('label_fontsize') has_label_fontsize = True if not isinstance(label_fontsize, list): # we assume its a single size then label_fontsize = [label_fontsize] * N else: has_label_fontsize = False if 'label_offsets' in kwds: has_offsets = True offsets = kwds.pop('label_offsets') else: has_offsets = False # give default values below hyperplane_legend = kwds.pop('hyperplane_legend', 'long' if dim < 3 else False) if 'hyperplane_opacities' in kwds: hyperplane_opacities = kwds.pop('hyperplane_opacities') has_opacity = True if not isinstance(hyperplane_opacities, list): # we assume a single number then hyperplane_opacities = [hyperplane_opacities] * N else: has_opacity = False point_sizes = kwds.pop('point_sizes', 50) if not isinstance(point_sizes, list): point_sizes = [point_sizes] * N if 'ranges' in kwds: ranges_set = True ranges = kwds.pop('ranges') if not type(ranges) in [list, tuple]: # ranges is a single number ranges = [ranges] * N # So ranges is some type of list. elif dim == 2: # arrangement of lines in the plane if not type(ranges[0]) in [list, tuple]: # a single interval ranges = [ranges] * N elif dim == 3: # arrangement of planes in 3-space if not type(ranges[0][0]) in [list, tuple]: ranges = [ranges] * N elif dim not in [2, 3]: # ranges is not an option unless dim is 2 or 3 ranges_set = False else: # a list of intervals, one for each hyperplane is given pass # ranges does not need to be modified else: ranges_set = False # give default values below # the extra keywords have now been handled # now handle the legend if dim in [1, 2]: # points on a line or lines in the plane if hyperplane_legend in [True, 'long']: hyps = hyperplane_arrangement.hyperplanes() legend_labels = [hyps[i]._latex_() for i in range(N)] elif hyperplane_legend == 'short': legend_labels = [str(i) for i in range(N)] else: # dim==3, arrangement of planes in 3-space if hyperplane_legend in [True, 'long']: legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'long') elif hyperplane_legend == 'short': legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'short') ## done handling the legend ## now create the plot p = Graphics() for i in range(N): newk = copy(kwds) if has_hyp_label: newk['hyperplane_label'] = hyp_labels[i] if has_offsets: if not isinstance(offsets, list): newk['label_offset'] = offsets else: newk['label_offset'] = offsets[i] else: newk['hyperplane_label'] = False if has_label_color: newk['label_color'] = label_colors[i] if has_label_fontsize: newk['label_fontsize'] = label_fontsize[i] if has_opacity: newk['opacity'] = hyperplane_opacities[i] if dim == 1: newk['point_size'] = point_sizes[i] if dim in [1, 2] and hyperplane_legend: # more options than T/F newk['legend_label'] = legend_labels[i] if ranges_set: newk['ranges'] = ranges[i] p += plot_hyperplane(hyperplane_arrangement[i], rgbcolor=hyp_colors[i], **newk) if dim == 1: if hyperplane_legend: # there are more options than T/F p.legend(True) return p elif dim == 2: if hyperplane_legend: # there are more options than T/F p.legend(True) return p else: # dim==3 if hyperplane_legend: # there are more options than T/F return p, legend3d else: return p
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