***********
Programação
***********

.. _section-loadattach:

Carregando e Anexando Arquivos do Sage
======================================

A seguir ilustramos como carregar no Sage programas escritos em um
arquivo separado. Crie um arquivo chamado ``example.sage`` com o
seguinte conteúdo:

.. skip

::

    print("Hello World")
    print(2^3)

Você pode ler e executar o arquivo ``example.sage`` usando o comando
``load``.

.. skip

::

    sage: load "example.sage"
    Hello World
    8

Você também pode anexar um arquivo em Sage à sessão em execução usando
o comando ``attach``.

.. skip

::

    sage: attach "example.sage"
    Hello World
    8

Agora se você alterar ``example.sage`` e adicionar uma linha em branco
(por exemplo), então o conteúdo de ``example.sage`` será
automaticamente recarregado no Sage.

Em particular, ``attach`` automaticamente recarrega um arquivo toda
vez que ele for modificado, o que é útil para desenvolver e testar um
programa, enquanto ``load`` carrega o arquivo apenas uma vez.

Quando o Sage carrega ``example.sage`` ele converte esse arquivo para
o Python, o qual é então executado pelo interpretador do Python. Essa
conversão é mínima; ela essencialmente consiste em encapsular inteiros
em ``Integer()``, números float em ``RealNumber()``, substituir ``^``
por ``**``, e substituir, por exemplo, ``R.2`` por ``R.gen(2)``. A
versão convertida de ``example.sage`` é armazenada no mesmo diretório
de ``example.sage`` e é chamada ``example.sage.py``. Esse arquivo
contém o seguinte código:

::

    print("Hello World")
    print(Integer(2)**Integer(3))

Inteiros literais são encapsulados e ``^`` é substituído por ``**``.
(Em Python, ``^`` significa "ou exclusivo" e ``**`` significa
"exponenciação".)

Esse "" está implementado em ``sage/misc/interpreter.py``.)

Você pode colar código tabulado com muitas linhas no Sage desde que
existam linhas em branco separando blocos de código (isso não é
necessário em arquivos). Todavia, a melhor forma de adicionar tal
código a uma sessão do Sage é salvá-lo em um arquivo e usar
``attach``, como descrito anteriormente.


.. _section-compile:

Criando Código Compilado
========================

Velocidade é crucial em cálculos matemáticos. Embora o Python seja uma
linguagem conveniente de alto nível, certos cálculos podem ser várias
vezes mais rápidos do que em Python se eles forem implementados usando
tipos estáticos em uma linguagem compilada. Alguns aspectos do Sage
seriam muito lentos se eles fossem escritos inteiramente em Python.
Para lidar com isso, o Sage suporta uma "versão" compilada do Python
chamada Cython ([Cyt]_ and [Pyr]_). O Cython é simultaneamente similar
ao Python e ao C. A maior parte das construções em Python, incluindo
"list comprehensions", expressões condicionais, código como ``+=`` são
permitidos; você também pode importar código que você escreveu em
outros módulos em Python. Além disso, você pode declarar variáveis em
C arbitrárias, e qualquer chamada de bibliotecas em C pode ser feita
diretamente. O código resultante é convertido para C e compilado
usando um compilador para C.

Para criar o seu próprio código compilado em Sage, nomeie o arquivo
com uma extensão ``.spyx`` (em vez de ``.sage``). Se você está
trabalhando na linha de comando, você pode anexar e carregar código
compilado exatamente como se faz com código interpretado (no momento,
anexar e carregar código em Cython não é suportado no Notebook). A
compilação é realizada implicitamente sem que você tenha que fazer
qualquer coisa explicitamente. Veja
``$SAGE_ROOT/examples/programming/sagex/factorial.spyx`` para um
exemplo de uma implementação compilada para a função fatorial que usa
diretamente a biblioteca GMP em C. Experimente o seguinte, usando cd,
vá para o diretório ``$SAGE_ROOT/examples/programming/sagex/``, e
então faça o seguinte:

.. skip

::

    sage: load "factorial.spyx"
    ***************************************************
                    Recompiling factorial.spyx
    ***************************************************
    sage: factorial(50)
    30414093201713378043612608166064768844377641568960512000000000000L
    sage: time n = factorial(10000)
    CPU times: user 0.03 s, sys: 0.00 s, total: 0.03 s
    Wall time: 0.03

Aqui o sufixo L indica um "long integer" do Python (veja
:ref:`section-mathannoy`).

