MUI_IIMAI_1415_Tutorial_Avanzado_Programación

1082 days ago by trinidad1415

Herramientas de Sage

En este tutorial de Sage, veremos los elementos de programación de Sage, cómo utilizar R dentro de Sage, cómo importar archivos CSV y algunos conceptos de programación más avanzados (objetos, eficiencia y programación funcional)

Programación en Sage

El lenguaje de programación de Sage por defecto es Python. Según Wikipedia:

"Python es un lenguaje de programación interpretado cuya filosofía hace hincapié en una sintaxis muy limpia y que favorezca un código legible.

Se trata de un lenguaje de programación multiparadigma, ya que soporta orientación a objetos, programación imperativa y, en menor medida, programación funcional. Es un lenguaje interpretado, usa tipado dinámico y es multiplataforma.

Es administrado por la Python Software Foundation. Posee una licencia de código abierto, denominada Python Software Foundation License,1 que es compatible con la Licencia pública general de GNU a partir de la versión 2.1.1, e incompatible en ciertas versiones anteriores."

Funciones

Para definir una función (programación) en Sage/Python, usamos la palabra clave def, seguida del nombre de la función, las variables de entrada entre paréntesis y dos puntos. En las siguientes líneas se escriben los comandos que queremos que evalúe la función.La identación es muy importante en Python: todo lo que esté con la misma identación será el cuerpo de la función, para terminar la función basta escribir sin identación. Por último, el valor que devuelve la función se determina por la palabra clave return.

def g(x,y): return x*y 
       

Podemos usar la función igual que usamos cualquier función matemática.

g(1,3) 
       

Las funciones pueden recibir listas como parámetros y devolver listas en el return.

def sumaCuadrados(v): return sum([k^2 for k in v]) 
       
sumaCuadrados([1,3..9]) 
       
def primos(n): return [k for k in [1..n] if is_prime(k)] 
       
primos(1000) 
       

Como parámetro de entrada y salida vale cualquier tipo de objeto de Sage:

def graficaDerivada(f,x): return plot(f(x),(x,-1,1))+plot(f.diff(x)(x),(x,-1,1),color="green") 
       
f(x)=exp(x)-x graficaDerivada(f,x) 
       

También podemos utilizar una función dentro de otra.

def sumaPrimos(n): return sum(primos(n)) 
       
sumaPrimos(10) 
       

Funciones lambda

En ocasiones, necesitamos definir funciones que no vamos a reutilizar. La notación lambda permite definir la función en una línea. Con esta notación, una función se define como lambda variable: definición de la función

f=lambda a:a+2 
       
f(3) 
       

La variable puede ser de cualquier tipo de dato de Sage

g=lambda v:v[0] 
       
g([1,2,3]) 
       

Normalmente la usaremos combinada con otras funciones

# Tenemos una lista de puntos del plano puntos=[(0,1),(1,1),(2,1),(4,3)] 
       
# Obtenemos una lista con el módulo de los vectores map(lambda v:sqrt(v[0]^2+v[1]^2),puntos) 
       

Estructuras de control

Las estructuras de control seleccionan cuál es la siguiente sentencia a ejecutar (selección -- if) o hacen que una sentencia se ejecute repetidamente (iteración -- while o for).

Selección -  If

La estructura de selección condicional if nos permite elegir ejecutar o no un bloque de instrucciones. La sintaxis es:

if condición:

   instrucciones a ejecutar (1 o varias)

También se puede elegir ejecutar otras instrucciones en el caso en el que no se cumpla la condición:

if condición:

   instrucciones a ejecutar (1 o varias)

else:

   instrucciones a ejecutar (1 o varias)

Por ejemplo, vamos a calcular el valor absoluto de un número.

a=-5 if a<0: a=-a; a 
       

Usando else ejecutamos una instrucción cuando no se cumple la condición.

a=pi/2 if a>3: show( latex(a)+ latex(' es mayor que 3')) else: show( latex(a)+ latex(' es menor que 3')) 
       

Podemos combinar varias condiciones con los operadores and y or.

a=pi/2 if a>1 and a<3: show( latex(a)+ latex(' está entre 1 y 5')) else: show( latex(a)+ latex(' no está entre 1 y 5')) 
       

Repetición - While

Para repetir una sentencia un número indeterminado de veces, podemos usar un bucle while, con sintaxis:

