Path: blob/master/src/smc_sagews/smc_sagews/sage_salvus.py
Views: 286
##################################################################################1# #2# Extra code that the Salvus server makes available in the running Sage session. #3# #4##################################################################################56#########################################################################################7# Copyright (C) 2016, Sagemath Inc.8# #9# Distributed under the terms of the GNU General Public License (GPL), version 2+ #10# #11# http://www.gnu.org/licenses/ #12#########################################################################################1314from __future__ import absolute_import, division15import six1617# set backend of matplot lib before any other module is loaded18import matplotlib19import imp20matplotlib.use('Agg')2122import copy, os, sys, types, re2324import sage.all252627def is_string(s):28return isinstance(s, six.string_types)293031def is_dataframe(obj):32if 'pandas' not in str(type(obj)):33# avoid having to import pandas unless it's really likely to be necessary.34return35# CRITICAL: do not import pandas at the top level since it can take up to 3s -- it's **HORRIBLE**.36try:37from pandas import DataFrame38except ImportError:39return False40return isinstance(obj, DataFrame)414243# This reduces a lot of confusion for Sage worksheets -- people expect44# to be able to import from the current working directory.45sys.path.append('.')4647salvus = None484950def set_salvus(salvus_obj):51global salvus52salvus = salvus_obj53from . import sage_jupyter54sage_jupyter.salvus = salvus_obj555657import json58from uuid import uuid4596061def uuid():62return str(uuid4())636465##########################################################################66# New function interact implementation67##########################################################################68import inspect6970interacts = {}717273def jsonable(x):74"""75Given any object x, make a JSON-able version of x, doing as best we can.76For some objects, sage as Sage integers, this works well. For other77objects which make no sense in Javascript, we get a string.78"""79import sage.all80try:81json.dumps(x)82return x83except:84if isinstance(x, (sage.all.Integer)):85return int(x)86else:87return str(x)888990class InteractCell(object):91def __init__(self,92f,93layout=None,94width=None,95style=None,96update_args=None,97auto_update=True,98flicker=False,99output=True):100"""101Given a function f, create an object that describes an interact102for working with f interactively.103104INPUT:105106- `f` -- Python function107- ``width`` -- (default: None) overall width of the interact canvas108- ``style`` -- (default: None) extra CSS style to apply to canvas109- ``update_args`` -- (default: None) only call f if one of the args in110this list of strings changes.111- ``auto_update`` -- (default: True) call f every time an input changes112(or one of the arguments in update_args).113- ``flicker`` -- (default: False) if False, the output part of the cell114never shrinks; it can only grow, which aleviates flicker.115- ``output`` -- (default: True) if False, do not automatically116provide any area to display output.117"""118self._flicker = flicker119self._output = output120self._uuid = uuid()121# Prevent garbage collection until client specifically requests it,122# since we want to be able to store state.123interacts[self._uuid] = self124self._f = f125self._width = jsonable(width)126self._style = str(style)127128if six.PY3:129_fas = inspect.getfullargspec(f)130args, varargs, varkw, defaults = _fas.args, _fas.varargs, _fas.varkw, _fas.defaults131elif six.PY2:132(args, varargs, varkw, defaults) = inspect.getargspec(f)133134if defaults is None:135defaults = []136137n = len(args) - len(defaults)138self._controls = dict([139(arg, interact_control(arg, defaults[i - n] if i >= n else None))140for i, arg in enumerate(args)141])142143self._last_vals = {}144for arg in args:145self._last_vals[arg] = self._controls[arg].default()146147self._ordered_args = args148self._args = set(args)149150if isinstance(layout, dict):151# Implement the layout = {'top':, 'bottom':, 'left':,152# 'right':} dictionary option that is in the Sage153# notebook. I personally think it is really awkward and154# unsuable, but there may be many interacts out there that155# use it.156# Example layout={'top': [['a', 'b'], ['x', 'y']], 'left': [['c']], 'bottom': [['d']]}157top = layout.get('top', [])158bottom = layout.get('bottom', [])159left = layout.get('left', [])160right = layout.get('right', [])161new_layout = []162for row in top:163new_layout.append(row)164if len(left) > 0 and len(right) > 0:165new_layout.append(left[0] + [''] + right[0])166del left[0]167del right[0]168elif len(left) > 0 and len(right) == 0:169new_layout.append(left[0] + [''])170del left[0]171elif len(left) == 0 and len(right) > 0:172new_layout.append([''] + right[0])173del right[0]174while len(left) > 0 and len(right) > 0:175new_layout.append(left[0] + ['_salvus_'] + right[0])176del left[0]177del right[0]178while len(left) > 0:179new_layout.append(left[0])180del left[0]181while len(right) > 0:182new_layout.append(right[0])183del right[0]184for row in bottom:185new_layout.append(row)186layout = new_layout187188if layout is None:189layout = [[(str(arg), 12, None)] for arg in self._ordered_args]190else:191try:192v = []193for row in layout:194new_row = []195for x in row:196if is_string(x):197x = (x, )198if len(x) == 1:199new_row.append((str(x[0]), 12 // len(row), None))200elif len(x) == 2:201new_row.append((str(x[0]), int(x[1]), None))202elif len(x) == 3:203new_row.append((str(x[0]), int(x[1]), str(x[2])))204v.append(new_row)205layout = v206except:207raise ValueError(208"layout must be None or a list of tuples (variable_name, width, [optional label]), where width is an integer between 1 and 12, variable_name is a string, and label is a string. The widths in each row must add up to at most 12. The empty string '' denotes the output area."209)210211# Append a row for any remaining controls:212layout_vars = set(sum([[x[0] for x in row] for row in layout], []))213for v in args:214if v not in layout_vars:215layout.append([(v, 12, None)])216217if self._output:218if '' not in layout_vars:219layout.append([('', 12, None)])220221self._layout = layout222223# TODO -- this is UGLY224if not auto_update:225c = button('Update')226c._opts['var'] = 'auto_update'227self._controls['auto_update'] = c228self._ordered_args.append("auto_update")229layout.append([('auto_update', 2)])230update_args = ['auto_update']231232self._update_args = update_args233234def jsonable(self):235"""236Return a JSON-able description of this interact, which the client237can use for laying out controls.238"""239X = {240'controls':241[self._controls[arg].jsonable() for arg in self._ordered_args],242'id':243self._uuid244}245if self._width is not None:246X['width'] = self._width247if self._layout is not None:248X['layout'] = self._layout249X['style'] = self._style250X['flicker'] = self._flicker251return X252253def __call__(self, vals):254"""255Call self._f with inputs specified by vals. Any input variables not256specified in vals will have the value they had last time.257"""258self.changed = [str(x) for x in list(vals.keys())]259for k, v in vals.items():260x = self._controls[k](v)261self._last_vals[k] = x262263if self._update_args is not None:264do_it = False265for v in self._update_args:266if v in self.changed:267do_it = True268if not do_it:269return270271interact_exec_stack.append(self)272try:273self._f(**dict([(k, self._last_vals[k]) for k in self._args]))274finally:275interact_exec_stack.pop()276277278class InteractFunction(object):279def __init__(self, interact_cell):280self.__dict__['interact_cell'] = interact_cell281282def __call__(self, **kwds):283salvus.clear()284for arg, value in kwds.items():285self.__setattr__(arg, value)286return self.interact_cell(kwds)287288def __setattr__(self, arg, value):289I = self.__dict__['interact_cell']290if arg in I._controls and not isinstance(value, control):291# setting value of existing control292v = I._controls[arg].convert_to_client(value)293desc = {'var': arg, 'default': v}294I._last_vals[arg] = value295else:296# create a new control297new_control = interact_control(arg, value)298I._controls[arg] = new_control299desc = new_control.jsonable()300# set the id of the containing interact301desc['id'] = I._uuid302salvus.javascript("worksheet.set_interact_var(obj)",303obj=jsonable(desc))304305def __getattr__(self, arg):306I = self.__dict__['interact_cell']307try:308return I._last_vals[arg]309except Exception as err:310print(err)311raise AttributeError(312"no interact control corresponding to input variable '%s'" %313arg)314315def __delattr__(self, arg):316I = self.__dict__['interact_cell']317try:318del I._controls[arg]319except KeyError:320pass321desc = {'id': I._uuid, 'name': arg}322salvus.javascript("worksheet.del_interact_var(obj)",323obj=jsonable(desc))324325def changed(self):326"""327Return the variables that changed since last evaluation of the interact function328body. [SALVUS only]329330For example::331332@interact333def f(n=True, m=False, xyz=[1,2,3]):334print(n, m, xyz, interact.changed())335"""336return self.__dict__['interact_cell'].changed337338339class _interact_layout:340def __init__(self, *args):341self._args = args342343def __call__(self, f):344return interact(f, *self._args)345346347class Interact(object):348"""349Use interact to create interactive worksheet cells with sliders,350text boxes, radio buttons, check boxes, color selectors, and more.351352Put ``@interact`` on the line before a function definition in a353cell by itself, and choose appropriate defaults for the variable354names to determine the types of controls (see tables below). You355may also put ``@interact(layout=...)`` to control the layout of356controls. Within the function, you may explicitly set the value357of the control corresponding to a variable foo to bar by typing358interact.foo = bar.359360Type "interact.controls.[tab]" to get access to all of the controls.361362INPUT:363364- ``f`` -- function365- ``width`` -- number, or string such as '80%', '300px', '20em'.366- ``style`` -- CSS style string, which allows you to change the border,367background color, etc., of the interact.368- ``update_args`` -- (default: None); list of strings, so that369only changing the corresponding controls causes the function to370be re-evaluated; changing other controls will not cause an update.371- ``auto_update`` -- (default: True); if False, a button labeled372'Update' will appear which you can click on to re-evalute.373- ``layout`` -- (default: one control per row) a list [row0,374row1, ...] of lists of tuples row0 = [(var_name, width,375label), ...], where the var_name's are strings, the widths376must add up to at most 12, and the label is optional. This377will layout all of the controls and output using Twitter378Bootstraps "Fluid layout", with spans corresponding379to the widths. Use var_name='' to specify where the output380goes, if you don't want it to last. You may specify entries for381controls that you will create later using interact.var_name = foo.382383384NOTES: The flicker and layout options above are only in SALVUS.385For backwards compatibility with the Sage notebook, if layout386is a dictionary (with keys 'top', 'bottom', 'left', 'right'),387then the appropriate layout will be rendered as it used to be388in the Sage notebook.389390OUTPUT:391392- creates an interactive control.393394395AUTOMATIC CONTROL RULES396-----------------------397398There are also some defaults that allow you to make controls399automatically without having to explicitly specify them. E.g.,400you can make ``x`` a continuous slider of values between ``u`` and401``v`` by just writing ``x=(u,v)`` in the argument list.402403- ``u`` - blank input_box404- ``u=elt`` - input_box with ``default=element``, unless other rule below405- ``u=(umin,umax)`` - continuous slider (really `100` steps)406- ``u=(umin,umax,du)`` - slider with step size ``du``407- ``u=list`` - buttons if ``len(list)`` at most `5`; otherwise, drop down408- ``u=generator`` - a slider (up to `10000` steps)409- ``u=bool`` - a checkbox410- ``u=Color('blue')`` - a color selector; returns ``Color`` object411- ``u=matrix`` - an ``input_grid`` with ``to_value`` set to412``matrix.parent()`` and default values given by the matrix413- ``u=(default, v)`` - ``v`` anything as above, with given ``default`` value414- ``u=(label, v)`` - ``v`` anything as above, with given ``label`` (a string)415416EXAMPLES:417418419The layout option::420421@interact(layout={'top': [['a', 'b']], 'left': [['c']],422'bottom': [['d']], 'right':[['e']]})423def _(a=x^2, b=(0..20), c=100, d=x+1, e=sin(2)):424print(a+b+c+d+e)425426We illustrate some features that are only in Salvus, not in the427Sage cell server or Sage notebook.428429You can set the value of a control called foo to 100 using430interact.foo=100. For example::431432@interact433def f(n=20, twice=None):434interact.twice = int(n)*2435436437In this example, we create and delete multiple controls depending438on properties of the input::439440@interact441def f(n=20, **kwds):442print(kwds)443n = Integer(n)444if n % 2 == 1:445del interact.half446else:447interact.half = input_box(n/2, readonly=True)448if n.is_prime():449interact.is_prime = input_box('True', readonly=True)450else:451del interact.is_prime452453We illustrate not automatically updating the function until a454button is pressed::455456@interact(auto_update=False)457def f(a=True, b=False):458print(a, b)459460You can access the value of a control associated to a variable foo461that you create using interact.foo, and check whether there is a462control associated to a given variable name using hasattr::463464@interact465def f():466if not hasattr(interact, 'foo'):467interact.foo = 'hello'468else:469print(interact.foo)470471An indecisive interact::472473@interact474def f(n=selector(['yes', 'no'])):475for i in range(5):476interact.n = i%2477sleep(.2)478479We use the style option to make a holiday interact::480481@interact(width=25,482style="background-color:lightgreen; border:5px dashed red;")483def f(x=button('Merry ...',width=20)):484pass485486We make a little box that can be dragged around, resized, and is487updated via a computation (in this case, counting primes)::488489@interact(width=30,490style="background-color:lightorange; position:absolute; z-index:1000; box-shadow : 8px 8px 4px #888;")491def f(prime=text_control(label="Counting primes: ")):492salvus.javascript("cell.element.closest('.salvus-cell-output-interact').draggable().resizable()")493p = 2494c = 1495while True:496interact.prime = '%s, %.2f'%(p, float(c)/p)497p = next_prime(p)498c += 1499sleep(.25)500"""501def __call__(self,502f=None,503layout=None,504width=None,505style=None,506update_args=None,507auto_update=True,508flicker=False,509output=True):510if f is None:511return _interact_layout(layout, width, style, update_args,512auto_update, flicker)513else:514return salvus.interact(f,515layout=layout,516width=width,517style=style,518update_args=update_args,519auto_update=auto_update,520flicker=flicker,521output=output)522523def __setattr__(self, arg, value):524I = interact_exec_stack[-1]525if arg in I._controls and not isinstance(value, control):526# setting value of existing control527v = I._controls[arg].convert_to_client(value)528desc = {'var': arg, 'default': v}529I._last_vals[arg] = value530else:531# create a new control532new_control = interact_control(arg, value)533I._controls[arg] = new_control534desc = new_control.jsonable()535desc['id'] = I._uuid536salvus.javascript("worksheet.set_interact_var(obj)", obj=desc)537538def __delattr__(self, arg):539try:540del interact_exec_stack[-1]._controls[arg]541except KeyError:542pass543desc['id'] = I._uuid544salvus.javascript("worksheet.del_interact_var(obj)", obj=jsonable(arg))545546def __getattr__(self, arg):547try:548return interact_exec_stack[-1]._last_vals[arg]549except Exception as err:550raise AttributeError(551"no interact control corresponding to input variable '%s'" %552arg)553554def changed(self):555"""556Return the variables that changed since last evaluation of the interact function557body. [SALVUS only]558559For example::560561@interact562def f(n=True, m=False, xyz=[1,2,3]):563print(n, m, xyz, interact.changed())564"""565return interact_exec_stack[-1].changed566567568interact = Interact()569interact_exec_stack = []570571572class control:573def __init__(self,574control_type,575opts,576repr,577convert_from_client=None,578convert_to_client=jsonable):579# The type of the control -- a string, used for CSS selectors, switches, etc.580self._control_type = control_type581# The options that define the control -- passed to client582self._opts = dict(opts)583# Used to print the control to a string.584self._repr = repr585# Callable that the control may use in converting from JSON586self._convert_from_client = convert_from_client587self._convert_to_client = convert_to_client588self._last_value = self._opts['default']589590def convert_to_client(self, value):591try:592return self._convert_to_client(value)593except Exception as err:594sys.stderr.write("convert_to_client: %s -- %s\n" % (err, self))595sys.stderr.flush()596return jsonable(value)597598def __call__(self, obj):599"""600Convert JSON-able object returned from client to describe601value of this control.602"""603if self._convert_from_client is not None:604try:605x = self._convert_from_client(obj)606except Exception as err:607sys.stderr.write("%s -- %s\n" % (err, self))608sys.stderr.flush()609x = self._last_value610else:611x = obj612self._last_value = x613return x614615def __repr__(self):616return self._repr617618def label(self):619"""Return the label of this control."""620return self._opts['label']621622def default(self):623"""Return default value of this control."""624return self(self._opts['default'])625626def type(self):627"""Return type that values of this control are coerced to."""628return self._opts['type']629630def jsonable(self):631"""Return JSON-able object the client browser uses to render the control."""632X = {'control_type': self._control_type}633for k, v in self._opts.items():634X[k] = jsonable(v)635return X636637638def list_of_first_n(v, n):639"""Given an iterator v, return first n elements it produces as a list."""640if not hasattr(v, 'next'):641v = v.__iter__()642w = []643while n > 0:644try:645w.append(next(v))646except StopIteration:647return w648n -= 1649return w650651652def automatic_control(default):653from sage.all import Color654from sage.structure.element import is_Matrix655label = None656default_value = None657658for _ in range(2):659if isinstance(default, tuple) and len(default) == 2 and is_string(660default[0]):661label, default = default662if isinstance(default, tuple) and len(default) == 2 and hasattr(663default[1], '__iter__'):664default_value, default = default665666if isinstance(default, control):667if label:668default._opts['label'] = label669return default670elif is_string(default):671return input_box(default, label=label, type=str)672elif is_string(default):673return input_box(default, label=label, type=str)674elif isinstance(default, bool):675return checkbox(default, label=label)676elif isinstance(default, list):677return selector(default,678default=default_value,679label=label,680buttons=len(default) <= 5)681elif isinstance(default, Color):682return color_selector(default=default, label=label)683elif isinstance(default, tuple):684if len(default) == 2:685return slider(default[0],686default[1],687default=default_value,688label=label)689elif len(default) == 3:690return slider(default[0],691default[1],692default[2],693default=default_value,694label=label)695else:696return slider(list(default), default=default_value, label=label)697elif is_Matrix(default):698return input_grid(default.nrows(),699default.ncols(),700default=default.list(),701to_value=default.parent(),702label=label)703elif hasattr(default, '__iter__'):704return slider(list_of_first_n(default, 10000),705default=default_value,706label=label)707else:708return input_box(default, label=label)709710711def interact_control(arg, value):712if isinstance(value, control):713if value._opts['label'] is None:714value._opts['label'] = arg715c = value716else:717c = automatic_control(value)718if c._opts['label'] is None:719c._opts['label'] = arg720c._opts['var'] = arg721return c722723724def sage_eval(x, locals=None, **kwds):725if is_string(x):726x = str(x).strip()727if x.isspace():728return None729from sage.all import sage_eval730return sage_eval(x, locals=locals, **kwds)731732733class ParseValue:734def __init__(self, type):735self._type = type736737def _eval(self, value):738if is_string(value):739if not value:740return ''741return sage_eval(742value, locals=None if salvus is None else salvus.namespace)743else:744return value745746def __call__(self, value):747from sage.all import Color748if self._type is None:749return self._eval(value)750elif self._type is str:751return str(value)752elif self._type is str:753return str(value)754elif self._type is Color:755try:756return Color(value)757except ValueError:758try:759return Color("#" + value)760except ValueError:761raise TypeError("invalid color '%s'" % value)762else:763return self._type(self._eval(value))764765766def input_box(default=None,767label=None,768type=None,769nrows=1,770width=None,771readonly=False,772submit_button=None):773"""774An input box interactive control for use with the :func:`interact` command.775776INPUT:777778- default -- default value779- label -- label test780- type -- the type that the input is coerced to (from string)781- nrows -- (default: 1) the number of rows of the box782- width -- width; how wide the box is783- readonly -- is it read-only?784- submit_button -- defaults to true if nrows > 1 and false otherwise.785"""786return control(control_type='input-box',787opts=locals(),788repr="Input box",789convert_from_client=ParseValue(type))790791792def checkbox(default=True, label=None, readonly=False):793"""794A checkbox interactive control for use with the :func:`interact` command.795"""796return control(control_type='checkbox', opts=locals(), repr="Checkbox")797798799def color_selector(default='blue',800label=None,801readonly=False,802widget=None,803hide_box=False):804"""805A color selector.806807SALVUS only: the widget option is ignored -- SALVUS only provides808bootstrap-colorpicker.809810EXAMPLES::811812@interact813def f(c=color_selector()):814print(c)815"""816from sage.all import Color817default = Color(default).html_color()818return control(control_type='color-selector',819opts=locals(),820repr="Color selector",821convert_from_client=lambda x: Color(str(x)),822convert_to_client=lambda x: Color(x).html_color())823824825def text_control(default='', label=None, classes=None):826"""827A read-only control that displays arbitrary HTML amongst the other828interact controls. This is very powerful, since it can display829any HTML.830831INPUT::832833- ``default`` -- actual HTML to display834- ``label`` -- string or None835- ``classes`` -- space separated string of CSS classes836837EXAMPLES::838839We output the factorization of a number in a text_control::840841@interact842def f(n=2013, fact=text_control("")):843interact.fact = factor(n)844845We use a CSS class to make the text_control look like a button:846847@interact848def f(n=text_control("foo <b>bar</b>", classes='btn')):849pass850851We animate a picture into view:852853@interact854def f(size=[10,15,..,30], speed=[1,2,3,4]):855for k in range(size):856interact.g = text_control("<img src='http://sagemath.org/pix/sage_logo_new.png' width=%s>"%(20*k))857sleep(speed/50.0)858"""859return control(control_type='text',860opts=locals(),861repr="Text %r" % (default))862863864def button(default=None, label=None, classes=None, width=None, icon=None):865"""866Create a button. [SALVUS only]867868You can tell that pressing this button triggered the interact869evaluation because interact.changed() will include the variable870name tied to the button.871872INPUT:873874- ``default`` -- value variable is set to875- ``label`` -- string (default: None)876- ``classes`` -- string if None; if given, space separated877list of CSS classes. e.g., Bootstrap CSS classes such as:878btn-primary, btn-info, btn-success, btn-warning, btn-danger,879btn-link, btn-large, btn-small, btn-mini.880See http://twitter.github.com/bootstrap/base-css.html#buttons881If button_classes a single string, that class is applied to all buttons.882- ``width`` - an integer or string (default: None); if given,883all buttons are this width. If an integer, the default units884are 'ex'. A string that specifies any valid HTML units (e.g., '100px', '3em')885is also allowed [SALVUS only].886- ``icon`` -- None or string name of any icon listed at the font887awesome website (http://fortawesome.github.com/Font-Awesome/), e.g., 'fa-repeat'888889EXAMPLES::890891@interact892def f(hi=button('Hello', label='', classes="btn-primary btn-large"),893by=button("By")):894if 'hi' in interact.changed():895print("Hello to you, good sir.")896if 'by' in interact.changed():897print("See you.")898899Some buttons with icons::900901@interact902def f(n=button('repeat', icon='fa-repeat'),903m=button('see?', icon="fa-eye", classes="btn-large")):904print(interact.changed())905"""906return control(control_type="button",907opts=locals(),908repr="Button",909convert_from_client=lambda x: default,910convert_to_client=lambda x: str(x))911912913class Slider:914def __init__(self, start, stop, step_size, max_steps):915if isinstance(start, (list, tuple)):916self.vals = start917else:918if step_size is None:919if stop is None:920step_size = start / float(max_steps)921else:922step_size = (stop - start) / float(max_steps)923from sage.all import srange # sage range is much better/more flexible.924self.vals = srange(start, stop, step_size, include_endpoint=True)925# Now check to see if any of thee above constructed a list of926# values that exceeds max_steps -- if so, linearly interpolate:927if len(self.vals) > max_steps:928n = len(self.vals) // max_steps929self.vals = [self.vals[n * i] for i in range(len(self.vals) // n)]930931def to_client(self, val):932if val is None:933return 0934if isinstance(val, (list, tuple)):935return [self.to_client(v) for v in val]936else:937# Find index into self.vals of closest match.938try:939return self.vals.index(val) # exact match940except ValueError:941pass942z = [(abs(val - x), i) for i, x in enumerate(self.vals)]943z.sort()944return z[0][1]945946def from_client(self, val):947if val is None:948return self.vals[0]949# val can be a n-tuple or an integer950if isinstance(val, (list, tuple)):951return tuple([self.vals[v] for v in val])952else:953return self.vals[int(val)]954955956class InputGrid:957def __init__(self, nrows, ncols, default, to_value):958self.nrows = nrows959self.ncols = ncols960self.to_value = to_value961self.value = copy.deepcopy(self.adapt(default))962963def adapt(self, x):964if not isinstance(x, list):965return [[x for _ in range(self.ncols)] for _ in range(self.nrows)]966elif not all(isinstance(elt, list) for elt in x):967return [[x[i * self.ncols + j] for j in range(self.ncols)]968for i in range(self.nrows)]969else:970return x971972def from_client(self, x):973if len(x) == 0:974self.value = []975elif isinstance(x[0], list):976self.value = [[sage_eval(t) for t in z] for z in x]977else:978# x is a list of (unicode) strings -- we sage eval them all at once (instead of individually).979s = '[' + ','.join([str(t) for t in x]) + ']'980v = sage_eval(s)981self.value = [982v[n:n + self.ncols]983for n in range(0, self.nrows * self.ncols, self.ncols)984]985986return self.to_value(987self.value) if self.to_value is not None else self.value988989def to_client(self, x=None):990if x is None:991v = self.value992else:993v = self.adapt(x)994self.value = v # save value in our local cache995return [[repr(x) for x in y] for y in v]996997998def input_grid(nrows, ncols, default=0, label=None, to_value=None, width=5):999r"""1000A grid of input boxes, for use with the :func:`interact` command.10011002INPUT:10031004- ``nrows`` - an integer1005- ``ncols`` - an integer1006- ``default`` - an object; the default put in this input box1007- ``label`` - a string; the label rendered to the left of the box.1008- ``to_value`` - a list; the grid output (list of rows) is1009sent through this function. This may reformat the data or1010coerce the type.1011- ``width`` - an integer; size of each input box in characters10121013EXAMPLES:10141015Solving a system::10161017@interact1018def _(m = input_grid(2,2, default = [[1,7],[3,4]],1019label=r'$M\qquad =$', to_value=matrix, width=8),1020v = input_grid(2,1, default=[1,2],1021label=r'$v\qquad =$', to_value=matrix)):1022try:1023x = m.solve_right(v)1024html('$$%s %s = %s$$'%(latex(m), latex(x), latex(v)))1025except:1026html('There is no solution to $$%s x=%s$$'%(latex(m), latex(v)))10271028Squaring an editable and randomizable matrix::10291030@interact1031def f(reset = button('Randomize', classes="btn-primary", icon="fa-th"),1032square = button("Square", icon="fa-external-link"),1033m = input_grid(4,4,default=0, width=5, label="m =", to_value=matrix)):1034if 'reset' in interact.changed():1035print("randomize")1036interact.m = [[random() for _ in range(4)] for _ in range(4)]1037if 'square' in interact.changed():1038salvus.tex(m^2)10391040"""1041ig = InputGrid(nrows, ncols, default, to_value)10421043return control(control_type='input-grid',1044opts={1045'default': ig.to_client(),1046'label': label,1047'width': width,1048'nrows': nrows,1049'ncols': ncols1050},1051repr="Input Grid",1052convert_from_client=ig.from_client,1053convert_to_client=ig.to_client)105410551056def slider(start,1057stop=None,1058step=None,1059default=None,1060label=None,1061display_value=True,1062max_steps=500,1063step_size=None,1064range=False,1065width=None,1066animate=True):1067"""1068An interactive slider control for use with :func:`interact`.10691070There are several ways to call the slider function, but they all1071take several named arguments:10721073- ``default`` - an object (default: None); default value is closest1074value. If range=True, default can also be a 2-tuple (low, high).1075- ``label`` -- string1076- ``display_value`` -- bool (default: True); whether to display the1077current value to the right of the slider.1078- ``max_steps`` -- integer, default: 500; this is the maximum1079number of values that the slider can take on. Do not make1080it too large, since it could overwhelm the client. [SALVUS only]1081- ``range`` -- bool (default: False); instead, you can select1082a range of values (lower, higher), which are returned as a10832-tuple. You may also set the value of the slider or1084specify a default value using a 2-tuple.1085- ``width`` -- how wide the slider appears to the user [SALVUS only]1086- ``animate`` -- True (default), False,"fast", "slow", or the1087duration of the animation in milliseconds. [SALVUS only]10881089You may call the slider function as follows:10901091- slider([list of objects], ...) -- slider taking values the objects in the list10921093- slider([start,] stop[, step]) -- slider over numbers from start1094to stop. When step is given it specifies the increment (or1095decrement); if it is not given, then the number of steps equals1096the width of the control in pixels. In all cases, the number of1097values will be shrunk to be at most the pixel_width, since it is1098not possible to select more than this many values using a slider.10991100EXAMPLES::110111021103Use one slider to modify the animation speed of another::11041105@interact1106def f(speed=(50,100,..,2000), x=slider([1..50], animate=1000)):1107if 'speed' in interact.triggers():1108print("change x to have speed {}".format(speed))1109del interact.x1110interact.x = slider([1..50], default=interact.x, animate=speed)1111return1112"""1113if step_size is not None: # for compat with sage1114step = step_size1115slider = Slider(start, stop, step, max_steps)1116vals = [str(x) for x in slider.vals] # for display by the client1117if range and default is None:1118default = [0, len(vals) - 1]1119return control(control_type='range-slider' if range else 'slider',1120opts={1121'default': slider.to_client(default),1122'label': label,1123'animate': animate,1124'vals': vals,1125'display_value': display_value,1126'width': width1127},1128repr="Slider",1129convert_from_client=slider.from_client,1130convert_to_client=slider.to_client)113111321133def range_slider(*args, **kwds):1134"""1135range_slider is the same as :func:`slider`, except with range=True.11361137EXAMPLES:11381139A range slider with a constraint::11401141@interact1142def _(t = range_slider([1..1000], default=(100,200), label=r'Choose a range for $\alpha$')):1143print(t)1144"""1145kwds['range'] = True1146return slider(*args, **kwds)114711481149def selector(values,1150label=None,1151default=None,1152nrows=None,1153ncols=None,1154width=None,1155buttons=False,1156button_classes=None):1157"""1158A drop down menu or a button bar for use in conjunction with1159the :func:`interact` command. We use the same command to1160create either a drop down menu or selector bar of buttons,1161since conceptually the two controls do exactly the same thing1162- they only look different. If either ``nrows`` or ``ncols``1163is given, then you get a buttons instead of a drop down menu.11641165INPUT:11661167- ``values`` - either (1) a list [val0, val1, val2, ...] or (2)1168a list of pairs [(val0, lbl0), (val1,lbl1), ...] in which case1169all labels must be given -- use None to auto-compute a given label.1170- ``label`` - a string (default: None); if given, this label1171is placed to the left of the entire button group1172- ``default`` - an object (default: first); default value in values list1173- ``nrows`` - an integer (default: None); if given determines1174the number of rows of buttons; if given, buttons=True1175- ``ncols`` - an integer (default: None); if given determines1176the number of columns of buttons; if given, buttons=True1177- ``width`` - an integer or string (default: None); if given,1178all buttons are this width. If an integer, the default units1179are 'ex'. A string that specifies any valid HTML units (e.g., '100px', '3em')1180is also allowed [SALVUS only].1181- ``buttons`` - a bool (default: False, except as noted1182above); if True, use buttons1183- ``button_classes`` - [SALVUS only] None, a string, or list of strings1184of the of same length as values, whose entries are a whitespace-separated1185string of CSS classes, e.g., Bootstrap CSS classes such as:1186btn-primary, btn-info, btn-success, btn-warning, btn-danger,1187btn-link, btn-large, btn-small, btn-mini.1188See http://twitter.github.com/bootstrap/base-css.html#buttons1189If button_classes a single string, that class is applied to all buttons.1190"""1191if (len(values) > 0 and isinstance(values[0], tuple)1192and len(values[0]) == 2):1193vals = [z[0] for z in values]1194lbls = [str(z[1]) if z[1] is not None else None for z in values]1195else:1196vals = values1197lbls = [None] * len(vals)11981199for i in range(len(vals)):1200if lbls[i] is None:1201v = vals[i]1202lbls[i] = v if is_string(v) else str(v)12031204if default is None:1205default = 01206else:1207try:1208default = vals.index(default)1209except IndexError:1210default = 012111212opts = dict(locals())1213for k in ['vals', 'values', 'i', 'v', 'z']:1214if k in opts:1215del opts[k] # these could have a big jsonable repr12161217opts['lbls'] = lbls1218return control(control_type='selector',1219opts=opts,1220repr="Selector labeled %r with values %s" % (label, values),1221convert_from_client=lambda n: vals[int(n)],1222convert_to_client=lambda x: vals.index(x))122312241225interact_functions = {}1226interact_controls = [1227'button', 'checkbox', 'color_selector', 'input_box', 'range_slider',1228'selector', 'slider', 'text_control', 'input_grid'1229]12301231for f in ['interact'] + interact_controls:1232interact_functions[f] = globals()[f]123312341235# A little magic so that "interact.controls.[tab]" shows all the controls.1236class Controls:1237pass123812391240Interact.controls = Controls()1241for f in interact_controls:1242interact.controls.__dict__[f] = interact_functions[f]12431244##########################################################################################1245# Cell object -- programatically control the current cell.1246##########################################################################################124712481249class Cell(object):1250def id(self):1251"""1252Return the UUID of the cell in which this function is called.1253"""1254return salvus._id12551256def hide(self, component='input'):1257"""1258Hide the 'input' or 'output' component of a cell.1259"""1260salvus.hide(component)12611262def show(self, component='input'):1263"""1264Show the 'input' or 'output' component of a cell.1265"""1266salvus.show(component)12671268def hideall(self):1269"""1270Hide the input and output fields of the cell in which this code executes.1271"""1272salvus.hide('input')1273salvus.hide('output')12741275#def input(self, val=None):1276# """1277# Get or set the value of the input component of the cell in1278# which this code executes.1279# """1280# salvus.javascript("cell.set_input(obj)", obj=val)1281#1282#def output(self, val=None):1283# """1284# Get or set the value of the output component of the cell in1285# which this code executes.1286# """1287# salvus.javascript("cell.set_output(obj)", obj=val)1288# return salvus.output(val, self._id)128912901291cell = Cell()12921293##########################################################################################1294# Cell decorators -- aka "percent modes"1295##########################################################################################12961297import sage.misc.html1298try:1299_html = sage.misc.html.HTML()1300except:1301_html = sage.misc.html.HTMLFragmentFactory130213031304class HTML:1305"""1306Cell mode that renders everything after %html as HTML13071308EXAMPLES::13091310---1311%html1312<h1>A Title</h1>1313<h2>Subtitle</h2>13141315---1316%html(hide=True)1317<h1>A Title</h1>1318<h2>Subtitle</h2>13191320---1321%html("<h1>A title</h1>", hide=False)13221323---1324%html(hide=False) <h1>Title</h1>13251326"""1327def __init__(self, hide=False):1328self._hide = hide13291330def __call__(self, *args, **kwds):1331if len(kwds) > 0 and len(args) == 0:1332return HTML(**kwds)1333if len(args) > 0:1334self._render(args[0], **kwds)13351336def _render(self, s, hide=None):1337if hide is None:1338hide = self._hide1339if hide:1340salvus.hide('input')1341salvus.html(s)13421343def table(self, rows=None, header=False):1344"""1345Renders a given matrix or nested list as an HTML table.13461347Arguments::13481349* **rows**: the rows of the table as a list of lists1350* **header**: if True, the first row is formatted as a header (default: False)1351"""1352# TODO: support columns as in http://doc.sagemath.org/html/en/reference/misc/sage/misc/table.html1353assert rows is not None, '"rows" is a mandatory argument, should be a list of lists'13541355from sage.matrix.matrix import is_Matrix1356import numpy as np13571358if is_Matrix(rows):1359table = list(rows) # list of Sage Vectors1360elif isinstance(rows, np.ndarray):1361table = rows.tolist()1362else:1363table = rows13641365assert isinstance(table,1366(tuple, list)), '"rows" must be a list of lists'13671368def as_unicode(s):1369'''1370This not only deals with unicode strings, but also converts e.g. `Integer` objects to a str1371'''1372try:1373if six.PY3:1374return str(s, 'utf8')1375else:1376return str(s).encode('utf-8')1377except:1378return "?".encode('utf-8')13791380def mk_row(row, header=False):1381is_vector = hasattr(row, 'is_vector') and row.is_vector()1382assert isinstance(1383row, (tuple, list)1384) or is_vector, '"rows" must contain lists or vectors for each row'1385tag = 'th' if header else 'td'1386row = [1387'<{tag}>{}</{tag}>'.format(as_unicode(_), tag=tag) for _ in row1388]1389return '<tr>{}</tr>'.format(''.join(row))13901391thead = '<thead>{}</thead>'.format(mk_row(1392table.pop(0), header=True)) if header else ''1393h_rows = [mk_row(row) for row in table]1394html_table = '<table style="width: auto;" class="table table-bordered">{}<tbody>{}</tbody></table>'1395self(html_table.format(thead, ''.join(h_rows)))139613971398html = HTML()1399html.iframe = _html.iframe # written in a way that works fine140014011402def coffeescript(s=None, once=False):1403"""1404Execute code using CoffeeScript.14051406For example:14071408%coffeescript console.log 'hi'14091410or14111412coffeescript("console.log 'hi'")14131414You may either pass in a string or use this as a cell decorator,1415i.e., put %coffeescript at the top of a cell.14161417If you set once=False, the code will be executed every time the output of the cell is rendered, e.g.,1418on load, like with %auto::14191420coffeescript('console.log("hi")', once=False)14211422or14231424%coffeescript(once=False)1425console.log("hi")142614271428EXTRA FUNCTIONALITY:14291430When executing code, a function called print is defined, and objects cell and worksheet.::14311432print(1,2,'foo','bar') -- displays the inputs in the output cell14331434cell -- has attributes cell.output (the html output box) and cell.cell_id14351436worksheet -- has attributes project_page and editor, and methods interrupt, kill, and14371438execute_code: (opts) =>1439opts = defaults opts,1440code : required1441data : undefined1442preparse : true1443cb : undefined14441445OPTIMIZATION: When used alone as a cell decorator in a Sage worksheet1446with once=False (the default), rendering is done entirely client side,1447which is much faster, not requiring a round-trip to the server.1448"""1449if s is None:1450return lambda s: salvus.javascript(s, once=once, coffeescript=True)1451else:1452return salvus.javascript(s, coffeescript=True, once=once)145314541455def javascript(s=None, once=False):1456"""1457Execute code using JavaScript.14581459For example:14601461%javascript console.log('hi')14621463or14641465javascript("console.log('hi')")146614671468You may either pass in a string or use this as a cell decorator,1469i.e., put %javascript at the top of a cell.14701471If once=False (the default), the code will be executed every time the output of the1472cell is rendered, e.g., on load, like with %auto::14731474javascript('.. some code ', once=False)14751476or14771478%javascript(once=False)1479... some code14801481WARNING: If once=True, then this code is likely to get executed *before* the rest1482of the output for this cell has been rendered by the client.14831484javascript('console.log("HI")', once=False)14851486EXTRA FUNCTIONALITY:14871488When executing code, a function called print is defined, and objects cell and worksheet.::14891490print(1,2,'foo','bar') -- displays the inputs in the output cell14911492cell -- has attributes cell.output (the html output box) and cell.cell_id14931494worksheet -- has attributes project_page and editor, and methods interrupt, kill, and14951496execute_code: (opts) =>1497opts = defaults opts,1498code : required1499data : undefined1500preparse : true1501cb : undefined15021503This example illustrates using worksheet.execute_code::15041505%coffeescript1506for i in [500..505]1507worksheet.execute_code1508code : "i=salvus.data['i']; i, factor(i)"1509data : {i:i}1510cb : (mesg) ->1511if mesg.stdout then print(mesg.stdout)1512if mesg.stderr then print(mesg.stderr)15131514OPTIMIZATION: When used alone as a cell decorator in a Sage worksheet1515with once=False (the default), rendering is done entirely client side,1516which is much faster, not requiring a round-trip to the server.1517"""1518if s is None:1519return lambda s: salvus.javascript(s, once=once)1520else:1521return salvus.javascript(s, once=once)152215231524javascript_exec_doc = r"""15251526To send code from Javascript back to the Python process to1527be executed use the worksheet.execute_code function::15281529%javascript worksheet.execute_code(string_to_execute)15301531You may also use a more general call format of the form::15321533%javascript1534worksheet.execute_code({code:string_to_execute, data:jsonable_object,1535preparse:true or false, cb:function});15361537The data object is available when the string_to_execute is being1538evaluated as salvus.data. For example, if you execute this code1539in a cell::15401541javascript('''1542worksheet.execute_code({code:"a = salvus.data['b']/2; print(a)", data:{b:5},1543preparse:false, cb:function(mesg) { console.log(mesg)} });1544''')15451546then the Python variable a is set to 2, and the Javascript console log will display::15471548Object {done: false, event: "output", id: "..."}1549Object {stdout: "2\n", done: true, event: "output", id: "..."}15501551You can also send an interrupt signal to the Python process from1552Javascript by calling worksheet.interrupt(), and kill the process1553with worksheet.kill(). For example, here the a=4 never1554happens (but a=2 does)::15551556%javascript1557worksheet.execute_code({code:'a=2; sleep(100); a=4;',1558cb:function(mesg) { worksheet.interrupt(); console.log(mesg)}})15591560or using CoffeeScript (a Javascript preparser)::15611562%coffeescript1563worksheet.execute_code1564code : 'a=2; sleep(100); a=4;'1565cb : (mesg) ->1566worksheet.interrupt()1567console.log(mesg)15681569The Javascript code is evaluated with numerous standard Javascript libraries available,1570including jQuery, Twitter Bootstrap, jQueryUI, etc.15711572"""15731574for s in [coffeescript, javascript]:1575s.__doc__ += javascript_exec_doc157615771578def latex0(s=None, **kwds):1579"""1580Create and display an arbitrary LaTeX document as a png image in the Salvus Notebook.15811582In addition to directly calling latex.eval, you may put %latex (or %latex.eval(density=75, ...etc...))1583at the top of a cell, which will typeset everything else in the cell.1584"""1585if s is None:1586return lambda t: latex0(t, **kwds)1587if 'filename' not in kwds:1588import tempfile1589delete_file = True1590kwds['filename'] = tempfile.mkstemp(suffix=".png")[1]1591else:1592delete_file = False1593if 'locals' not in kwds:1594kwds['locals'] = salvus.namespace1595if 'globals' not in kwds:1596kwds['globals'] = salvus.namespace1597sage.misc.latex.latex.add_package_to_preamble_if_available('soul')1598sage.misc.latex.Latex.eval(sage.misc.latex.latex, s, **kwds)1599salvus.file(kwds['filename'], once=False)1600if delete_file:1601os.unlink(kwds['filename'])1602return ''160316041605latex0.__doc__ += sage.misc.latex.Latex.eval.__doc__160616071608class Time:1609"""1610Time execution of code exactly once in Salvus by:16111612- putting %time at the top of a cell to time execution of the entire cell1613- put %time at the beginning of line to time execution of just that line1614- write time('some code') to executation of the contents of the string.16151616If you want to time repeated execution of code for benchmarking purposes, use1617the timeit command instead.1618"""1619def __init__(self, start=False):1620if start:1621from sage.all import walltime, cputime1622self._start_walltime = walltime()1623self._start_cputime = cputime()16241625def before(self, code):1626return Time(start=True)16271628def after(self, code):1629from sage.all import walltime, cputime1630print(("\nCPU time: %.2f s, Wall time: %.2f s" %1631(cputime(self._start_cputime), walltime(self._start_walltime))))1632self._start_cputime = self._start_walltime = None16331634def __call__(self, code):1635from sage.all import walltime, cputime1636not_as_decorator = self._start_cputime is None1637if not_as_decorator:1638self.before(code)1639salvus.execute(code)1640if not_as_decorator:1641self.after(code)164216431644time = Time()164516461647def file(path):1648"""1649Block decorator to write to a file. Use as follows:16501651%file('filename') put this line in the file16521653or16541655%file('filename')1656everything in the rest of the1657cell goes into the file with given name.165816591660As with all block decorators in Salvus, the arguments to file can1661be arbitrary expressions. For examples,16621663a = 'file'; b = ['name', 'txt']16641665%file(a+b[0]+'.'+b[1]) rest of line goes in 'filename.txt'1666"""1667return lambda content: open(path, 'w').write(content)166816691670def timeit(*args, **kwds):1671"""1672Time execution of a command or block of commands.16731674This command has been enhanced for Salvus so you may use it as1675a block decorator as well, e.g.,16761677%timeit 2+316781679and16801681%timeit(number=10, preparse=False) 2^316821683%timeit(number=10, seconds=True) 2^316841685and16861687%timeit(preparse=False)16881689[rest of the cell]16901691Here is the original docstring for timeit:16921693"""1694def go(code):1695print((sage.misc.sage_timeit.sage_timeit(code,1696globals_dict=salvus.namespace,1697**kwds)))16981699if len(args) == 0:1700return lambda code: go(code)1701else:1702go(*args)170317041705# TODO: these need to also give the argspec1706timeit.__doc__ += sage.misc.sage_timeit.sage_timeit.__doc__170717081709class Capture:1710"""1711Capture or ignore the output from evaluating the given code. (SALVUS only).17121713Use capture as a block decorator by placing either %capture or1714%capture(optional args) at the beginning of a cell or at the1715beginning of a line. If you use just plain %capture then stdout1716and stderr are completely ignored. If you use %capture(args)1717you can redirect or echo stdout and stderr to variables or1718files. For example if you start a cell with this line::17191720%capture(stdout='output', stderr=open('error','w'), append=True, echo=True)17211722then stdout is appended (because append=True) to the global1723variable output, stderr is written to the file 'error', and the1724output is still displayed in the output portion of the cell (echo=True).17251726INPUT:17271728- stdout -- string (or object with write method) to send stdout output to (string=name of variable)1729- stderr -- string (or object with write method) to send stderr output to (string=name of variable)1730- append -- (default: False) if stdout/stderr are a string, append to corresponding variable1731- echo -- (default: False) if True, also echo stdout/stderr to the output cell.1732"""1733def __init__(self, stdout, stderr, append, echo):1734self.v = (stdout, stderr, append, echo)17351736def before(self, code):1737(stdout, stderr, append, echo) = self.v1738self._orig_stdout_f = orig_stdout_f = sys.stdout._f1739if stdout is not None:1740if hasattr(stdout, 'write'):17411742def write_stdout(buf):1743stdout.write(buf)1744elif is_string(stdout):1745if (stdout not in salvus.namespace) or not append:1746salvus.namespace[stdout] = ''1747if not is_string(salvus.namespace[stdout]):1748salvus.namespace[stdout] = str(salvus.namespace[stdout])17491750def write_stdout(buf):1751salvus.namespace[stdout] += buf1752else:1753raise TypeError(1754"stdout must be None, a string, or have a write method")17551756def f(buf, done):1757write_stdout(buf)1758if echo:1759orig_stdout_f(buf, done)1760elif done:1761orig_stdout_f('', done)17621763sys.stdout._f = f1764elif not echo:17651766def f(buf, done):1767if done:1768orig_stdout_f('', done)17691770sys.stdout._f = f17711772self._orig_stderr_f = orig_stderr_f = sys.stderr._f1773if stderr is not None:1774if hasattr(stderr, 'write'):17751776def write_stderr(buf):1777stderr.write(buf)1778elif is_string(stderr):1779if (stderr not in salvus.namespace) or not append:1780salvus.namespace[stderr] = ''1781if not is_string(salvus.namespace[stderr]):1782salvus.namespace[stderr] = str(salvus.namespace[stderr])17831784def write_stderr(buf):1785salvus.namespace[stderr] += buf1786else:1787raise TypeError(1788"stderr must be None, a string, or have a write method")17891790def f(buf, done):1791write_stderr(buf)1792if echo:1793orig_stderr_f(buf, done)1794elif done:1795orig_stderr_f('', done)17961797sys.stderr._f = f1798elif not echo:17991800def f(buf, done):1801if done:1802orig_stderr_f('', done)18031804sys.stderr._f = f18051806return self18071808def __call__(self,1809code=None,1810stdout=None,1811stderr=None,1812append=False,1813echo=False):1814if code is None:1815return Capture(stdout=stdout,1816stderr=stderr,1817append=append,1818echo=echo)1819if salvus._prefix:1820if not code.startswith("%"):1821code = salvus._prefix + '\n' + code1822salvus.execute(code)18231824def after(self, code):1825sys.stdout._f = self._orig_stdout_f1826sys.stderr._f = self._orig_stderr_f182718281829capture = Capture(stdout=None, stderr=None, append=False, echo=False)18301831import sage.misc.cython183218331834def asy(code=None, **kwds):1835# make a .pdf from .asy code and display it1836# asy command can also be used to make .eps file1837import tempfile1838import subprocess1839fname1 = tempfile.mkstemp(suffix=".asy")[1]1840fname2 = tempfile.mkstemp(suffix=".png")[1]1841with open(fname1, "w") as outf1:1842outf1.write(code + '\n')1843cmd = "/usr/bin/asy -offscreen -f png -o {} {}".format(fname2, fname1)1844subprocess.call(cmd.split())1845salvus.file(fname2)1846os.unlink(fname1)1847os.unlink(fname2)1848print('')184918501851def cython(code=None, **kwds):1852"""1853Block decorator to easily include Cython code in CoCalc worksheets.18541855Put %cython at the top of a cell, and the rest of that cell is compiled as1856Cython code and made directly available to use in other cells.18571858You can pass options to cython by typing "%cython(... var=value...)"1859instead of just "%cython".18601861If you give the option silent=True (not the default) then this won't1862print what functions get globally defined as a result of evaluating code.18631864This is a wrapper around Sage's own cython function, whose1865docstring is below:18661867ORIGINAL DOCSTRING:18681869"""1870if code is None:1871return lambda code: cython(code, **kwds)1872from sage.misc.temporary_file import tmp_dir1873path = tmp_dir()1874filename = os.path.join(path, 'a.pyx')1875open(filename, 'w').write(code)18761877silent = kwds.get('silent', False)1878if 'silent' in kwds:1879del kwds['silent']18801881if 'annotate' not in kwds and not silent:1882kwds['annotate'] = True18831884modname, path = sage.misc.cython.cython(filename, **kwds)18851886try:1887sys.path.insert(0, path)1888module = __import__(modname)1889finally:1890del sys.path[0]18911892defined = []1893for name, value in inspect.getmembers(module):1894if not name.startswith('_') and name != 'init_memory_functions':1895salvus.namespace[name] = value1896defined.append(name)1897if not silent:1898if defined:1899print(("Defined %s" % (', '.join(defined))))1900else:1901print("No functions defined.")19021903files = os.listdir(path)1904html_filename = None1905for n in files:1906base, ext = os.path.splitext(n)1907if ext.startswith('.html') and '_pyx_' in base:1908html_filename = os.path.join(path, n)1909if html_filename is not None:1910salvus.file(html_filename,1911raw=True,1912show=True,1913text="Auto-generated code...")191419151916cython.__doc__ += sage.misc.cython.cython.__doc__191719181919class script:1920r"""1921Block decorator to run an arbitrary shell command with input from a1922cell in Salvus.19231924Put %script('shell command line') or %script(['command', 'arg1',1925'arg2', ...]) by itself on a line in a cell, and the command line1926is run with stdin the rest of the contents of the cell. You can1927also use script in single line mode, e.g.,::19281929%script('gp -q') factor(2^97 - 1)19301931or19321933%script(['gp', '-q']) factor(2^97 - 1)19341935will launch a gp session, feed 'factor(2^97-1)' into stdin, and1936display the resulting factorization.19371938NOTE: the result is stored in the attribute "stdout", so you can do::19391940s = script('gp -q')1941%s factor(2^97-1)1942s.stdout1943'\n[11447 1]\n\n[13842607235828485645766393 1]\n\n'19441945and s.stdout will now be the output string.19461947You may also specify the shell environment with the env keyword.1948"""1949def __init__(self, args, env=None):1950self._args = args1951self._env = env19521953def __call__(self, code=''):1954import subprocess1955try:1956s = None1957s = subprocess.Popen(self._args,1958stdin=subprocess.PIPE,1959stdout=subprocess.PIPE,1960stderr=subprocess.STDOUT,1961shell=is_string(self._args),1962env=self._env)1963s.stdin.write(code)1964s.stdin.close()1965finally:1966if s is None:1967return1968try:1969self.stdout = s.stdout.read()1970sys.stdout.write(self.stdout)1971finally:1972try:1973os.system("pkill -TERM -P %s" % s.pid)1974except OSError:1975pass1976try:1977os.kill(s.pid, 9)1978except OSError:1979pass198019811982def python(code):1983"""1984Block decorator to run code in pure Python mode, without it being1985preparsed by the Sage preparser. Otherwise, nothing changes.19861987To use this, put %python by itself in a cell so that it applies to1988the rest of the cell, or put it at the beginning of a line to1989disable preparsing just for that line.1990"""1991salvus.execute(code, preparse=False)199219931994def python3(code=None, **kwargs):1995"""1996Block decorator to run code in a pure Python3 mode session.19971998To use this, put %python3 by itself in a cell so that it applies to1999the rest of the cell, or put it at the beginning of a line to2000run just that line using python3.20012002You can combine %python3 with capture, if you would like to capture2003the output to a variable. For example::20042005%capture(stdout='p3')2006%python32007x = set([1,2,3])2008print(x)20092010Afterwards, p3 contains the output '{1, 2, 3}' and the variable x2011in the controlling Sage session is in no way impacted.20122013.. note::20142015State is preserved between cells.2016CoCalc %python3 mode uses the jupyter `python3` kernel.2017"""2018if python3.jupyter_kernel is None:2019python3.jupyter_kernel = jupyter("python3")2020return python3.jupyter_kernel(code, **kwargs)202120222023python3.jupyter_kernel = None202420252026def anaconda(code=None, **kwargs):2027"""2028Block decorator to run code in a pure anaconda mode session.20292030To use this, put %anaconda by itself in a cell so that it applies to2031the rest of the cell, or put it at the beginning of a line to2032run just that line using anaconda.20332034You can combine %anaconda with capture, if you would like to capture2035the output to a variable. For example::20362037%capture(stdout='a')2038%anaconda2039x = set([1,2,3])2040print(x)20412042Afterwards, a contains the output '{1, 2, 3}' and the variable x2043in the controlling Sage session is in no way impacted.20442045.. note::20462047State is preserved between cells.2048CoCalc %anaconda mode uses the jupyter `anaconda5` kernel.2049"""2050if anaconda.jupyter_kernel is None:2051anaconda.jupyter_kernel = jupyter("anaconda5")2052return anaconda.jupyter_kernel(code, **kwargs)205320542055anaconda.jupyter_kernel = None205620572058def singular_kernel(code=None, **kwargs):2059"""2060Block decorator to run code in a Singular mode session.20612062To use this, put %singular_kernel by itself in a cell so that it applies to2063the rest of the cell, or put it at the beginning of a line to2064run just that line using singular_kernel.20652066State is preserved between cells.20672068This is completely different than the singular command in Sage itself, which2069supports things like x = singular(sage_object), and *also* provides a way2070to execute code by beginning cells with %singular. The singular interface in2071Sage uses pexpect, so might be less robust than singular_kernel.20722073.. note::20742075SMC %singular_kernel mode uses the jupyter `singular` kernel:2076https://github.com/sebasguts/jupyter_kernel_singular2077"""2078if singular_kernel.jupyter_kernel is None:2079singular_kernel.jupyter_kernel = jupyter("singular")2080return singular_kernel.jupyter_kernel(code, **kwargs)208120822083singular_kernel.jupyter_kernel = None208420852086def perl(code):2087"""2088Block decorator to run code in a Perl session.20892090To use this, put %perl by itself in a cell so that it applies to2091the rest of the cell, or put it at the beginning of a line to2092run just that line using perl.20932094EXAMPLE:20952096A perl cell::20972098%perl2099$apple_count = 5;2100$count_report = "There are $apple_count apples.";2101print "The report is: $count_report\n";21022103Or use %perl on one line::21042105%perl $apple_count = 5; $count_report = "There are $apple_count apples."; print "The report is: $count_report\n";21062107You can combine %perl with capture, if you would like to capture2108the output to a variable. For example::21092110%capture(stdout='p')2111%perl print "hi"21122113Afterwards, p contains 'hi'.21142115NOTE: No state is preserved between calls. Each call is a separate process.2116"""2117script('sage-native-execute perl')(code)211821192120def ruby(code):2121"""2122Block decorator to run code in a Ruby session.21232124To use this, put %ruby by itself in a cell so that it applies to2125the rest of the cell, or put it at the beginning of a line to2126run just that line using ruby.21272128EXAMPLE:21292130A ruby cell::21312132%ruby2133lang = "ruby"2134print "Hello from #{lang}!"21352136Or use %ruby on one line::21372138%ruby lang = "ruby"; print "Hello from #{lang}!"21392140You can combine %ruby with capture, if you would like to capture2141the output to a variable. For example::21422143%capture(stdout='p')2144%ruby lang = "ruby"; print "Hello from #{lang}!"21452146Afterwards, p contains 'Hello from ruby!'.21472148NOTE: No state is preserved between calls. Each call is a separate process.2149"""2150script('sage-native-execute ruby')(code)215121522153def fortran(x, library_paths=[], libraries=[], verbose=False):2154"""2155Compile Fortran code and make it available to use.21562157INPUT:21582159- x -- a string containing code21602161Use this as a decorator. For example, put this in a cell and evaluate it::21622163%fortran21642165C FILE: FIB1.F2166SUBROUTINE FIB(A,N)2167C2168C CALCULATE FIRST N FIBONACCI NUMBERS2169C2170INTEGER N2171REAL*8 A(N)2172DO I=1,N2173IF (I.EQ.1) THEN2174A(I) = 0.0D02175ELSEIF (I.EQ.2) THEN2176A(I) = 1.0D02177ELSE2178A(I) = A(I-1) + A(I-2)2179ENDIF2180ENDDO2181END2182C END FILE FIB1.F218321842185In the next cell, evaluate this::21862187import numpy2188n = numpy.array(range(10),dtype=float)2189fib(n,int(10))2190n21912192This will produce this output: array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.])2193"""2194import builtins2195from sage.misc.temporary_file import tmp_dir2196if len(x.splitlines()) == 1 and os.path.exists(x):2197filename = x2198x = open(x).read()2199if filename.lower().endswith('.f90'):2200x = '!f90\n' + x22012202from numpy import f2py2203from random import randint22042205# Create everything in a temporary directory2206mytmpdir = tmp_dir()22072208try:2209old_cwd = os.getcwd()2210os.chdir(mytmpdir)22112212old_import_path = os.sys.path2213os.sys.path.append(mytmpdir)22142215name = "fortran_module_%s" % randint(0, 2**64) # Python module name2216# if the first line has !f90 as a comment, gfortran will2217# treat it as Fortran 90 code2218if x.startswith('!f90'):2219fortran_file = name + '.f90'2220else:2221fortran_file = name + '.f'22222223s_lib_path = ""2224s_lib = ""2225for s in library_paths:2226s_lib_path = s_lib_path + "-L%s " % s22272228for s in libraries:2229s_lib = s_lib + "-l%s " % s22302231log = name + ".log"2232extra_args = '--quiet --f77exec=sage-inline-fortran --f90exec=sage-inline-fortran %s %s >"%s" 2>&1' % (2233s_lib_path, s_lib, log)22342235f2py.compile(x, name, extra_args=extra_args, source_fn=fortran_file)2236log_string = open(log).read()22372238# f2py.compile() doesn't raise any exception if it fails.2239# So we manually check whether the compiled file exists.2240# NOTE: the .so extension is used expect on Cygwin,2241# that is even on OS X where .dylib might be expected.2242soname = name2243uname = os.uname()[0].lower()2244if uname[:6] == "cygwin":2245soname += '.dll'2246else:2247soname += '.so'2248if not os.path.isfile(soname):2249raise RuntimeError("failed to compile Fortran code:\n" +2250log_string)22512252if verbose:2253print(log_string)22542255m = builtins.__import__(name)22562257finally:2258os.sys.path = old_import_path2259os.chdir(old_cwd)2260try:2261import shutil2262shutil.rmtree(mytmpdir)2263except OSError:2264# This can fail for example over NFS2265pass22662267for k, x in m.__dict__.items():2268if k[0] != '_':2269salvus.namespace[k] = x227022712272def sh(code=None, **kwargs):2273"""2274Run a bash script in Salvus. Uses jupyter bash kernel2275which allows keeping state between cells.22762277EXAMPLES:22782279Use as a block decorator on a single line::22802281%sh pwd22822283and multiline22842285%sh2286echo "hi"2287pwd2288ls -l22892290You can also just directly call it::22912292sh('pwd')22932294The output is printed. To capture it, use capture22952296%capture(stdout='output')2297%sh pwd22982299After that, the variable output contains the current directory23002301Remember shell state between cells23022303%sh2304FOO='xyz'2305cd /tmp2306... new cell will show settings from previous cell ...2307%sh2308echo $FOO2309pwd23102311Display image file (this is a feature of jupyter bash kernel)23122313%sh2314display < sage_logo.png23152316.. WARNING::23172318The jupyter bash kernel does not separate stdout and stderr as cell is running.2319It only returns ok or error depending on exit status of last command in the cell.2320So all cell output captured goes to either stdout or stderr variable, depending2321on exit status of the last command in the %sh cell.2322"""2323if sh.jupyter_kernel is None:2324sh.jupyter_kernel = jupyter("bash")2325sh.jupyter_kernel(2326'function command_not_found_handle { printf "%s: command not found\n" "$1" >&2; return 127;}'2327)2328return sh.jupyter_kernel(code, **kwargs)232923302331sh.jupyter_kernel = None233223332334# use jupyter kernel for GNU octave instead of sage interpreter interface2335def octave(code=None, **kwargs):2336r"""2337Run GNU Octave code in a sage worksheet.23382339INPUT:23402341- ``code`` -- a string containing code23422343Use as a decorator. For example, put this in a cell and evaluate it::23442345%octave2346x = -10:0.1:10;2347plot (x, sin (x))23482349.. note::23502351SMC %octave mode uses the jupyter `octave` kernel.2352"""2353if octave.jupyter_kernel is None:2354octave.jupyter_kernel = jupyter("octave")2355octave.jupyter_kernel.smc_image_scaling = 12356return octave.jupyter_kernel(code, **kwargs)235723582359octave.jupyter_kernel = None236023612362# jupyter kernel for %ir mode2363def r(code=None, **kwargs):2364r"""2365Run R code in a sage worksheet.23662367INPUT:23682369- ``code`` -- a string containing code23702371Use as a decorator. For example, put this in a cell and evaluate it to see a scatter plot2372of built-in mtcars dataframe variables `mpg` vs `wt`::23732374%r2375with(mtcars,plot(wt,mpg))23762377.. note::23782379SMC %r mode uses the jupyter `ir` kernel.2380"""2381if r.jupyter_kernel is None:2382r.jupyter_kernel = jupyter("ir")2383r.jupyter_kernel('options(repr.plot.res = 240)')2384r.jupyter_kernel.smc_image_scaling = .52385return r.jupyter_kernel(code, **kwargs)238623872388r.jupyter_kernel = None238923902391# jupyter kernel for %scala mode2392def scala211(code=None, **kwargs):2393r"""2394Run scala code in a sage worksheet.23952396INPUT:23972398- ``code`` -- a string containing code23992400Use as a decorator.24012402.. note::24032404SMC %scala211 mode uses the jupyter `scala211` kernel.2405"""2406if scala211.jupyter_kernel is None:2407scala211.jupyter_kernel = jupyter("scala211")2408return scala211.jupyter_kernel(code, **kwargs)240924102411scala211.jupyter_kernel = None2412# add alias for generic scala2413scala = scala211241424152416def prun(code):2417"""2418Use %prun followed by a block of code to profile execution of that2419code. This will display the resulting profile, along with a menu2420to select how to sort the data.24212422EXAMPLES:24232424Profile computing a tricky integral (on a single line)::24252426%prun integrate(sin(x^2),x)24272428Profile a block of code::24292430%prun2431E = EllipticCurve([1..5])2432v = E.anlist(10^5)2433r = E.rank()2434"""2435import cProfile, pstats2436from sage.misc.all import tmp_filename24372438filename = tmp_filename()2439cProfile.runctx(salvus.namespace['preparse'](code), salvus.namespace,2440locals(), filename)24412442@interact2443def f(title=text_control('', "<h1>CoCalc Profiler</h1>"),2444sort=(2445"First sort by",2446selector([2447('calls', 'number of calls to the function'),2448('time', ' total time spent in the function'),2449('cumulative',2450'total time spent in this and all subfunctions (from invocation till exit)'2451),2452('module', 'name of the module that contains the function'),2453('name', 'name of the function')2454],2455width="100%",2456default='time')),2457strip_dirs=True):2458try:2459p = pstats.Stats(filename)2460if strip_dirs:2461p.strip_dirs()2462p.sort_stats(sort)2463p.print_stats()2464except Exception as msg:2465print(msg)246624672468##############################################################2469# The %fork cell decorator.2470##############################################################247124722473def _wait_in_thread(pid, callback, filename):2474from sage.structure.sage_object import load24752476def wait():2477try:2478os.waitpid(pid, 0)2479callback(load(filename))2480except Exception as msg:2481callback(msg)24822483from threading import Thread2484t = Thread(target=wait, args=tuple([]))2485t.start()248624872488def async_(f, args, kwds, callback):2489"""2490Run f in a forked subprocess with given args and kwds, then call the2491callback function when f terminates.2492"""2493from sage.misc.all import tmp_filename2494filename = tmp_filename() + '.sobj'2495sys.stdout.flush()2496sys.stderr.flush()2497pid = os.fork()2498if pid:2499# The parent master process2500try:2501_wait_in_thread(pid, callback, filename)2502return pid2503finally:2504if os.path.exists(filename):2505os.unlink(filename)2506else:2507# The child process2508try:2509result = f(*args, **kwds)2510except Exception as msg:2511result = str(msg)2512from sage.structure.sage_object import save2513save(result, filename)2514os._exit(0)251525162517class Fork(object):2518"""2519The %fork block decorator evaluates its code in a forked subprocess2520that does not block the main process.25212522You may still use the @fork function decorator from Sage, as usual,2523to run a function in a subprocess. Type "sage.all.fork?" to see2524the help for the @fork decorator.25252526WARNING: This is highly experimental and possibly flaky. Use with2527caution.25282529All (picklelable) global variables that are set in the forked2530subprocess are set in the parent when the forked subprocess2531terminates. However, the forked subprocess has no other side2532effects, except what it might do to file handles and the2533filesystem.25342535To see currently running forked subprocesses, type2536fork.children(), which returns a dictionary {pid:execute_uuid}.2537To kill a given subprocess and stop the cell waiting for input,2538type fork.kill(pid). This is currently the only way to stop code2539running in %fork cells.25402541TODO/WARNING: The subprocesses spawned by fork are not killed2542if the parent process is killed first!25432544NOTE: All pexpect interfaces are reset in the child process.2545"""2546def __init__(self):2547self._children = {}25482549def children(self):2550return dict(self._children)25512552def __call__(self, s):25532554if isinstance(s, types.FunctionType): # check for decorator usage2555import sage.parallel.decorate2556return sage.parallel.decorate.fork(s)25572558salvus._done = False25592560id = salvus._id25612562changed_vars = set([])25632564def change(var, val):2565changed_vars.add(var)25662567def f():2568# Run some commands to tell Sage that its2569# pid has changed.2570import sage.misc.misc2571imp.reload(sage.misc.misc)25722573# The pexpect interfaces (and objects defined in them) are2574# not valid.2575sage.interfaces.quit.invalidate_all()25762577salvus.namespace.on('change', None, change)2578salvus.execute(s)2579result = {}2580from sage.structure.sage_object import dumps2581for var in changed_vars:2582try:2583result[var] = dumps(salvus.namespace[var])2584except:2585result[var] = 'unable to pickle %s' % var2586return result25872588from sage.structure.sage_object import loads25892590def g(s):2591if isinstance(s, Exception):2592sys.stderr.write(str(s))2593sys.stderr.flush()2594else:2595for var, val in s.items():2596try:2597salvus.namespace[var] = loads(val)2598except:2599print(("unable to unpickle %s" % var))2600salvus._conn.send_json({'event': 'output', 'id': id, 'done': True})2601if pid in self._children:2602del self._children[pid]26032604pid = async_(f, tuple([]), {}, g)2605print(("Forked subprocess %s" % pid))2606self._children[pid] = id26072608def kill(self, pid):2609if pid in self._children:2610salvus._conn.send_json({2611'event': 'output',2612'id': self._children[pid],2613'done': True2614})2615os.kill(pid, 9)2616del self._children[pid]2617else:2618raise ValueError("Unknown pid = (%s)" % pid)261926202621fork = Fork()26222623####################################################2624# Display of 2d/3d graphics objects2625####################################################26262627from sage.misc.all import tmp_filename2628from sage.plot.animate import Animation2629import matplotlib.figure263026312632def show_animation(obj, delay=20, gif=False, **kwds):2633if gif:2634t = tmp_filename(ext='.gif')2635obj.gif(delay, t, **kwds)2636salvus.file(t, raw=False)2637os.unlink(t)2638else:2639t = tmp_filename(ext='.webm')2640obj.ffmpeg(t, delay=delay, **kwds)2641# and let delete when worksheet ends - need this so can replay video.2642salvus.file(t, raw=True)264326442645def show_2d_plot_using_matplotlib(obj, svg, **kwds):2646if isinstance(obj, matplotlib.image.AxesImage):2647# The result of imshow, e.g.,2648#2649# from matplotlib import numpy, pyplot2650# pyplot.imshow(numpy.random.random_integers(255, size=(100,100,3)))2651#2652t = tmp_filename(ext='.png')2653obj.write_png(t)2654salvus.file(t)2655os.unlink(t)2656return26572658if isinstance(obj, matplotlib.axes.Axes):2659obj = obj.get_figure()26602661if 'events' in kwds:2662from smc_sagews.graphics import InteractiveGraphics2663ig = InteractiveGraphics(obj, **kwds['events'])2664n = '__a' + uuid().replace(2665'-', '') # so it doesn't get garbage collected instantly.2666obj.__setattr__(n, ig)2667kwds2 = dict(kwds)2668del kwds2['events']2669ig.show(**kwds2)2670else:2671t = tmp_filename(ext='.svg' if svg else '.png')2672if isinstance(obj, matplotlib.figure.Figure):2673obj.savefig(t, **kwds)2674else:2675obj.save(t, **kwds)2676salvus.file(t)2677os.unlink(t)267826792680def show_3d_plot_using_tachyon(obj, **kwds):2681t = tmp_filename(ext='.png')2682obj.save(t, **kwds)2683salvus.file(t)2684os.unlink(t)268526862687def show_graph_using_d3(obj, **kwds):2688salvus.d3_graph(obj, **kwds)268926902691def plot3d_using_matplotlib(expr,2692rangeX,2693rangeY,2694density=40,2695elev=45.,2696azim=35.,2697alpha=0.85,2698cmap=None):2699"""2700Plots a symbolic expression in two variables on a two dimensional grid2701and renders the function using matplotlib's 3D projection.2702The purpose is to make it possible to create vectorized images (PDF, SVG)2703for high-resolution images in publications -- instead of rasterized image formats.27042705Example::2706%var x y2707plot3d_using_matplotlib(x^2 + (1-y^2), (x, -5, 5), (y, -5, 5))27082709Arguments::27102711* expr: symbolic expression, e.g. x^2 - (1-y)^22712* rangeX: triple: (variable, minimum, maximum), e.g. (x, -10, 10)2713* rangeY: like rangeX2714* density: grid density2715* elev: elevation, e.g. 452716* azim: azimuth, e.g. 352717* alpha: alpha transparency of plot (default: 0.85)2718* cmap: matplotlib colormap, e.g. matplotlib.cm.Blues (default)2719"""2720from matplotlib import cm2721import matplotlib.pyplot as plt2722from mpl_toolkits.mplot3d import axes3d2723import numpy as np27242725cmap = cmap or cm.Blues27262727plt.cla()2728fig = plt.figure()2729ax = fig.gca(projection='3d')2730ax.view_init(elev=elev, azim=azim)27312732xx = np.linspace(rangeX[1], rangeX[2], density)2733yy = np.linspace(rangeY[1], rangeY[2], density)2734X, Y = np.meshgrid(xx, yy)27352736import numpy as np2737exprv = np.vectorize(lambda x1, x2 : \2738float(expr.subs({rangeX[0] : x1, rangeY[0] : x2})))2739Z = exprv(X, Y)2740zlim = np.min(Z), np.max(Z)27412742ax.plot_surface(X,2743Y,2744Z,2745alpha=alpha,2746cmap=cmap,2747linewidth=.5,2748shade=True,2749rstride=int(len(xx) / 10),2750cstride=int(len(yy) / 10))27512752ax.set_xlabel('X')2753ax.set_xlim(*rangeX[1:])2754ax.set_ylabel('Y')2755ax.set_ylim(*rangeY[1:])2756ax.set_zlabel('Z')2757ax.set_zlim(*zlim)27582759plt.show()276027612762# Sage version 8.9 introduced2763# https://doc.sagemath.org/html/en/reference/plotting/sage/plot/multigraphics.html#sage.plot.multigraphics.MultiGraphics2764# which complicates the logic below.2765try:2766# Try to import both GraphicsArray and MultiGraphics2767from sage.plot.multigraphics import GraphicsArray, MultiGraphics2768except:2769# Import failed, so probably 8.9 -- we try to import GraphicsArray.2770# If this also fails, then Sage has changed a lot and some manual work is needed.2771from sage.plot.graphics import GraphicsArray2772# Also ensure MultiGraphics is defined but None. We'll have to2773# check for None in the places where MultiGraphics is used below.2774MultiGraphics = None27752776from sage.plot.graphics import Graphics2777from sage.plot.plot3d.base import Graphics3d2778from sage.plot.plot3d.tachyon import Tachyon27792780# used in show function2781GRAPHICS_MODULES_SHOW = [2782Graphics,2783GraphicsArray,2784matplotlib.figure.Figure,2785matplotlib.axes.Axes,2786matplotlib.image.AxesImage,2787]27882789if MultiGraphics is not None:2790GRAPHICS_MODULES_SHOW.append(MultiGraphics)27912792GRAPHICS_MODULES_SHOW = tuple(GRAPHICS_MODULES_SHOW)279327942795def show(*objs, **kwds):2796"""2797Show a 2d or 3d graphics object (or objects), animation, or matplotlib figure, or show an2798expression typeset nicely using LaTeX.27992800- display: (default: True); if True, use display math for expression (big and centered).28012802- svg: (default: True); if True, show 2d plots using svg (otherwise use png)28032804- d3: (default: True); if True, show graphs (vertices and edges) using an interactive D3 viewer2805for the many options for this viewer, type28062807import smc_sagews.graphics2808smc_sagews.graphics.graph_to_d3_jsonable?28092810If false, graphs are converted to plots and displayed as usual.28112812- renderer: (default: 'webgl'); for 3d graphics2813- 'webgl' (fastest) using hardware accelerated 3d;2814- 'canvas' (slower) using a 2d canvas, but may work better with transparency;2815- 'tachyon' -- a ray traced static image.28162817- spin: (default: False); spins 3d plot, with number determining speed (requires mouse over plot)28182819- events: if given, {'click':foo, 'mousemove':bar}; each time the user clicks,2820the function foo is called with a 2-tuple (x,y) where they clicked. Similarly2821for mousemove. This works for Sage 2d graphics and matplotlib figures.28222823- viewer: optional string, set to "tachyon" for static ray-tracing view of 3d image28242825- background: string (default: 'transparent'), specifies background color for 3d images.2826Ignored if viewer is set to 'tachyon' or if object type is Tachyon.2827May be 'transparent' or any valid CSS color string, e.g.: 'red', '#00ff00', 'rgb(0,0,255)'.28282829- foreground: string, specifies frame color for 3d images. Defaults to 'gray' when2830background is 'transparent', otherwise default is computed for visibility based on canvas2831background.28322833ANIMATIONS:28342835- animations are by default encoded and displayed using an efficiently web-friendly2836format (currently webm, which is **not supported** by Safari or IE).28372838- ``delay`` - integer (default: 20); delay in hundredths of a2839second between frames.28402841- gif=False -- if you set gif=True, instead use an animated gif,2842which is much less efficient, but works on all browsers.28432844You can also use options directly to the animate command, e.g., the figsize option below:28452846a = animate([plot(sin(x + a), (x, 0, 2*pi)) for a in [0, pi/4, .., 2*pi]], figsize=6)2847show(a, delay=30)284828492850EXAMPLES:28512852Some examples:28532854show(2/3)2855show([1, 4/5, pi^2 + e], 1+pi)2856show(x^2, display=False)2857show(e, plot(sin))28582859Here's an example that illustrates creating a clickable image with events::28602861@interact2862def f0(fun=x*sin(x^2), mousemove='', click='(0,0)'):2863click = sage_eval(click)2864g = plot(fun, (x,0,5), zorder=0) + point(click, color='red', pointsize=100, zorder=10)2865ymax = g.ymax(); ymin = g.ymin()2866m = fun.derivative(x)(x=click[0])2867b = fun(x=click[0]) - m*click[0]2868g += plot(m*x + b, (click[0]-1,click[0]+1), color='red', zorder=10)2869def h(p):2870f0.mousemove = p2871def c(p):2872f0(click=p)2873show(g, events={'click':c, 'mousemove':h}, svg=True, gridlines='major', ymin=ymin, ymax=ymax)2874"""2875# svg=True, d3=True,2876svg = kwds.get('svg', True)2877d3 = kwds.get('d3', True)2878display = kwds.get('display', True)2879for t in ['svg', 'd3', 'display']:2880if t in kwds:2881del kwds[t]2882from smc_sagews import graphics28832884def show0(obj, combine_all=False):2885# Either show the object and return None or2886# return a string of html to represent obj.2887if isinstance(obj, GRAPHICS_MODULES_SHOW):2888show_2d_plot_using_matplotlib(obj, svg=svg, **kwds)2889elif isinstance(obj, Animation):2890show_animation(obj, **kwds)2891elif isinstance(obj, Graphics3d):28922893# _extra_kwds processing follows the example of2894extra_kwds = {} if obj._extra_kwds is None else obj._extra_kwds2895for k in [2896'spin',2897'renderer',2898'viewer',2899'frame',2900'height',2901'width',2902'background',2903'foreground',2904'aspect_ratio',2905]:2906if k in extra_kwds and k not in kwds:2907kwds[k] = obj._extra_kwds[k]29082909if kwds.get('viewer') == 'tachyon':2910show_3d_plot_using_tachyon(obj, **kwds)2911else:2912if kwds.get('viewer') == 'threejs':2913del kwds['viewer']2914if kwds.get('online'):2915del kwds['online']2916salvus.threed(obj, **kwds)2917elif isinstance(obj, Tachyon):2918show_3d_plot_using_tachyon(obj, **kwds)2919elif isinstance(2920obj, (sage.graphs.graph.Graph, sage.graphs.digraph.DiGraph)):2921if d3:2922show_graph_using_d3(obj, **kwds)2923else:2924show(obj.plot(), **kwds)2925elif is_string(obj):2926return obj2927elif isinstance(obj, (list, tuple)):2928v = []2929for a in obj:2930b = show0(a)2931if b is not None:2932v.append(b)2933if combine_all:2934return ' '.join(v)2935s = ', '.join(v)2936if isinstance(obj, list):2937return '[%s]' % s2938else:2939return '(%s)' % s2940elif is_dataframe(obj):2941html(obj.to_html(), hide=False)2942else:2943__builtins__['_'] = obj2944s = str(sage.misc.latex.latex(obj))2945if r'\text{\texttt' in s and 'tikzpicture' not in s:2946# In this case the mathjax latex mess is so bad, it is better to just print and give up!2947print(obj)2948return2949# Add anything here that Sage produces and mathjax can't handle, and2950# which people complain about... (obviously, I wish there were a way to2951# know -- e.g., if Sage had a way to tell whether latex it produces2952# will work with mathjax or not).2953if '\\begin{tikzpicture}' in s or '\\raisebox' in s:2954# special case -- mathjax has no support for tikz or \raisebox so we just immediately display it (as a png); this is2955# better than nothing.2956sage.misc.latex.latex.eval(s)2957return ''2958elif r'\begin{tabular}' in s:2959# tabular is an environment for text, not formular.2960# Sage's `tabular` should actually use \array!2961sage.misc.latex.latex.eval(s)2962return ''2963# default2964elif display:2965return "$\\displaystyle %s$" % s2966else:2967return "$%s$" % s29682969sys.stdout.flush()2970sys.stderr.flush()2971s = show0(objs, combine_all=True)29722973if six.PY3:2974from html import escape2975elif six.PY2:2976# deprecated in py32977from cgi import escape29782979if s is not None:2980if len(s) > 0:2981if display:2982salvus.html("<div align='center'>%s</div>" % escape(s))2983else:2984salvus.html("<div>%s</div>" % escape(s))2985sys.stdout.flush()2986sys.stderr.flush()298729882989# Make it so plots plot themselves correctly when they call their repr.2990Graphics.show = show2991GraphicsArray.show = show2992if MultiGraphics is not None:2993MultiGraphics.show = show2994Animation.show = show29952996# Very "evil" abuse of the display manager, so sphere().show() works:2997try:2998from sage.repl.rich_output import get_display_manager2999get_display_manager().display_immediately = show3000except:3001# so doesn't crash on older versions of Sage.3002pass300330043005###################################################3006# %auto -- automatically evaluate a cell on load3007###################################################3008def auto(s):3009"""3010The %auto decorator sets a cell so that it will be automatically3011executed when the Sage process first starts. Make it the first3012line of a cell.30133014Thus %auto allows you to initialize functions, variables, interacts,3015etc., e.g., when loading a worksheet.3016"""3017return s # the do-nothing block decorator.301830193020def hide(component='input'):3021"""3022Hide a component of a cell. By default, hide hides the the code3023editor part of the cell, but you can hide other parts by passing3024in an optional argument:30253026'input', 'output'30273028Use the cell.show(...) function to reveal a cell component.3029"""3030if component not in ['input', 'output']:3031# Allow %hide to work, for compatability with sagenb.3032hide('input')3033return component3034cell.hide(component)303530363037def hideall(code=None):3038cell.hideall()3039if code is not None: # for backwards compat with sagenb3040return code304130423043##########################################################3044# A "%exercise" cell mode -- a first step toward3045# automated homework.3046##########################################################3047class Exercise:3048def __init__(self, question, answer, check=None, hints=None):3049import sage.all3050from sage.structure.element import is_Matrix3051if not (isinstance(answer, (tuple, list)) and len(answer) == 2):3052if is_Matrix(answer):3053default = sage.all.parent(answer)(0)3054else:3055default = ''3056answer = [answer, default]30573058if check is None:3059R = sage.all.parent(answer[0])30603061def check(attempt):3062return R(attempt) == answer[0]30633064if hints is None:3065hints = ['', '', '', "The answer is %s." % answer[0]]30663067self._question = question3068self._answer = answer3069self._check = check3070self._hints = hints30713072def _check_attempt(self, attempt, interact):3073from sage.misc.all import walltime3074response = "<div class='well'>"3075try:3076r = self._check(attempt)3077if isinstance(r, tuple) and len(r) == 2:3078correct = r[0]3079comment = r[1]3080else:3081correct = bool(r)3082comment = ''3083except TypeError as msg:3084response += "<h3 style='color:darkgreen'>Huh? -- %s (attempt=%s)</h3>" % (3085msg, attempt)3086else:3087if correct:3088response += "<h1 style='color:blue'>RIGHT!</h1>"3089if self._start_time:3090response += "<h2 class='lighten'>Time: %.1f seconds</h2>" % (3091walltime() - self._start_time, )3092if self._number_of_attempts == 1:3093response += "<h3 class='lighten'>You got it first try!</h3>"3094else:3095response += "<h3 class='lighten'>It took you %s attempts.</h3>" % (3096self._number_of_attempts, )3097else:3098response += "<h3 style='color:darkgreen'>Not correct yet...</h3>"3099if self._number_of_attempts == 1:3100response += "<h4 style='lighten'>(first attempt)</h4>"3101else:3102response += "<h4 style='lighten'>(%s attempts)</h4>" % self._number_of_attempts31033104if self._number_of_attempts > len(self._hints):3105hint = self._hints[-1]3106else:3107hint = self._hints[self._number_of_attempts - 1]3108if hint:3109response += "<span class='lighten'>(HINT: %s)</span>" % (3110hint, )3111if comment:3112response += '<h4>%s</h4>' % comment31133114response += "</div>"31153116interact.feedback = text_control(response, label='')31173118return correct31193120def ask(self, cb):3121from sage.misc.all import walltime3122self._start_time = walltime()3123self._number_of_attempts = 03124attempts = []31253126@interact(layout=[[('question', 12)], [('attempt', 12)],3127[('feedback', 12)]])3128def f(question=("<b>Question:</b>", text_control(self._question)),3129attempt=('<b>Answer:</b>', self._answer[1])):3130if 'attempt' in interact.changed() and attempt != '':3131attempts.append(attempt)3132if self._start_time == 0:3133self._start_time = walltime()3134self._number_of_attempts += 13135if self._check_attempt(attempt, interact):3136cb({3137'attempts': attempts,3138'time': walltime() - self._start_time3139})314031413142def exercise(code):3143r"""3144Use the %exercise cell decorator to create interactive exercise3145sets. Put %exercise at the top of the cell, then write Sage code3146in the cell that defines the following (all are optional):31473148- a ``question`` variable, as an HTML string with math in dollar3149signs31503151- an ``answer`` variable, which can be any object, or a pair3152(correct_value, interact control) -- see the docstring for3153interact for controls.31543155- an optional callable ``check(answer)`` that returns a boolean or3156a 2-tuple31573158(True or False, message),31593160where the first argument is True if the answer is correct, and3161the optional second argument is a message that should be3162displayed in response to the given answer. NOTE: Often the3163input "answer" will be a string, so you may have to use Integer,3164RealNumber, or sage_eval to evaluate it, depending3165on what you want to allow the user to do.31663167- hints -- optional list of strings to display in sequence each3168time the user enters a wrong answer. The last string is3169displayed repeatedly. If hints is omitted, the correct answer3170is displayed after three attempts.31713172NOTE: The code that defines the exercise is executed so that it3173does not impact (and is not impacted by) the global scope of your3174variables elsewhere in your session. Thus you can have many3175%exercise cells in a single worksheet with no interference between3176them.31773178The following examples further illustrate how %exercise works.31793180An exercise to test your ability to sum the first $n$ integers::31813182%exercise3183title = "Sum the first n integers, like Gauss did."3184n = randint(3, 100)3185question = "What is the sum $1 + 2 + \\cdots + %s$ of the first %s positive integers?"%(n,n)3186answer = n*(n+1)//231873188Transpose a matrix::31893190%exercise3191title = r"Transpose a $2 \times 2$ Matrix"3192A = random_matrix(ZZ,2)3193question = "What is the transpose of $%s?$"%latex(A)3194answer = A.transpose()31953196Add together a few numbers::31973198%exercise3199k = randint(2,5)3200title = "Add %s numbers"%k3201v = [randint(1,10) for _ in range(k)]3202question = "What is the sum $%s$?"%(' + '.join([str(x) for x in v]))3203answer = sum(v)32043205The trace of a matrix::32063207%exercise3208title = "Compute the trace of a matrix."3209A = random_matrix(ZZ, 3, x=-5, y = 5)^23210question = "What is the trace of $$%s?$$"%latex(A)3211answer = A.trace()32123213Some basic arithmetic with hints and dynamic feedback::32143215%exercise3216k = randint(2,5)3217title = "Add %s numbers"%k3218v = [randint(1,10) for _ in range(k)]3219question = "What is the sum $%s$?"%(' + '.join([str(x) for x in v]))3220answer = sum(v)3221hints = ['This is basic arithmetic.', 'The sum is near %s.'%(answer+randint(1,5)), "The answer is %s."%answer]3222def check(attempt):3223c = Integer(attempt) - answer3224if c == 0:3225return True3226if abs(c) >= 10:3227return False, "Gees -- not even close!"3228if c < 0:3229return False, "too low"3230if c > 0:3231return False, "too high"3232"""3233f = closure(code)32343235def g():3236x = f()3237return x.get('title',3238''), x.get('question', ''), x.get('answer', ''), x.get(3239'check', None), x.get('hints', None)32403241title, question, answer, check, hints = g()3242obj = {}3243obj['E'] = Exercise(question, answer, check, hints)3244obj['title'] = title32453246def title_control(t):3247return text_control('<h3 class="lighten">%s</h3>' % t)32483249the_times = []32503251@interact(layout=[[('go', 1), ('title', 11, '')], [('')],3252[('times', 12, "<b>Times:</b>")]],3253flicker=True)3254def h(go=button(" " * 5 + "Go" + " " * 7,3255label='',3256icon='fa-refresh',3257classes="btn-large btn-success"),3258title=title_control(title),3259times=text_control('')):3260c = interact.changed()3261if 'go' in c or 'another' in c:3262interact.title = title_control(obj['title'])32633264def cb(obj):3265the_times.append("%.1f" % obj['time'])3266h.times = ', '.join(the_times)32673268obj['E'].ask(cb)32693270title, question, answer, check, hints = g(3271) # get ready for next time.3272obj['title'] = title3273obj['E'] = Exercise(question, answer, check, hints)327432753276def closure(code):3277"""3278Wrap the given code block (a string) in a closure, i.e., a3279function with an obfuscated random name.32803281When called, the function returns locals().3282"""3283import uuid3284# TODO: strip string literals first3285code = ' ' + ('\n '.join(code.splitlines()))3286fname = "__" + str(uuid.uuid4()).replace('-', '_')3287closure = "def %s():\n%s\n return locals()" % (fname, code)32883289class Closure:3290def __call__(self):3291return self._f()32923293c = Closure()3294salvus.execute(closure)3295c._f = salvus.namespace[fname]3296del salvus.namespace[fname]3297return c329832993300#########################################3301# Dynamic variables (linked to controls)3302#########################################330333043305def _dynamic(var, control=None):3306if control is None:3307control = salvus.namespace.get(var, '')33083309@interact(layout=[[(var, 12)]], output=False)3310def f(x=(var, control)):3311salvus.namespace.set(var, x, do_not_trigger=[var])33123313def g(y):3314f.x = y33153316salvus.namespace.on('change', var, g)33173318if var in salvus.namespace:3319x = salvus.namespace[var]332033213322def dynamic(*args, **kwds):3323"""3324Make variables in the global namespace dynamically linked to a control from the3325interact label (see the documentation for interact).33263327EXAMPLES:33283329Make a control linked to a variable that doesn't yet exist::33303331dynamic('xyz')33323333Make a slider and a selector, linked to t and x::33343335dynamic(t=(1..10), x=[1,2,3,4])3336t = 5 # this changes the control3337"""3338for var in args:3339if not is_string(var):3340i = id(var)3341for k, v in salvus.namespace.items():3342if id(v) == i:3343_dynamic(k)3344return3345else:3346_dynamic(var)33473348for var, control in kwds.items():3349_dynamic(var, control)335033513352import sage.all335333543355def var0(*args, **kwds):3356if len(args) == 1:3357name = args[0]3358else:3359name = args3360G = salvus.namespace3361v = sage.all.SR.var(name, **kwds)3362if isinstance(v, tuple):3363for x in v:3364G[repr(x)] = x3365else:3366G[repr(v)] = v3367return v336833693370def var(*args, **kwds):3371"""3372Create symbolic variables and inject them into the global namespace.33733374NOTE: In CoCalc, you can use var as a line decorator::33753376%var x3377%var a,b,theta # separate with commas3378%var x y z t # separate with spaces33793380Use latex_name to customizing how the variables is typeset:33813382var1 = var('var1', latex_name=r'\sigma^2_1')3383show(e^(var1**2))33843385Multicolored variables made using the %var line decorator:33863387%var(latex_name=r"\color{green}{\theta}") theta3388%var(latex_name=r"\color{red}{S_{u,i}}") sui3389show(expand((sui + x^3 + theta)^2))3390339133923393Here is the docstring for var in Sage:33943395"""3396if 'latex_name' in kwds:3397# wrap with braces -- sage should probably do this, but whatever.3398kwds['latex_name'] = '{%s}' % kwds['latex_name']3399if len(args) > 0:3400return var0(*args, **kwds)3401else:34023403def f(s):3404return var0(s, *args, **kwds)34053406return f340734083409var.__doc__ += sage.all.var.__doc__34103411#############################################3412# Variable reset -- we have to rewrite3413# this because of all the monkey patching3414# that we do.3415#############################################34163417import sage.misc.reset341834193420def reset(vars=None, attached=False):3421"""3422If vars is specified, just restore the value of vars and leave3423all other variables alone. In CoCalc, you can also use3424reset as a line decorator::34253426%reset x, pi, sin # comma-separated3427%reset x pi sin # commas are optional34283429If vars is not given, delete all user-defined variables, reset3430all global variables back to their default states, and reset3431all interfaces to other computer algebra systems.34323433Original reset docstring::34343435"""3436if vars is not None:3437restore(vars)3438return3439G = salvus.namespace3440T = type(sys) # module type3441for k in list(G.keys()):3442if k[0] != '_' and type(k) != T:3443try:3444if k != 'salvus':3445del G[k]3446except KeyError:3447pass3448restore()3449from sage.symbolic.assumptions import forget3450forget()3451sage.misc.reset.reset_interfaces()3452if attached:3453sage.misc.reset.reset_attached()3454# reset() adds 'pretty_print' and 'view' to show_identifiers()3455# user can shadow these and they will appear in show_identifiers()3456# 'sage_salvus' is added when the following line runs; user may not shadow it3457exec('sage.misc.session.state_at_init = dict(globals())', salvus.namespace)345834593460reset.__doc__ += sage.misc.reset.reset.__doc__346134623463def restore(vars=None):3464""3465if is_string(vars):3466vars = str(vars) # sage.misc.reset is unicode ignorant3467if ',' in vars: # sage.misc.reset is stupid about commas and space -- TODO: make a patch to sage3468vars = [v.strip() for v in vars.split(',')]3469import sage.calculus.calculus3470sage.misc.reset._restore(salvus.namespace, default_namespace, vars)3471sage.misc.reset._restore(sage.calculus.calculus.syms_cur,3472sage.calculus.calculus.syms_default, vars)347334743475restore.__doc__ += sage.misc.reset.restore.__doc__347634773478# NOTE: this is not used anymore3479def md2html(s):3480from .markdown2Mathjax import sanitizeInput, reconstructMath3481from markdown2 import markdown34823483delims = [('\\(', '\\)'), ('$$', '$$'), ('\\[', '\\]'),3484('\\begin{equation}', '\\end{equation}'),3485('\\begin{equation*}', '\\end{equation*}'),3486('\\begin{align}', '\\end{align}'),3487('\\begin{align*}', '\\end{align*}'),3488('\\begin{eqnarray}', '\\end{eqnarray}'),3489('\\begin{eqnarray*}', '\\end{eqnarray*}'),3490('\\begin{math}', '\\end{math}'),3491('\\begin{displaymath}', '\\end{displaymath}')]34923493tmp = [((s, None), None)]3494for d in delims:3495tmp.append((sanitizeInput(tmp[-1][0][0], equation_delims=d), d))34963497extras = ['code-friendly', 'footnotes', 'smarty-pants', 'wiki-tables']3498markedDownText = markdown(tmp[-1][0][0], extras=extras)34993500while len(tmp) > 1:3501markedDownText = reconstructMath(markedDownText,3502tmp[-1][0][1],3503equation_delims=tmp[-1][1])3504del tmp[-1]35053506return markedDownText350735083509# NOTE: this is not used anymore3510class Markdown(object):3511r"""3512Cell mode that renders everything after %md as markdown.35133514EXAMPLES::35153516---3517%md3518# A Title35193520## A subheading35213522---3523%md(hide=True)3524# A title35253526- a list35273528---3529md("# A title")353035313532---3533%md `some code`353435353536This uses the Python markdown2 library with the following3537extras enabled:35383539'code-friendly', 'footnotes',3540'smarty-pants', 'wiki-tables'35413542See https://github.com/trentm/python-markdown2/wiki/Extras3543We also use markdown2Mathjax so that LaTeX will be properly3544typeset if it is wrapped in $'s and $$'s, \(, \), \[, \],3545\begin{equation}, \end{equation}, \begin{align}, \end{align}.,3546"""3547def __init__(self, hide=False):3548self._hide = hide35493550def __call__(self, *args, **kwds):3551if len(kwds) > 0 and len(args) == 0:3552return Markdown(**kwds)3553if len(args) > 0:3554self._render(args[0], **kwds)35553556def _render(self, s, hide=None):3557if hide is None:3558hide = self._hide3559html(md2html(s), hide=hide)356035613562# not used3563#md = Markdown()35643565# Instead... of the above server-side markdown, we use this client-side markdown.356635673568class Marked(object):3569r"""3570Cell mode that renders everything after %md as Github flavored3571markdown [1] with mathjax and hides the input by default.35723573[1] https://help.github.com/articles/github-flavored-markdown35743575The rendering is done client-side using marked and mathjax.35763577EXAMPLES::35783579---3580%md3581# A Title35823583## A subheading35843585---3586%md(hide=False)3587# A title35883589- a list35903591---3592md("# A title", hide=False)359335943595---3596%md(hide=False) `some code`35973598"""3599def __init__(self, hide=False):3600self._hide = hide36013602def __call__(self, *args, **kwds):3603if len(kwds) > 0 and len(args) == 0:3604return Marked(**kwds)3605if len(args) > 0:3606self._render(args[0], **kwds)36073608def _render(self, s, hide=None):3609if hide is None:3610hide = self._hide3611if hide:3612salvus.hide('input')3613salvus.md(s)361436153616md = Marked()361736183619#####3620## Raw Input3621# - this is the Python 2.x interpretation. In Python 3.x there is no raw_input,3622# and raw_input is renamed input (to cause more confusion).3623#####3624def raw_input(prompt='',3625default='',3626placeholder='',3627input_width=None,3628label_width=None,3629type=None):3630"""3631Read a string from the user in the worksheet interface to Sage.36323633INPUTS:36343635- prompt -- (default: '') a label to the left of the input3636- default -- (default: '') default value to put in input box3637- placeholder -- (default: '') default placeholder to put in grey when input box empty3638- input_width -- (default: None) css that gives the width of the input box3639- label_width -- (default: None) css that gives the width of the label3640- type -- (default: None) if not given, returns a unicode string representing the exact user input.3641Other options include:3642- type='sage' -- will evaluate it to a sage expression in the global scope.3643- type=anything that can be called, e.g., type=int, type=float.36443645OUTPUT:36463647- By default, returns a **unicode** string (not a normal Python str). However, can be customized3648by changing the type.36493650EXAMPLE::36513652print(raw_input("What is your full name?", default="Sage Math", input_width="20ex", label_width="25ex"))36533654"""3655return salvus.raw_input(prompt=prompt,3656default=default,3657placeholder=placeholder,3658input_width=input_width,3659label_width=label_width,3660type=type)366136623663def input(*args, **kwds):3664"""3665Read a string from the user in the worksheet interface to Sage and return evaluated object.36663667Type raw_input? for more help; this function is the same as raw_input, except with type='sage'.36683669EXAMPLE::36703671print(type(input("What is your age", default=18, input_width="20ex", label_width="25ex")))36723673"""3674kwds['type'] = 'sage'3675return salvus.raw_input(*args, **kwds)367636773678#####3679## Clear3680def clear():3681"""3682Clear the output of the current cell. You can use this to3683dynamically animate the output of a cell using a for loop.36843685SEE ALSO: delete_last_output3686"""3687salvus.clear()368836893690def delete_last_output():3691"""3692Delete the last output message.36933694SEE ALSO: clear3695"""3696salvus.delete_last_output()369736983699#####3700# Generic Pandoc cell decorator370137023703def pandoc(fmt, doc=None, hide=True):3704"""3705INPUT:37063707- fmt -- one of 'docbook', 'haddock', 'html', 'json', 'latex', 'markdown', 'markdown_github',3708'markdown_mmd', 'markdown_phpextra', 'markdown_strict', 'mediawiki',3709'native', 'opml', 'rst', 'textile'37103711- doc -- a string in the given format37123713OUTPUT:37143715- Called directly, you get the HTML rendered version of doc as a string.37163717- If you use this as a cell decorator, it displays the HTML output, e.g.,37183719%pandoc('mediawiki')3720* ''Unordered lists'' are easy to do:3721** Start every line with a star.3722*** More stars indicate a deeper level.37233724"""3725if doc is None:3726return lambda x: html(pandoc(fmt, x), hide=hide3727) if x is not None else ''37283729import subprocess3730p = subprocess.Popen(['pandoc', '-f', fmt, '--mathjax'],3731stdout=subprocess.PIPE,3732stderr=subprocess.PIPE,3733stdin=subprocess.PIPE)3734if not is_string(doc):3735if six.PY2:3736doc = str(doc).encode('utf-8')3737else:3738doc = str(doc, 'utf8')3739p.stdin.write(doc.encode('UTF-8'))3740p.stdin.close()3741err = p.stderr.read()3742if err:3743raise RuntimeError(err)3744return p.stdout.read()374537463747def wiki(doc=None, hide=True):3748"""3749Mediawiki markup cell decorator. E.g.,37503751EXAMPLE::37523753%wiki(hide=False)3754* ''Unordered lists'' and math like $x^3 - y^2$ are both easy3755** Start every line with a star.3756*** More stars indicate a deeper level. """3757if doc is None:3758return lambda doc: wiki(doc=doc, hide=hide) if doc else ''3759html(pandoc('mediawiki', doc=doc), hide=hide)376037613762mediawiki = wiki37633764######376537663767def load_html_resource(filename):3768fl = filename.lower()3769if fl.startswith('http://') or fl.startswith('https://'):3770# remote url3771url = fl3772else:3773# local file3774url = salvus.file(filename, show=False)3775ext = os.path.splitext(filename)[1][1:].lower()3776if ext == "css":3777salvus.javascript(3778'''$.get("%s", function(css) { $('<style type=text/css></style>').html(css).appendTo("body")});'''3779% url)3780elif ext == "html":3781salvus.javascript('element.append($("<div>").load("%s"))' % url)3782elif ext == "coffee":3783salvus.coffeescript(3784'$.ajax({url:"%s"}).done (data) ->\n eval(CoffeeScript.compile(data))'3785% url)3786elif ext == "js":3787salvus.html('<script src="%s"></script>' % url)378837893790def attach(*args):3791r"""3792Load file(s) into the Sage worksheet process and add to list of attached files.3793All attached files that have changed since they were last loaded are reloaded3794the next time a worksheet cell is executed.37953796INPUT:37973798- ``files`` - list of strings, filenames to attach37993800.. SEEALSO::38013802:meth:`sage.repl.attach.attach` docstring has details on how attached files3803are handled3804"""3805# can't (yet) pass "attach = True" to load(), so do this38063807if len(args) == 1:3808if is_string(args[0]):3809args = tuple(args[0].replace(',', ' ').split())3810if isinstance(args[0], (list, tuple)):3811args = args[0]3812try:3813from sage.repl.attach import load_attach_path3814except ImportError:3815raise NotImplementedError("sage_salvus: attach not available")38163817for fname in args:3818for path in load_attach_path():3819fpath = os.path.join(path, fname)3820fpath = os.path.expanduser(fpath)3821if os.path.isfile(fpath):3822load(fname)3823sage.repl.attach.add_attached_file(fpath)3824break3825else:3826raise IOError('did not find file %r to attach' % fname)382738283829# Monkey-patched the load command3830def load(*args, **kwds):3831"""3832Load Sage object from the file with name filename, which will have3833an .sobj extension added if it doesn't have one. Or, if the input3834is a filename ending in .py, .pyx, or .sage, load that file into3835the current running session. Loaded files are not loaded into3836their own namespace, i.e., this is much more like Python's3837"execfile" than Python's "import".38383839You may also load an sobj or execute a code file available on the web3840by specifying the full URL to the file. (Set ``verbose = False`` to3841supress the download progress indicator.)38423843INPUT:38443845- args -- any number of filename strings with any of the following extensions:38463847.sobj, .sage, .py, .pyx, .html, .css, .js, .coffee, .pdf38483849- ``verbose`` -- (default: True) load file over the network.38503851If you load any of the web types (.html, .css, .js, .coffee), they are loaded3852into the web browser DOM (or Javascript session), not the Python process.38533854If you load a pdf, it is displayed in the output of the worksheet. The extra3855options are passed to smc.pdf -- see the docstring for that.38563857In CoCalc you may also use load as a decorator, with exactly one filename as input::38583859%load foo.sage38603861This loads a single file whose name has a space in it::38623863%load a b.sage38643865The following are all valid ways to use load::38663867%load a.html3868%load a.css3869%load a.js3870%load a.coffee3871%load a.css3872load('a.css', 'a.js', 'a.coffee', 'a.html')3873load(['a.css', 'a.js', 'a.coffee', 'a.html'])38743875ALIAS: %runfile is the same as %load, for compatibility with IPython.3876"""3877if len(args) == 1:3878if is_string(args[0]):3879args = (args[0].strip(), )3880if isinstance(args[0], (list, tuple)):3881args = args[0]38823883if len(args) == 0 and len(kwds) == 1:3884# This supports3885# %load(verbose=False) a.sage3886# which doesn't really matter right now, since there is a bug in Sage's own3887# load command, where it isn't verbose for network code, but is for objects.3888def f(*args):3889return load(*args, **kwds)38903891return f38923893t = '__tmp__'3894i = 03895while t + str(i) in salvus.namespace:3896i += 13897t += str(i)38983899# First handle HTML related args -- these are all very oriented toward cloud.sagemath worksheets3900html_extensions = set(['js', 'css', 'coffee', 'html'])3901other_args = []3902for arg in args:3903i = arg.rfind('.')3904if i != -1 and arg[i + 1:].lower() in html_extensions:3905load_html_resource(arg)3906elif i != -1 and arg[i + 1:].lower() == 'pdf':3907show_pdf(arg, **kwds)3908else:3909other_args.append(arg)39103911# pdf?3912for arg in args:3913i = arg.find('.')39143915# now handle remaining non-web arguments.3916if other_args:3917try:3918exec(3919'salvus.namespace["%s"] = sage.misc.persist.load(*__args, **__kwds)'3920% t, salvus.namespace, {3921'__args': other_args,3922'__kwds': kwds3923})3924return salvus.namespace[t]3925finally:3926try:3927del salvus.namespace[t]3928except:3929pass393039313932# add alias, due to IPython.3933runfile = load39343935## Make it so pylab (matplotlib) figures display, at least using pylab.show3936import pylab393739383939def _show_pylab(svg=True):3940"""3941Show a Pylab plot in a Sage Worksheet.39423943INPUTS:39443945- svg -- boolean (default: True); if True use an svg; otherwise, use a png.3946"""3947try:3948ext = '.svg' if svg else '.png'3949filename = uuid() + ext3950pylab.savefig(filename)3951salvus.file(filename)3952finally:3953try:3954os.unlink(filename)3955except:3956pass395739583959pylab.show = _show_pylab3960matplotlib.figure.Figure.show = show39613962import matplotlib.pyplot396339643965def _show_pyplot(svg=True):3966"""3967Show a Pylab plot in a Sage Worksheet.39683969INPUTS:39703971- svg -- boolean (default: True); if True use an svg; otherwise, use a png.3972"""3973try:3974ext = '.svg' if svg else '.png'3975filename = uuid() + ext3976matplotlib.pyplot.savefig(filename)3977salvus.file(filename)3978finally:3979try:3980os.unlink(filename)3981except:3982pass398339843985matplotlib.pyplot.show = _show_pyplot39863987## Our own displayhook39883989_system_sys_displayhook = sys.displayhook39903991DISPLAYHOOK_MODULES_SHOW = [3992Graphics3d,3993Graphics,3994GraphicsArray,3995matplotlib.figure.Figure,3996matplotlib.axes.Axes,3997matplotlib.image.AxesImage,3998Animation,3999Tachyon,4000]40014002if MultiGraphics is not None:4003DISPLAYHOOK_MODULES_SHOW.append(MultiGraphics)40044005DISPLAYHOOK_MODULES_SHOW = tuple(DISPLAYHOOK_MODULES_SHOW)400640074008def displayhook(obj):4009if isinstance(obj, DISPLAYHOOK_MODULES_SHOW):4010show(obj)4011else:4012_system_sys_displayhook(obj)401340144015sys.displayhook = displayhook4016import sage.misc.latex, types40174018# We make this a list so that users can append to it easily.4019TYPESET_MODE_EXCLUDES = [4020sage.misc.latex.LatexExpr,4021type(None),4022type,4023sage.plot.plot3d.base.Graphics3d,4024sage.plot.graphics.Graphics,4025GraphicsArray,4026]40274028if MultiGraphics is not None:4029TYPESET_MODE_EXCLUDES.append(MultiGraphics)40304031TYPESET_MODE_EXCLUDES = tuple(TYPESET_MODE_EXCLUDES)403240334034def typeset_mode(on=True, display=True, **args):4035"""4036Turn typeset mode on or off. When on, each output is typeset using LaTeX.40374038EXAMPLES::40394040typeset_mode() # turns typesetting on40414042typeset_mode(False) # turn typesetting off40434044typeset_mode(True, display=False) # typesetting mode on, but do not make output big and centered40454046"""4047if is_string(on): # e.g., %typeset_mode False4048on = sage_eval(on, {'false': False, 'true': True})4049if on:40504051def f(obj):4052if isinstance(obj, TYPESET_MODE_EXCLUDES):4053displayhook(obj)4054else:4055show(obj, display=display)40564057sys.displayhook = f4058else:4059sys.displayhook = displayhook406040614062def python_future_feature(feature=None, enable=None):4063"""4064Enable python features from the __future__ system.40654066EXAMPLES::40674068Enable python3 printing:40694070python_future_feature('print_function', True)4071python_future_feature('print_function') # returns True4072print("hello", end="")40734074Then switch back to python2 printing4075python_future_feature('print_function', False)4076print "hello"40774078"""4079return salvus.python_future_feature(feature, enable)408040814082def py3print_mode(enable=None):4083"""4084Enable python3 print syntax.40854086EXAMPLES::40874088Enable python3 printing:40894090py3print_mode(True)4091py3print_mode() # returns True4092print("hello", end="")40934094Then switch back to python2 printing4095py3print_mode(False)4096print "hello"40974098"""4099return salvus.python_future_feature('print_function', enable)410041014102def default_mode(mode):4103"""4104Set the default mode for cell evaluation. This is equivalent4105to putting %mode at the top of any cell that does not start4106with %. Use default_mode() to return the current mode.4107Use default_mode("") to have no default mode.41084109EXAMPLES::41104111Make Pari/GP the default mode:41124113default_mode("gp")4114default_mode() # outputs "gp"41154116Then switch back to Sage::41174118default_mode("") # or default_mode("sage")41194120You can also use default_mode as a line decorator::41214122%default_mode gp # equivalent to default_mode("gp")4123"""4124return salvus.default_mode(mode)412541264127#######################################################4128# Monkey patching and deprecation --4129#######################################################41304131# Monkey patch around a bug in Python's findsource that breaks deprecation in cloud worksheets.4132# This won't matter if we switch to not using exec, since then there will be a file behind4133# each block of code. However, for now we have to do this.4134import inspect4135_findsource = inspect.findsource413641374138def findsource(object):4139try:4140return _findsource(object)4141except:4142raise IOError(4143'source code not available') # as *claimed* by the Python docs!414441454146inspect.findsource = findsource41474148#######################################################4149# Viewing pdf's4150#######################################################415141524153def show_pdf(filename, viewer="object", width=1000, height=600, scale=1.6):4154"""4155Display a PDF file from the filesystem in an output cell of a worksheet.41564157It uses the HTML object tag, which uses either the browser plugin,4158or provides a download link in case the browser can't display pdf's.41594160INPUT:41614162- filename4163- width -- (default: 1000) -- pixel width of viewer4164- height -- (default: 600) -- pixel height of viewer4165"""4166url = salvus.file(filename, show=False)4167s = '''<object data="%s" type="application/pdf" width="%s" height="%s">4168<p>Your browser doesn't support embedded PDF's, but you can <a href="%s">download %s</a></p>4169</object>''' % (url, width, height, url, filename)4170salvus.html(s)417141724173########################################################4174# Documentation of modes4175########################################################4176def modes():4177"""4178To use a mode command, either type41794180%command <a line of code>41814182or41834184%command4185[rest of cell]41864187Create your own mode command by defining a function that takes4188a string as input and outputs a string. (Yes, it is that simple.)4189"""4190import re4191mode_cmds = set()4192for s in open(os.path.realpath(__file__), 'r'):4193s = s.strip()4194if s.startswith('%'):4195sm = (re.findall(r'%[a-zA-Z]+', s))4196if len(sm) > 0:4197mode_cmds.add(sm[0])4198mode_cmds.discard('%s')4199for k, v in sage.interfaces.all.__dict__.items():4200if isinstance(v, sage.interfaces.expect.Expect):4201mode_cmds.add('%' + k)4202mode_cmds.update([4203'%cython', '%time', '%auto', '%hide', '%hideall', '%fork', '%runfile',4204'%default_mode', '%typeset_mode'4205])4206v = list(sorted(mode_cmds))4207return v420842094210########################################################4211# Go mode4212########################################################4213def go(s):4214"""4215Run a go program. For example,42164217%go4218func main() { fmt.Println("Hello World") }42194220You can set the whole worksheet to be in go mode by typing42214222%default_mode go42234224NOTES:42254226- The official Go tutorial as a long Sage Worksheet is available here:42274228https://github.com/sagemath/cloud-examples/tree/master/go42294230- There is no relation between one cell and the next. Each is a separate4231self-contained go program, which gets compiled and run, with the only4232side effects being changes to the filesystem. The program itself is4233stored in a random file that is deleted after it is run.42344235- The %go command automatically adds 'package main' and 'import "fmt"'4236(if fmt. is used) to the top of the program, since the assumption4237is that you're using %go interactively.4238"""4239import uuid4240name = str(uuid.uuid4())4241if 'fmt.' in s and '"fmt"' not in s and "'fmt'" not in s:4242s = 'import "fmt"\n' + s4243if 'package main' not in s:4244s = 'package main\n' + s4245try:4246open(name + '.go', 'w').write(s.encode("UTF-8"))4247(child_stdin, child_stdout,4248child_stderr) = os.popen3('go build %s.go' % name)4249err = child_stderr.read()4250sys.stdout.write(child_stdout.read())4251sys.stderr.write(err)4252sys.stdout.flush()4253sys.stderr.flush()4254if not os.path.exists(name): # failed to produce executable4255return4256(child_stdin, child_stdout, child_stderr) = os.popen3("./" + name)4257sys.stdout.write(child_stdout.read())4258sys.stderr.write(child_stderr.read())4259sys.stdout.flush()4260sys.stderr.flush()4261finally:4262try:4263os.unlink(name + '.go')4264except:4265pass4266try:4267os.unlink(name)4268except:4269pass427042714272########################################################4273# Java mode4274########################################################4275def java(s):4276"""4277Run a Java program. For example,42784279%java4280public class YourName { public static void main(String[] args) { System.out.println("Hello world"); } }42814282You can set the whole worksheet to be in java mode by typing42834284%default_mode java42854286NOTE:42874288- There is no relation between one cell and the next. Each is a separate4289self-contained java program, which gets compiled and run, with the only4290side effects being changes to the filesystem. The program itself is4291stored in a file named as the public class that is deleted after it is run.4292"""4293name = re.search('public class (?P<name>[a-zA-Z0-9]+)', s)4294if name:4295name = name.group('name')4296else:4297print('error public class name not found')4298return4299try:4300open(name + '.java', 'w').write(s.encode("UTF-8"))4301(child_stdin, child_stdout,4302child_stderr) = os.popen3('javac %s.java' % name)4303err = child_stderr.read()4304sys.stdout.write(child_stdout.read())4305sys.stderr.write(err)4306sys.stdout.flush()4307sys.stderr.flush()4308if not os.path.exists(name + '.class'): # failed to produce executable4309return4310(child_stdin, child_stdout, child_stderr) = os.popen3('java %s' % name)4311sys.stdout.write(child_stdout.read())4312sys.stderr.write('\n' + child_stderr.read())4313sys.stdout.flush()4314sys.stderr.flush()4315finally:4316pass4317try:4318os.unlink(name + '.java')4319except:4320pass4321try:4322os.unlink(name + '.class')4323except:4324pass432543264327########################################################4328# Julia mode4329########################################################433043314332def julia(code=None, **kwargs):4333"""4334Block decorator to run Julia over Jupyter bridge.43354336To use this, put %julia on a line by itself in a cell so that it applies to4337the rest of the cell, or put it at the beginning of a line to4338run just that line using julia.43394340State is preserved between cells.43414342This is different than the julia command in Sage itself (which you can4343access via sage.interfaces.julia), which uses a more brittle pexpect interface.43444345"""4346if julia.jupyter_kernel is None:4347julia.jupyter_kernel = jupyter("julia-1.7")4348return julia.jupyter_kernel(code, **kwargs)434943504351julia.jupyter_kernel = None43524353# Help command4354import sage.misc.sagedoc4355import sage.version4356import sage.misc.sagedoc435743584359def help(*args, **kwds):4360if len(args) > 0 or len(kwds) > 0:4361sage.misc.sagedoc.help(*args, **kwds)4362else:4363s = """4364## Welcome to Sage %s!43654366- **Online documentation:** [View the Sage documentation online](http://www.sagemath.org/doc/).43674368- **Help:** For help on any object or function, for example `matrix_plot`, enter `matrix_plot?` followed by tab or shift+enter. For help on any module (or object or function), for example, `sage.matrix`, enter `help(sage.matrix)`.43694370- **Tab completion:** Type `obj` followed by tab to see all completions of obj. To see all methods you may call on `obj`, type `obj.` followed by tab.43714372- **Source code:** Enter `matrix_plot??` followed by tab or shift+enter to look at the source code of `matrix_plot`.43734374- **License information:** For license information about Sage and its components, enter `license()`.""" % sage.version.version4375salvus.md(s)437643774378# Import the jupyter kernel client.4379try:4380from .sage_jupyter import jupyter4381except:4382from sage_jupyter import jupyter438343844385# license() workaround for IPython pager4386# could also set os.environ['TERM'] to 'dumb' to workaround the pager4387def license():4388r"""4389Display Sage license file COPYING.txt43904391You can also view this information in an SMC terminal session:43924393| $ sage4394| sage: license()43954396"""4397print((sage.misc.copying.license))439843994400# search_src4401import glob440244034404# from http://stackoverflow.com/questions/9877462/is-there-a-python-equivalent-to-the-which-commane4405# in python 3.3+ there is shutil.which()4406def which(pgm):4407path = os.getenv('PATH')4408for p in path.split(os.path.pathsep):4409p = os.path.join(p, pgm)4410if os.path.exists(p) and os.access(p, os.X_OK):4411return p441244134414try:4415from .sage_server import MAX_CODE_SIZE4416except:4417from sage_server import MAX_CODE_SIZE441844194420def search_src(str, max_chars=MAX_CODE_SIZE):4421r"""4422Get file names resulting from git grep of smc repo44234424INPUT:44254426- ``str`` -- string, expression to search for; will be quoted4427- ``max_chars`` -- integer, max characters to display from selected file44284429OUTPUT:44304431Interact selector of matching filenames. Choosing one causes its4432contents to be shown in salvus.code() output.4433"""4434sage_cmd = which("sage")4435if os.path.islink(sage_cmd):4436sage_cmd = os.readlink(sage_cmd)44374438# /projects/sage/sage-x.y/src/bin4439sdir = os.path.dirname(sage_cmd)44404441# /projects/sage/sage-x.y4442sdir = os.path.dirname(os.path.dirname(sdir))44434444# /projects/sage/sage-x.y/src4445sdir = glob.glob(sdir + "/src/sage")[0]44464447cmd = 'cd %s;timeout 5 git grep -il "%s"' % (sdir, str)4448srch = os.popen(cmd).read().splitlines()4449header = "files matched"4450nftext = header + ": %s" % len(srch)44514452@interact4453def _(fname=selector([nftext] + srch, "view source file:")):4454if not fname.startswith(header):4455with open(os.path.join(sdir, fname), 'r') as infile:4456code = infile.read(max_chars)4457salvus.code(code, mode="python", filename=fname)445844594460# search_doc4461def search_doc(str):4462r"""4463Create link to Google search of sage docs.44644465INPUT:44664467- ``str`` -- string, expression to search for; will be quoted44684469OUTPUT:44704471HTML hyperlink to google search4472"""4473txt = 'Use this link to search: ' + \4474'<a href="https://www.google.com/search?q=site%3Adoc.sagemath.org+' + \4475str + '&oq=site%3Adoc.sagemath.org">'+str+'</a>'4476salvus.html(txt)447744784479import sage.misc.session448044814482def show_identifiers():4483"""4484Returns a list of all variable names that have been defined during this session.44854486SMC introduces worksheet variables, including 'smc','salvus', 'require', and after reset(), 'sage_salvus'.4487These identifiers are removed from the output of sage.misc.session.show_identifiers() on return.4488User should not assign to these variables when running code in a worksheet.4489"""4490si = eval('show_identifiers.fn()', salvus.namespace)4491si2 = [4492v for v in si if v not in ['smc', 'salvus', 'require', 'sage_salvus']4493]4494return si2449544964497show_identifiers.fn = sage.misc.session.show_identifiers449844994500