Note que o Sage vai recompilar ``factorial.spyx`` se você encerrar e
reiniciar o Sage. A biblioteca compilada e compartilhada é armazenada
em ``$HOME/.sage/temp/hostname/pid/spyx``. Esses arquivos são
excluídos quando você encerra o Sage.

Nenhum pré-processamento é aplicado em arquivos spyx, por exemplo,
``1/3`` vai resultar em 0 em um arquivo spyx em vez do número racional
:math:`1/3`. Se ``foo`` é uma função da biblioteca Sage, para usá-la
em um arquivo spyx importe ``sage.all`` e use ``sage.all.foo``.

::

    import sage.all
    def foo(n):
        return sage.all.factorial(n)

Acessando Funções em C em Arquivos Separados
--------------------------------------------

É fácil também acessar funções em C definidas em arquivos \*.c
separados. Aqui vai um exemplo. Crie os arquivos ``test.c`` e
``test.spyx`` no mesmo diretório contendo:

Código C puro: ``test.c``

::

    int add_one(int n) {
      return n + 1;
    }

Código Cython: ``test.spyx``:

::

    cdef extern from "test.c":
        int add_one(int n)
    
    def test(n):
        return add_one(n)

Então o seguinte funciona:

.. skip

::

    sage: attach "test.spyx"
    Compiling (...)/test.spyx...
    sage: test(10)
    11

Se uma biblioteca ``foo`` adicional é necessária para compilar código
em C gerado a partir de um arquivo em Cython, adicione a linha ``clib
foo`` no arquivo fonte em Cython. De forma similar, um arquivo em C
adicional ``bar`` pode ser incluído na compilação declarando ``cfile
bar``.

.. _section-standalone:

Scripts Independentes em Python/Sage
====================================

O seguinte script em Sage fatora inteiros, polinômios, etc:

::

    #!/usr/bin/env sage
    
    import sys
    from sage.all import *
    
    if len(sys.argv) != 2:
        print("Usage: %s <n>" % sys.argv[0])
        print("Outputs the prime factorization of n.")
        sys.exit(1)
    
    print(factor(sage_eval(sys.argv[1])))

Para usar esse script, sua ``SAGE_ROOT`` precisa estar na sua variável
PATH. Se o script acima for chamado ``factor``, aqui está um exemplo
de como usá-lo:

::

    bash $ ./factor 2006
    2 * 17 * 59

Tipo de Dados
=============

Cada objeto em Sage possui um tipo bem definido. O Python possui
diversos tipos de dados, e a biblioteca do Sage adiciona ainda mais.
Os tipos de dados de Python incluem strings, listas, tuplas, inteiros
e floats, como ilustrado:

::

    sage: s = "sage"; type(s)
    <... 'str'>
    sage: s = 'sage'; type(s)      # you can use either single or double quotes
    <... 'str'>
    sage: s = [1,2,3,4]; type(s)
    <... 'list'>
    sage: s = (1,2,3,4); type(s)
    <... 'tuple'>
    sage: s = int(2006); type(s)
    <... 'int'>
    sage: s = float(2006); type(s)
    <... 'float'>

Além disso, o Sage acrescenta vários outros tipos. Por exemplo,
espaços vetoriais:

::

    sage: V = VectorSpace(QQ, 1000000); V
    Vector space of dimension 1000000 over Rational Field
    sage: type(V)
    <class 'sage.modules.free_module.FreeModule_ambient_field_with_category'>

Apenas certas funções podem ser aplicadas sobre ``V``. Em outros
softwares de matemática, essas seriam chamadas usando a notação
"funcional" ``foo(V,...)``. Em Sage, algumas funções estão anexadas ao
tipo (ou classe) de ``V``, e são chamadas usando uma sintaxe orientada
a objetos como em Java ou C++, por exemplo, ``V.foo()``. Isso ajuda a
manter o espaço de variáveis global sem milhares de funções, e permite
que várias funções diferentes com comportamento diferente possam ser
chamadas foo, sem a necessidade de usar um mecanismo de identificação
de tipos (ou casos) para decidir qual chamar. Além disso, se você
reutilizar o nome de uma função, essa função continua ainda disponível
(por exemplo, se você chamar algo ``zeta``, e então quiser calcular o
valor da função zeta de Riemann em 0.5, você continua podendo digitar
``s=.5; s.zeta()``).

::

    sage: zeta = -1
    sage: s=.5; s.zeta()     
    -1.46035450880959