while condición:

  instrucciones a ejecutar

Por ejemplo, para encontrar un número en una lista:

lista=[1,2,3,5,3,4,7] i=0; # Este bucle busca en la lista hasta que encuentra el 7 (es decir, mientras i está en la lista y lista[i] no es 7) while i<len(lista) and lista[i] != 7: i=i+1; # Hacemos que la salida sea explicativa if i<len(lista): print "El número ",lista[i]," está en la posición",i+1 else: print "No he encontrado el número" 
       

Repetición - For

Si queremos repetir una instrucción para cada elemento de un vector, podemos usar un bucle for. La sintaxis es:

for variable in lista:

  instrucciones a ejecutar

Vamos a mostrar por pantalla todos los cuadrados del 0 al 4.

for i in [0..4]: print i^2 
       

Y vamos a poner en mayúscula la primera letra de cada cadena de texto de una lista.

for str in ["juan","mario","eva","laura"]: print str.capitalize() 
       

En el siguiente ejemplo, se recibe una lista de cotas equiespaciadas (por ejemplo, la altura en metros de una carretera, medida cada kilómetro) y se calcula la pendiente.

cotas=[323,301,298,302,312,333,340,352,367] distanciaHorizontal=1000 pendiente=[] # comienzo en una lista vacía for k in range(len(cotas)-1): pendiente.append((cotas[k+1]-cotas[k])/distanciaHorizontal) pendiente 
       

Ejercicios

1. Crea una función que reciba los coeficientes $a,b,c$ del polinomio $ax^2+bx+c$ y devuelva las dos raíces del polinomio.

2. Crea una función que reciba dos números y devuelva el mayor de ellos.

3. Crea una función que reciba tres números y devuelva $1$ si son iguales y $0$ si alguno es distinto a los demás.

4. Crea una función que reciba una lista de números y devuelva la posición del primer elemento positivo

5. Crea una función que reciba una lista de números $l$ y un valor $M$ y devuelva el primer número $k$ tal que $l[0]+l[1]+...+l[k]>M$.

6. Crea una función que reciba una lista y sume todos los elementos.

7. Crea una función que reciba una lista de cotas medidas en intervalos equiespaciados y la distancia entre dos intervalos y devuelva el valor de la mayor pendiente.

 
       

R y Sage

Dentro de Sage podemos utilizar varios programas de software matemático. Vamos a ilustrar cómo trabajar con R.

El primer método es ir a la cabecera del documento, y en el desplegable que pone sage, elegir el programa R. Hazlo y después ejecuta el siguiente código.

x<- runif(1000) mean(x) 
       

Vuelva a poner sage en el desplegable. Un segundo modo de ejecutar comandos en otro lenguage, es escribir %nombredellenguaje al principio de la celda. Por ejemplo, %r hará que Sage entienda que todos los comandos en esa celda usan la sintaxis de R.

%r x<- runif(1000) mean(x) 
       

También podemos generar gráficas de R. Para ello hay que escribir png() antes de comenzar a generar las gráficas y .silent.me. <- dev.off() al final para que las muestre.

%r x <- rnorm(100) y <- rnorm(100, mean=5)*x+rnorm(100, mean=0.1) png() plot(y ~ x) abline(lm(y ~ x), lwd=2) .silent.me. <- dev.off() 
       

Los métodos anteriores son válidos si queremos utilizar puntualmente R para analizar datos o dibujar una gráfica, pero no son útiles cuando lo que queremos es utilizar los resultados para combinarlos con las funciones que nos ofrece Sage. Si lo que queremos es precisamente eso, es mejor utilizar los comandos de R llamándolos como r.nombrecomando. Veamos algunos ejemplos.

r.png() x = r.rnorm(100) r.hist(x) r.library("lattice") r("print(histogram(~wt | cyl, data=mtcars))") r.histogram(x = "~ wt | cyl", data="mtcars") r.dev_off() 
       

Cuando usamos R, los datos que nos devuelve están en "formato R", por lo que algunas operaciones no estarán disponibles. Para pasarlos a formato Sage, escribiremos al final ._sage_().

x=[1,4,3,2,4,1,2,1,2] r.mean(x)._sage_() 
       

Si vamos a utilizar una función de R repetidamente dentro de Sage, podemos "importarla" creándonos una función auxiliar que realice los cambios de formato.

def pnorm(xvalor): return (r.pnorm(xvalor.n()))._sage_() 
       
pnorm(1.3) 
       

Ejercicios

1. Genera en R dos vectores de 20 datos aleatorios con la distribución uniforme (runif) entre 0 y 1.

2. Calcula la media y varianza de los datos anteriores.

3. Dibújalos como puntos en un plano $x,y$.

4. Utilizando órdenes de Sage, dibuja la interpolación spline de los puntos, los puntos y la media.

 
       

Manejo de datos

En este apartado, veremos cómo importar archivos de datos y realizar algunas operaciones básicas. En general, podemos tener los archivos de datos en múltiples formatos y usualmente en Bases de Datos. Como el lenguaje de Sage es python, podemos acceder a multitud de formatos de archivo, pero por simplicidad, vamos a asumir que tenemos los datos en un archivo en formato csv.

El primer paso para leer un archivo es añadirlo al directorio "DATA" de la hoja de Sage en la que estamos trabajando. Vamos a cargar un archivo como ejemplo.

En el desplegable "DATA" de la parte superior de la página, elige la opción "upload or create a file".

Aparecerá una nueva página con varias opciones para crear un archivo. Introduce en "Or enter the URL of a file on the web:" la dirección:

http://samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv

En "What do you want to call it? (if different than the original name)" escribe "datos.csv" y pulsa "Upload or create a file". Después, vuelve a la página pulsando en "Worksheet".

import csv data =list( csv.reader(open(DATA+'datos.csv','rU')) ) 
       

Podemos visualizar los datos escribiendo "data" en una celda. Sin embargo el tamaño es tan grande que sólo nos mostrará parte. Para evitar eso, podemos mostrar las dos primeras filas.

data[0];data[1] 
       

Ordenando los datos

Veamos cómo ordenar listas con Sage, o cómo encontrar el máximo o el mínimo de un conjunto de datos.

Comenzaremos introdicendo una lista simple:

L = [2,3,1,6,8,-3,9,4,3,1,2];L 
       

Podemos encontrar el máximo y el mínimo usando los comando max y min.

max(L) 
       
min(L) 
       

Para encontrar la posición de un elemento concreto en la lista, usamos el método index.

L.index(-3) 
       
L.index(10) 
       

Podemos ver que el error surge porque el número no está en la lista. Podemos comprobarlo usando in

10 in L 
       

Recordemos que también podemos filtrar los elementos de una lista.

[ k for k in L if k>mean(L)] # Elegimos los elementos que están por encima de la media 
       

Podemos ordenar una lista de dos maneras. Una es utilizando la función sorted, que devuelve la lista ordenada.

sorted(L) 
       

Nótese que la lista no ha sido modificada.

       

También podemos ordenarla con el método sort.

Vamos a hacer una copia de nuestra lista con copy y vamos a ordenarla.

L2=copy(L);L2 
       
L2.sort();L2 
       

También podemos ordenar cambiando el criterio. Por ejemplo, vamos a ordenar los puntos según el valor de la función seno. Primero vamos a crear una lista con cada punto y el valor del seno que nos sirva de referencia.

LS=zip(L,map(sin,[k.n() for k in L]));LS 
       

Ahora ordeno la lista original indicando en la propiedad key que la función "que me da el peso" es la función seno.

sorted(L,key=sin) 
       

Podemos ver que la función se ha ordenado de modo que el primer valor es aquel tal que es seno es mínimo y el último en el que el seno es máximo.

Para ordenar una lista compleja tenemos que crear una función que asigne a cada elemento de la lista un valor. Después la ordenará usando esos valores.

Por ejemplo, creamos una función que devuelve la segunda posición de un par y ordenamos la lista de pares LS según esa función.

def f(x): return x[1] sorted(LS, key=f) 
       

Lo más cómodo es utilizar notación lambda

sorted(LS,key=lambda v: v[1]) 
       

Este mismo método sive con max y min.

max(LS, key=lambda v: v[1]) 
       

Podemos utilizar este método para saber las posiciones de los elementos ordenados. Para ello creamos una lista cuya primera coordenada es la posición del elemento y la segunda el valor.

LL=zip(range(len(L)),L);LL 
       

Ahora ordenamos la lista usando el segundo elemento.