Em alguns casos muito comuns, a notação funcional usual é também
suportada por conveniência e porque expressões matemáticas podem
parecer confusas usando a notação orientada a objetos. Aqui vão alguns
exemplos.

::

    sage: n = 2; n.sqrt()
    sqrt(2)
    sage: sqrt(2)
    sqrt(2)
    sage: V = VectorSpace(QQ,2)
    sage: V.basis()
        [
        (1, 0),
        (0, 1)
        ]
    sage: basis(V)
        [
        (1, 0),
        (0, 1)
        ]
    sage: M = MatrixSpace(GF(7), 2); M
    Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 7
    sage: A = M([1,2,3,4]); A
    [1 2]
    [3 4]
    sage: A.charpoly('x')
    x^2 + 2*x + 5
    sage: charpoly(A, 'x')
    x^2 + 2*x + 5

Para listar todas as funções para :math:`A`, use completamento tab.
Simplesmente digite ``A.``, então tecle ``[tab]`` no seu teclado, como
descrito em :ref:`section-tabcompletion`.

Listas, Tuplas e Sequências
===========================

O tipo de dados lista armazena elementos de um tipo arbitrário. Como
em C, C++, etc. (mas diferentemente da maioria dos sistemas de álgebra
computacional), os elementos da lista são indexados a partir do
:math:`0`:

::

    sage: v = [2, 3, 5, 'x', SymmetricGroup(3)]; v
    [2, 3, 5, 'x', Symmetric group of order 3! as a permutation group]
    sage: type(v)
    <... 'list'>
    sage: v[0]
    2
    sage: v[2]
    5

(Quando se indexa uma lista, é permitido que o índice não seja um int
do Python!) Um Inteiro do Sage (ou Racional, ou qualquer objeto que
possua um método ``__index__``) também ira funcionar.

::

    sage: v = [1,2,3]
    sage: v[2]
    3
    sage: n = 2      # Sage Integer
    sage: v[n]       # Perfectly OK!
    3
    sage: v[int(n)]  # Also OK.
    3

A função ``range`` cria uma lista de int's do Python (não Inteiros do
Sage):

::

    sage: range(1, 15)  # py2
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

Isso é útil quando se usa "list comprehensions" para construir listas:

::

    sage: L = [factor(n) for n in range(1, 15)]
    sage: L
    [1, 2, 3, 2^2, 5, 2 * 3, 7, 2^3, 3^2, 2 * 5, 11, 2^2 * 3, 13, 2 * 7]
    sage: L[12]
    13
    sage: type(L[12])
    <class 'sage.structure.factorization_integer.IntegerFactorization'>
    sage: [factor(n) for n in range(1, 15) if is_odd(n)]
    [1, 3, 5, 7, 3^2, 11, 13]

Para mais sobre como criar listas usando "list comprehensions", veja
[PyT]_.

Fatiamento de lista (list slicing) é um recurso fantástico. Se ``L`` é
uma lista, então ``L[m:n]`` retorna uma sub-lista de ``L`` obtida
começando do :math:`m`-ésimo elemento e terminando no
:math:`(n-1)`-ésimo elemento, como ilustrado abaixo.

::

    sage: L = [factor(n) for n in range(1, 20)]
    sage: L[4:9]
    [5, 2 * 3, 7, 2^3, 3^2]
    sage: L[:4]
    [1, 2, 3, 2^2]
    sage: L[14:4]
    []
    sage: L[14:]
    [3 * 5, 2^4, 17, 2 * 3^2, 19]

Tuplas são semelhantes à listas, exceto que elas são imutáveis: uma
vez criadas elas não podem ser alteradas.

::

    sage: v = (1,2,3,4); v
    (1, 2, 3, 4)
    sage: type(v)
    <... 'tuple'>
    sage: v[1] = 5
    Traceback (most recent call last):
    ...   
    TypeError: 'tuple' object does not support item assignment

Sequências são um terceiro tipo de dados do Sage semelhante a listas.
Diferentemente de listas e tuplas, Sequence não é um tipo de dados
nativo do Python. Por definição, uma sequência é mutável, mas usando o
método ``set_immutable`` da classe ``Sequence`` elas podem ser feitas
imutáveis, como mostra o exemplo a seguir. Todos os elementos da
sequência possuem um parente comum, chamado o universo da sequência.