LLordenada=sorted(LL, key=f);LLordenada 
       

Ahora nos quedamos con el primer elemento de cada pareja.

orden=[k[0] for k in LLordenada];orden 
       

Podemos usar la lista anterior para ordenar una base de datos en la que una columna sea L o acceder a los elementos en orden sin tener que ordenar toda la base de datos. Por ejemplo, vamos a ordenar LS respecto a la primera coordenada.

[LS[k] for k in orden] 
       

Volvamos ahora al archivo de datos cargado. Vamos a representar las coordenadas de las ventas.

posiciones=[(data[k][11],data[k][10]) for k in range(1,len(data))]; posiciones[0],posiciones[1] 
       
points(posiciones) 
       

Vamos a represntar tambien el precio.

precio=[int(data[k][9]) for k in range(1,len(data))] min(precio),max(precio),mean(precio).n() 
       

Vamos a asignar un color, de modo que las casas más baratas sean azules y las más caras rojas.

def rangoColores(k): porcentaje=float(precio[k]-min(precio))/float(max(precio)-min(precio)) r=porcentaje g=porcentaje*(1-porcentaje) b=(1-porcentaje) return (r,g,b) 
       
sum([points(posiciones[k], color=rangoColores(k)) for k in range(len(posiciones))]) 
       

Ejercicios

1. Carga el archivo que está en la dirección http://matematicas.unex.es/~trinidad/mui/municipios.csv como archivo de datos de esta hoja de Sage.

2. Obtén la posición en la lista del municipio más oriental y el más occidental.

3. Dibuja la posición en latitud y longitud de cada municipio. 

4. Crea una función que reciba una lista y devuelva el órden en el que habría que colocar sus elementos para que estuviera ordenada de menor a mayor. Por ejemplo, si la lista es [4,8,1], devolverá [3,1,2].

5. Ordena los municipios de menor a mayor latitud.

 
       

Sage es orientado a objetos

Anteriormente hemos definido el concepto de variables. Sin embargo, en Python las variables son algo más, pues conocen qué se puede hacer con ellas. Esto se denomina objetos.

La primera diferencia frente a una variable normal nos la encontramos cuando igualamos dos objetos. Python no copia el contenido de una variable en la otra, sino que asigna la nueva etiqueta al objeto que tenía la etiqueta. Veámoslo con un ejemplo:

a = [1,2,3] b = a # Asignamos el nombre b al objeto guardado en a. Ahora el objeto tiene dos nombres. b[1] = 0 # Modificamos uno de ellos a # y el otro se modifica. 
       

Un objeto en Python tiene asignados diversos métodos, es decir, operaciones que se pueden hacer con el objeto. Para ver las operaciones podemos escribir el objeto, seguido de un punto y pulsar <tab>:

a.append(3);a 
       

Clases

Vamos a construir una pequeña clase en Python.

class Glass(object): def __init__(self, size): # Cuando se crea el vaso se llama a este método self._size = size # Tamaño del vaso self._content = "nada" # Contenido def __repr__(self): # Este método controla cómo se muestra por pantalla if self._content == "nada": return "Un vaso vacío de tamaño %s"%(self._size) else: return "Un vaso de tamaño %s lleno de %s"%(self._size,self._content) def fill(self,content): # Método para llenar el vaso self._content = content def empty(self): # Método para vaciar el vaso self._content = "nada" 
       
myGlass = Glass("grande"); myGlass 
       
myGlass.fill("cerveza"); myGlass 
       
myGlass.empty(); myGlass 
       

Una convención en Python es que los métodos o atributos que comienzan con _ no deben ser utilizados, sino que se usan internamente.

En la primera línea hemos puesto entre los paréntesis "object". Toda clase "hereda" de otra. Cuando definimos una clase genérica, heredamos de la clase "object" (en Sage normalmente de SageObject). Pero podemos heredar de cualquier otra clase. De ese modo, tenemos todos los métodos y propiedades definidos para dicha clase.

class WineGlass(Glass): def fill(self): # Método para llenar el vaso self._content = "vino" 
       
vasoVino=WineGlass("mediano");vasoVino 
       
vasoVino.fill();vasoVino 
       

Incrementando la eficiencia

Estadísticas del desempeño de un programa