::

    sage: v = Sequence([1,2,3,4/5])
    sage: v
    [1, 2, 3, 4/5]
    sage: type(v)
    <class 'sage.structure.sequence.Sequence_generic'>
    sage: type(v[1])
    <type 'sage.rings.rational.Rational'>
    sage: v.universe()
    Rational Field
    sage: v.is_immutable()
    False
    sage: v.set_immutable()
    sage: v[0] = 3
    Traceback (most recent call last):
    ...
    ValueError: object is immutable; please change a copy instead.

Sequências são derivadas de listas e podem ser usadas em qualquer
lugar que listas são usadas.

::

    sage: v = Sequence([1,2,3,4/5])
    sage: isinstance(v, list)
    True
    sage: list(v)
    [1, 2, 3, 4/5]
    sage: type(list(v))
    <... 'list'>

Como um outro exemplo, bases para espaços vetoriais são sequências
imutáveis, pois é importante que elas não sejam modificadas.

::

    sage: V = QQ^3; B = V.basis(); B
    [
    (1, 0, 0),
    (0, 1, 0),
    (0, 0, 1)
    ]
    sage: type(B)
    <class 'sage.structure.sequence.Sequence_generic'>
    sage: B[0] = B[1]
    Traceback (most recent call last):
    ...
    ValueError: object is immutable; please change a copy instead.
    sage: B.universe()
    Vector space of dimension 3 over Rational Field

Dicionários
===========

Um dicionário (também chamado as vezes de lista associativa ou "hash
table") é um mapeamento de objetos em objetos arbitrários. (Exemplos
de objetos que admitem uma lista associativa são strings e números;
veja a documentação Python em https://docs.python.org/3/tutorial/index.html
para detalhes).

::

    sage: d = {1:5, 'sage':17, ZZ:GF(7)}
    sage: type(d)
    <... 'dict'>
    sage: list(d.keys())
    [1, 'sage', Integer Ring]
    sage: d['sage']
    17
    sage: d[ZZ]
    Finite Field of size 7
    sage: d[1]
    5

A terceira chave (key) ilustra como os índices de um dicionário podem
ser complicados, por exemplo, um anel de inteiros.

Você pode transformar o dicionário acima em uma lista com os mesmos
dados:

.. link

::

    sage: list(d.items())
    [(1, 5), ('sage', 17), (Integer Ring, Finite Field of size 7)]

É comum iterar sobre os pares em um dicionário:

:: 

    sage: d = {2:4, 3:9, 4:16}
    sage: [a*b for a, b in d.items()]
    [8, 27, 64]

Um dicionário não possui ordem, como o exemplo acima mostra.

Conjuntos
=========

O Python possui um tipo de conjuntos (set) nativo. O principal recurso
que ele oferece é a rápida verificação se um objeto está ou não em um
conjunto, juntamente com as operações comuns em conjuntos.

::

    sage: X = set([1,19,'a']);   Y = set([1,1,1, 2/3])
    sage: X   # random
    {1, 19, 'a'}
    sage: Y   # random
    {2/3, 1}
    sage: 'a' in X
    True
    sage: 'a' in Y
    False
    sage: X.intersection(Y)
    {1}

O Sage também possui o seu próprio tipo de dados para conjuntos que é
(em alguns casos) implementado usando o tipo nativo do Python, mas
possuir algumas funcionalidades adicionais. Crie um conjunto em Sage
usando ``Set(...)``. Por exemplo,

::

    sage: X = Set([1,19,'a']);   Y = Set([1,1,1, 2/3])
    sage: X   # random
    {'a', 1, 19}
    sage: Y   # random
    {1, 2/3}
    sage: X.intersection(Y)
    {1}
    sage: print(latex(Y))
    \left\{1, \frac{2}{3}\right\}
    sage: Set(ZZ)
    Set of elements of Integer Ring

Iteradores
==========

Iteradores foram adicionados recentemente ao Python e são
particularmente úteis em aplicações matemáticas. Aqui estão vários
exemplos; veja [PyT]_ para mais detalhes. Vamos criar um iterador
sobre o quadrados dos números inteiros até :math:`10000000`.

::

    sage: v = (n^2 for n in xrange(10000000))  # py2
    sage: v = (n^2 for n in range(10000000))  # py3
    sage: next(v)
    0
    sage: next(v)
    1
    sage: next(v)
    4

Criamos agora um iterador sobre os primos da forma :math:`4p+1` com
:math:`p` também primo, e observamos os primeiros valores.