En Sage utilizamos muchos tipos complejos de datos y métodos asociados a ellos. Esto hace que en ocasiones sea complejo determinar la eficiencia de un algoritmo. Las estadísticas de la ejecución de los algoritmos nos darán información de qué puntos del programa son los que consumen más tiempo y, por tanto, dónde debemos mejorar la eficiencia.

Vamos a comenzar con un ejemplo de cálculo de la constante de Brun. La constante de Brun se define como:

\[\sum_{p,p+2\in\mathbb{P}} \left(\frac{1}{p}+\frac{1}{p+2}\right),\]

donde $\mathbb{P}$ es el conjunto de los números primos.

#Suma de los inversos de los primos gemelos #1: Encuentra primos def criba(ls): '''Se queda con los elementos irreducibles de una lista de enteros''' primos = [] while ls: p = ls[0] primos.append(p) ls = [k for k in ls if k%p] return primos def lista_primos(K): 'genera los numeros primos menores que K' return criba(range(2,K)) #2: Selecciona los gemelos (Nos quedamos con el menor de cada par) def criba_gemelos(ls): '''recibe una lista de primos, y devuelve los numeros p tales que p+2 tambien esta en la lista''' return [p for p in ls if p+2 in ls] #3: Sumamos los inversos para aproximar la constante de Brun def brun(K): '''Devuelve la suma de los inversos de los primos gemelos menores que K''' primos = lista_primos(K) gemelos = criba_gemelos(primos) return sum( (1.0/p + 1.0/(p+2)) for p in gemelos) 
       
%time print brun(1e4) 
       
#importamos los modulos cProfile y pstats para ver las estadisticas de cuanto tiempo se pasa en cada parte del codigo import cProfile, pstats #No necesitamos entender la siguiente linea: tomalo como una version avanzada de timeit cProfile.runctx("brun(10000)", globals(), locals(), DATA + "Profile.prof") s = pstats.Stats(DATA + "Profile.prof") #Imprimimos las estadisticas, ordenadas por el tiempo total s.strip_dirs().sort_stats("time").print_stats() 
       

Vemos que la mayor parte del tiempo los dedica a criba gemelos y criba. Es sencillo ver que se puede mejorar la criba de gemelos pues dos números primos gemelos aparecerán juntos en la lista.

#2: Selecciona los gemelos. Nos quedamos con el menor de cada par def criba_gemelos(ls): return [ls[j] for j in xrange(len(ls)-1) if ls[j+1]==ls[j]+2] 
       
%time print brun(1e4) 
       

Ahora podemos ver que todo el tiempo lo dedica a la criba, que sería el siguiente punto a optimizar (si estás muy interesado, en las referencias está la optimización completa)

import cProfile, pstats cProfile.runctx("brun(50000)", globals(), locals(), DATA + "Profile.prof") s = pstats.Stats(DATA + "Profile.prof") s.strip_dirs().sort_stats("time").print_stats() 
       

Compilando el código en cyton

Una segunda manera (complementaria) de obtener algoritmos más rápidos es compilar el código. Comenzaremos por un código en Python para el cálculo de una integral por el método del trapecio compuesto.

def f(x): return sin(x**2) def integral(a, b, N): dx = (b-a)/N s = 0 for i in range(N): s += f(a+dx*i) return s * dx 
       
time integral(0.0, 1.0, 500000) 
       

Para compilar el código, hemos de escribirlo en Cython. Para ello escribimos %cyton al principio de la celda. Sage entiende que todo lo que escribamos en la celda será Cython. Además hemos de importar la función seno y cambiar ^ por **

%cython #Tenemos que importar la funcion seno from math import sin def f(x): return sin(x**2) def integral_cy1(a, b, N): dx = (b-a)/N s = 0 for i in range(N): s += f(a+dx*i) return s * dx 
       
time integral_cy1(0.0, 1.0, 500000) 
       

Se puede ver que el tiempo de ejecución se ha reducido a la mitad. Sin embargo, podemos mejorarlo sustancialmente indicando el tipo de datos.

%cython from math import sin def f(double x): return sin(x**2) def integral_cy2(double a, double b, int N): cdef double dx = (b-a)/N cdef int i cdef double s = 0 for i in range(N): s += f(a+dx*i) return s * dx 
       
time integral_cy2(0.0, 1.0, 500000) 
       

Incluso podemos hacerlo más rápido llamando directamente a librerías en C.