::

    sage: w = (4*p + 1 for p in Primes() if is_prime(4*p+1))
    sage: w         # in the next line, 0xb0853d6c is a random 0x number
    <generator object at 0xb0853d6c>
    sage: next(w)
    13
    sage: next(w)
    29
    sage: next(w)
    53

Certos anéis, por exemplo, corpos finitos e os inteiros possuem
iteradores associados a eles:

::

    sage: [x for x in GF(7)]
    [0, 1, 2, 3, 4, 5, 6]
    sage: W = ((x,y) for x in ZZ for y in ZZ)
    sage: next(W)
    (0, 0)
    sage: next(W)
    (0, 1)
    sage: next(W)
    (0, -1)

Laços, Funções, Enunciados de Controle e Comparações
====================================================

Nós já vimos alguns exemplos de alguns usos comuns de laços (loops)
``for``. Em Python, um laço ``for`` possui uma estrutura tabulada, tal
como

::

    >>> for i in range(5):
    ...      print(i)       
    ...
    0
    1
    2
    3
    4

Note que os dois pontos no final do enunciado (não existe "do" ou "od"
como no GAP ou Maple), e a identação antes dos comandos dentro do
laço, isto é, ``print(i)``. A tabulação é importante. No Sage, a
tabulação é automaticamente adicionada quando você digita ``enter``
após ":", como ilustrado abaixo.

::

    sage: for i in range(5):
    ....:     print(i)  # now hit enter twice
    0
    1
    2
    3
    4

O símbolo ``=`` é usado para atribuição.
O símbolo ``==`` é usado para verificar igualdade:

::

    sage: for i in range(15):
    ....:     if gcd(i,15) == 1:
    ....:         print(i)
    1
    2
    4
    7
    8
    11
    13
    14

Tenha em mente como a tabulação determina a estrutura de blocos para
enunciados ``if``, ``for``, e ``while``:

::

    sage: def legendre(a,p):
    ....:     is_sqr_modp=-1
    ....:     for i in range(p):
    ....:         if a % p == i^2 % p:
    ....:             is_sqr_modp=1
    ....:     return is_sqr_modp
             
    sage: legendre(2,7)
    1
    sage: legendre(3,7)
    -1

Obviamente essa não é uma implementação eficiente do símbolo de
Legendre! O objetivo é ilustrar vários aspectos da programação em
Python/Sage. A função {kronecker}, que já vem com o Sage, calcula o
símbolo de Legendre eficientemente usando uma biblioteca em C do PARI.

Finalmente, observamos que comparações, tais como ``==``, ``!=``,
``<=``, ``>=``, ``>``, ``<``, entre números irão automaticamente
converter ambos os números para o mesmo tipo, se possível:

::

    sage: 2 < 3.1; 3.1 <= 1
    True
    False
    sage: 2/3 < 3/2;   3/2 < 3/1
    True
    True

Use bool para desigualdades simbólicas:

::

    sage: x < x + 1
    x < x + 1
    sage: bool(x < x + 1)
    True

Quando se compara objetos de tipos diferentes no Sage, na maior parte
dos casos o Sage tenta encontrar uma coação canônica para ambos os
objetos em um parente comum (veja :ref:`section-coercion` para mais
detalhes). Se isso for bem sucedido, a comparação é realizada entre os
objetos que foram coagidos; se não for bem sucedido, os objetos são
considerados diferentes. Para testar se duas variáveis fazem
referência ao mesmo objeto use ``is``. Como se vê no próximo exemplo,
o int ``1`` do Python é único, mas o Inteiro ``1`` do Sage não é.

::

    sage: 1 is 2/2
    False
    sage: int(1) is int(2)/int(2)  # py2
    True
    sage: 1 is 1
    False
    sage: 1 == 2/2
    True

Nas duas linhas seguintes, a primeira igualdade é falsa (``False``)
porque não existe um morfismo canônico :math:`QQ\to \GF{5}`, logo
não há uma forma de comparar o :math:`1` em :math:`\GF{5}` com o
:math:`1 \in \QQ`. Em contraste, existe um mapa canônico entre
:math:`\ZZ \to \GF{5}`, logo a segunda comparação é verdadeira
(``True``)

::

    sage: GF(5)(1) == QQ(1); QQ(1) == GF(5)(1)
    False
    False
    sage: GF(5)(1) == ZZ(1); ZZ(1) == GF(5)(1)
    True
    True
    sage: ZZ(1) == QQ(1)
    True

ATENÇÃO: Comparação no Sage é mais restritiva do que no Magma, o qual
declara :math:`1 \in \GF{5}` igual a :math:`1 \in \QQ`.

::

    sage: magma('GF(5)!1 eq Rationals()!1')            # optional magma required
    true

Otimização (Profiling)
======================

Autor desta seção: Martin Albrecht (https://martinralbrecht.wordpress.com/)

    "Premature optimization is the root of all evil." - Donald Knuth

As vezes é útil procurar por gargalos em programas para entender quais
partes gastam maior tempo computacional; isso pode dar uma boa ideia
sobre quais partes otimizar. Python e portanto Sage fornecem várias
opções de "profiling" (esse é o nome que se dá ao processo de
otimização).

O mais simples de usar é o comando ``prun`` na linha de comando
interativa. Ele retorna um sumário sobre o tempo computacional
utilizado por cada função. Para analisar (a atualmente lenta! -- na
versão 1.0) multiplicação de matrizes sobre corpos finitos, por
exemplo, faça o seguinte:

::

    sage: k,a = GF(2**8, 'a').objgen()
    sage: A = Matrix(k,10,10,[k.random_element() for _ in range(10*10)])

.. skip

::

    sage: %prun B = A*A
           32893 function calls in 1.100 CPU seconds
    
    Ordered by: internal time
    
    ncalls tottime percall cumtime percall filename:lineno(function)
     12127  0.160   0.000   0.160  0.000 :0(isinstance)
      2000  0.150   0.000   0.280  0.000 matrix.py:2235(__getitem__)
      1000  0.120   0.000   0.370  0.000 finite_field_element.py:392(__mul__)
      1903  0.120   0.000   0.200  0.000 finite_field_element.py:47(__init__)
      1900  0.090   0.000   0.220  0.000 finite_field_element.py:376(__compat)
       900  0.080   0.000   0.260  0.000 finite_field_element.py:380(__add__)
         1  0.070   0.070   1.100  1.100 matrix.py:864(__mul__)
      2105  0.070   0.000   0.070  0.000 matrix.py:282(ncols)
      ...

Aqui ``ncalls`` é o números de chamadas, ``tottime`` é o tempo total
gasto por uma determinada função (excluíndo o tempo gasto em chamadas
de subfunções), ``percall`` é o quociente de ``tottime`` dividido por
``ncalls``. ``cumtime`` é o tempo total gasto nessa e em todas as
subfunções (isto é, desde o início até o término da execução da
função), ``percall`` é o quociente de ``cumtime`` dividido pelas
chamadas primitivas, e ``filename:lineno(function)`` fornece os dados
respectivos para cada função. A regra prática aqui é: Quanto mais no
topo uma função aparece nessa lista, mais custo computacional ela
acarreta. Logo é mais interessante para ser optimizada.

Como usual, ``prun?`` fornece detalhes sobre como usar o "profiler" e
como entender a saída de dados.

A saída de dados pode ser escrita em um objeto para permitir uma
análise mais detalhada:

.. skip

::

    sage: %prun -r A*A
    sage: stats = _
    sage: stats?

Note: digitando ``stats = prun -r A\*A`` obtém-se um erro de sintaxe
porque prun é um comando do IPython, não uma função comum.

Para uma representação gráfica dos dados do "profiling", você pode
usar o "hotspot profiler", um pequeno script chamado
``hotshot2cachetree`` e o programa ``kcachegrind`` (apenas no Unix). O
mesmo exemplo agora com o "hotspot profiler":

.. skip

::

    sage: k,a = GF(2**8, 'a').objgen()
    sage: A = Matrix(k,10,10,[k.random_element() for _ in range(10*10)])
    sage: import hotshot
    sage: filename = "pythongrind.prof"
    sage: prof = hotshot.Profile(filename, lineevents=1)

.. skip

::

    sage: prof.run("A*A")
    <hotshot.Profile instance at 0x414c11ec>
    sage: prof.close()

Isso resulta em um arquivo ``pythongrind.prof`` no diretório de
trabalho atual. Ele pode ser convertido para o formato cachegrind para
visualização.

Em uma linha de comando do sistema, digite

.. skip

::

    hotshot2calltree -o cachegrind.out.42 pythongrind.prof

O arquivo de saída ``cachegrind.out.42`` pode ser examinado com
``kcachegrind``. Note que a convenção de nomes ``cachegrind.out.XX``
precisa ser obedecida.