Por último, en las referencias se muestra cómo aplicar el mismo proceso para calcular el conjunto de Mandelbrot.

%cython import numpy cimport numpy #para declarar los tipos de los arrays tb tenemos que usar cimport def mandelbrot_cy5(float x0,float y0,float side,int N=200, int L=50, float R=3): '''returns an array NxN to be plotted with matrix_plot ''' cdef double complex c, z, I cdef float delta cdef int h, j, k cdef numpy.ndarray[numpy.uint16_t, ndim=2] m m = numpy.zeros((N,N), dtype=numpy.uint16) I = complex(0,1) delta = side/N for j in range(N): for k in range(N): c = (x0+j*delta)+ I*(y0+k*delta) z=0 h=0 while (h<L and z.real**2 + z.imag**2 < R*R): z=z*z+c h+=1 m[j,k]=h return m 
       
time m=mandelbrot_cy5(-0.59375, 0.46875, 0.046875,600,160) matrix_plot(m).show(figsize=(8,8)) 
       

Sage y C

En el apartado anterior, hemos comentado que podemos llamar a librerías en C. Vamos a ilustrar esto con un ejemplo.

Para cargar una función (o un "script") almacenado en fichero independiente podemos usar load o attach.

load DATA+"example.sage" 
       
attach DATA+"example.sage" 
       

Podemos cargar directamente una función en Sage que haga referencia a otra en C:

attach DATA+"test.spyx" 
       
test(10) 
       

Un poco de programación funcional

La programación funcional consiste en partir de una lista base e ir aplicando funciones (transformaciones) a esa lista hasta obtener la solución al problema. Hemos visto las listas por comprensión que sería una primera aproximación al problema. Sin embargo esa solución es muy ineficiente. Veamos una aproximación de Python a la programación funcional.

Vamos a crear una función que otiene el segundo número primo entre 10000 y 100000.

%time ls = srange(10000,100000) primos = [t for t in ls if is_prime(t) ] print primos[1] 
       

El problema de este algoritmo es que hemos generado todos los números primos entre 10000 y 100000 para luego sólo usar 38 de ellos. Para evitar generar todos esos primos, sustituimos srange por xsrange.

srange(100) 
       
xsrange(100) 
       

Podemos ver que xsrange(100) no devuelve ningún número. Lo que nos devuelve es un método para ir generando dichos números.

xlista = xsrange(100) 
       
print xlista.next() 
       
print xlista.next() 
       

Veamos un ejemplo de uso de xsrange:

acumulador = 0 for j in xsrange(101): acumulador += j acumulador 
       

También podemos generar listas por comprensión con xsrange. La diferencia es que tendremos que utilizar paréntesis en lugar de corchetes y que en lugar de darnos la lista, lo que nos da es un objeto denominado "iterador", que va generando los elementos de la lista según se van necesitando.

genera_cuadrados = (x^2 for x in xsrange(10)) for c in genera_cuadrados: print c 
       

Podemos ahora volver al programa original y hacer el cambio:

%time ls = xrange(10000,100000) primos = (t for t in ls if is_prime(t) ) primos.next() print primos.next() 
       

De modo alternativo:

%time contador = 2 for j in xsrange(10000,100000): if is_prime(j): contador -= 1 if contador == 0: break print j 
       

Generación de iteradores

Aunque xsrange nos ofrece una manera de generar listas "bajo demanda", en programación funcional es muy común necesitar listas "infinitas", pues a priori no se conoce el número de elementos que habrá que recorrer. Para generar iteradores infinitos, tenemos la palabra clave yield. Esta instrucción es similar al return con la diferencia de que no interrumpe la evaluación de la función: devuelve un elemento y se queda en ese punto de la función esperando a que le pidan otro elemento. En ese caso, continúa la evaluación por donde estaba.

def fib(): a, b = 0, 1 while 1: yield b a, b = b, a+b 
       
fibo=fib() 
       
for i in range(10): print fibo.next() 
       

Sage y Lisp

Como no podía ser de otra manera, si nos vemos necesitados de un lenguaje puro de programación funcional, Sage nos proporciona una interfaz para varios de ellos. El siguiente ejemplo muestra cómo ejecutar código Lisp dentro de Sage.

lisp.eval('(defun fib (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))') 
       
lisp(10).fib()