# Интерфейсы

Краеугольным камнем Sage является поддержка вычислений с использованием объектов из разных систем комьпютерной алгебры, которые находятся «под одной крышей» и используют общий интерфейс и чистый язык программирования.

Методы console и interact интерфейса делают разные вещи. Например, используя GAP как пример:

1. `gap.console()`: Открывает консоль GAP и передает управление GAP’у. Здесь Sage выступает в роли удобной командной строки, наподобие оболочки Bash в GNU/Linux.
2. `gap.interact()`: Это удобный способ взаимодействия с запущенным интерфейсом GAP, «заполненным» объектами Sage. Вы можете импортировать объекты Sage в сессию GAP (даже из интерактивного интерфейса), и пр.

## GP/PARI

PARI это компактная, очень продуманная и хорошо оптимизированная программа на C, сосредоточенная на теории чисел. Существует два раздельных интерфейса, которые вы можете использовать в Sage:

- `gp` - gp - интерпретатор «G o P ARI» , и
- `pari` - pari - С-библиотека PARI.

Например, следующие две строчки выполняют одну и ту же операцию. Они выглядят идентично, но вывод на самом деле отличается, а за кулисами происходят совсем разные вещи.

    sage: gp('znprimroot(10007)')
    Mod(5, 10007)
    sage: pari('znprimroot(10007)')
    Mod(5, 10007)

В первом случае отдельная копия интерпретатора GP запускается как сервер, и строка `'znprimroot(10007)'` отправляется в него, вычисляется с помощью GP, и результат записывается в переменную в GP (которая занимает пространство в памяти процесса GP и не будет освобождена). После этого значение переменной выводится на экран. Во втором случае отдельная программа не запускается, и строка `'znprimroot(10007)'` вычисляется конкретной функцией С-библиотеки PARI. Результат сохраняется в heap-памяти Python’а, которая освобождается после того, как переменная перестает использоваться. У объектов разный тип:

    sage: type(gp('znprimroot(10007)'))
    <class 'sage.interfaces.gp.GpElement'>
    sage: type(pari('znprimroot(10007)'))
    <type 'cypari2.gen.Gen'>

Так какой же способ использовать? Это зависит от того, что вы делаете. Интерфейс GP может делать все, что может делать программа GP/PARI, запускаемая из командной строки, потому как он запускает эту программу. Вы можете загрузить сложную программу PARI и запустить ее. С другой стороны, интерфейс PARI (через C-библиотеку) имеет намного больше ограничений. Во-первых, не все функции в ней реализованы. Во-вторых, много кода, например, численное интегрирование, не будет работать через интерфейс PARI. Интерйес PARI может быть намного быстрее и понятнее, чем сравнению с GP.

(Если у интерфейса GP закончится память при вычислении данной строки, он автоматически и без предупреждения удовит размер стека и попробует вычисление еще раз. Поэтому ваши вычисления всегда будут произведены корректно, если вы правильно расчитаете размер необходимой памяти. Этот удобный трюк не входит в арсенал простого интерпретатора GP. Относительно интерфейса C-библиотеки PARI: он сразу копирует каждый созданный объект из стека PARI, поэтому стек никогда не растет. Однако, каждый объект не должен превышать размера в 100 мегабайт, или стек будет переполнен при создании объекта. Дополнительное копирование немного влияет на общую производительность.)

Sage использует С-библиотеку PARI, чтобы поддерживать функциональность, схожую с интерпретатором GP/PARI, но включая различные сложные операции по работе с памятью и язык программирования Python.

Сначала, создадим список PARI из списка Python.

    sage: v = pari([1,2,3,4,5])
    sage: v
    [1, 2, 3, 4, 5]
    sage: type(v)
    <type 'cypari2.gen.Gen'>

Каждый объект PARI является объектом типа `Gen`. Тип PARI может быть получен с помощью функции-члена `type`.

    sage: v.type()
    't_VEC'

В PARI, чтобы создать эллиптическую кривую, нужно ввести `ellinit([1,2,3,4,5])`. В Sage способ схож, только `ellinit` — это метод, который может быть вызван для любого объекта PARI, например наша `t_VEC` \(v\).

    sage: e = v.ellinit()
    sage: e.type()
    't_VEC'
    sage: pari(e)[:13]
    [1, 2, 3, 4, 5, 9, 11, 29, 35, -183, -3429, -10351, 6128487/10351]

Теперь, когда у нас есть объект эллиптическая кривая, мы можем вычислить что-нибудь.

    sage: e.elltors()
    [1, [], []]
    sage: e.ellglobalred()
    [10351, [1, -1, 0, -1], 1, [11, 1; 941, 1], [[1, 5, 0, 1], [1, 5, 0, 1]]]
    sage: f = e.ellchangecurve([1,-1,0,-1])
    sage: f[:5]
    [1, -1, 0, 4, 3]

## GAP

Sage поставляется с GAP для вычислений в области дискретной математики, в особенности, в теории групп.

Вот пример функции `IdGroup` из GAP, которая использует базу данных небольших групп, которая должна быть установлена отдельно, как показано ниже.

    sage: G = gap('Group((1,2,3)(4,5), (3,4))')
    sage: G
    Group( [ (1,2,3)(4,5), (3,4) ] )
    sage: G.Center()
    Group( () )
    sage: G.IdGroup()    # optional - database_gap
    [ 120, 34 ]
    sage: G.Order()
    120

Мы можем провести те же вычисления в Sage без прямого вызова интерфейса GAP следующим образом:

    sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])
    sage: G.center()
    Subgroup of (Permutation Group with generators [(3,4), (1,2,3)(4,5)]) generated by [()]
    sage: G.group_id()     # optional - database_gap
    [120, 34]
    sage: n = G.order(); n
    120

Некотоые функции системы GAP доступны только после инсталляции двух дополнительных пакетов Sage. Они могут быть установлены командой:

    sage -i gap_packages database_gap

## Singular

Singular предоставляет массивную и продуманную библиотеку для базиса Грёбнера, нахождения наибольшего общего делителя полиномов, базиса пространств плоских кривых Римана-Роха и факторизации, наряду с другими вещами. Мы покажем пример факторизации полиномов с несколькими переменными, используя интерфейс Singular в Sage (не вводите `....:`):

    sage: R1 = singular.ring(0, '(x,y)', 'dp')
    sage: R1
    polynomial ring, over a field, global ordering
    //   coefficients: QQ
    //   number of vars : 2
    //        block   1 : ordering dp
    //                  : names    x y
    //        block   2 : ordering C
    sage: f = singular('9*y^8 - 9*x^2*y^7 - 18*x^3*y^6 - 18*x^5*y^6 +'
    ....:     '9*x^6*y^4 + 18*x^7*y^5 + 36*x^8*y^4 + 9*x^10*y^4 - 18*x^11*y^2 -'
    ....:     '9*x^12*y^3 - 18*x^13*y^2 + 9*x^16')

Теперь когда мы определили \(f\), мы выводим на экран и факторизуем.

    sage: f
    9*x^16-18*x^13*y^2-9*x^12*y^3+9*x^10*y^4-18*x^11*y^2+36*x^8*y^4+18*x^7*y^5-18*x^5*y^6+9*x^6*y^4-18*x^3*y^6-9*x^2*y^7+9*y^8
    sage: f.parent()
    Singular
    sage: F = f.factorize(); F
    [1]:
       _[1]=9
       _[2]=x^6-2*x^3*y^2-x^2*y^3+y^4
       _[3]=-x^5+y^2
    [2]:
       1,1,2
    sage: F[1][2]
    x^6-2*x^3*y^2-x^2*y^3+y^4

Как и на примере GAP в GAP, мы можем совершить данную факторизацию без прямого указания интерфейса Sage (однако за кулисами Sage все равно используется интерфейс Singular). Не вводите `....:`:

    sage: x, y = QQ['x, y'].gens()
    sage: f = (9*y^8 - 9*x^2*y^7 - 18*x^3*y^6 - 18*x^5*y^6 + 9*x^6*y^4
    ....:     + 18*x^7*y^5 + 36*x^8*y^4 + 9*x^10*y^4 - 18*x^11*y^2 - 9*x^12*y^3
    ....:     - 18*x^13*y^2 + 9*x^16)
    sage: factor(f)
    (9) * (-x^5 + y^2)^2 * (x^6 - 2*x^3*y^2 - x^2*y^3 + y^4)

## Maxima

Maxima включена в Sage, так же как реализация Лиспа. Пакет gnuplot (который Maxima использует по умолчанию для построения графиков) распространяется как дополнительный пакет Sage. Кроме остальных вещей, Maxima позволяет производить символические манипуляции. Maxima может интегрировать и дифференцировать функции символически, решать обыкновенные дифференциальные уравнения 1го порядка, большую часть линейных обыкновенных дифференциальных уравнений 2го порядка, использовать преобразования Лапласа как метод для решения линейных обыкновенных дифференциальных уравнений любого порядка. Maxima также «знает» о большом наборе специальных функций, имеет возможность строить графики при помощи gnuplot, имеет методы решения и манипуляции матрицами (к примеру, метод Гаусса, нахождение собственных значений и векторов), а также умеет решать полиномы.

Мы проиллюстрируем работу Sage/Maxima с помощью матрицы, значения \(i,j\) которой являются \(i/j\), для \(i,j=1,\ldots,4\).

    sage: f = maxima.eval('ij_entry[i,j] := i/j')
    sage: A = maxima('genmatrix(ij_entry,4,4)'); A
    matrix([1,1/2,1/3,1/4],[2,1,2/3,1/2],[3,3/2,1,3/4],[4,2,4/3,1])
    sage: A.determinant()
    0
    sage: A.echelon()
    matrix([1,1/2,1/3,1/4],[0,0,0,0],[0,0,0,0],[0,0,0,0])
    sage: A.eigenvalues()
    [[0,4],[3,1]]
    sage: A.eigenvectors()
    [[[0,4],[3,1]],[[[1,0,0,-4],[0,1,0,-2],[0,0,1,-4/3]],[[1,2,3,4]]]]

Вот другой пример:

    sage: A = maxima("matrix ([1, 0, 0], [1, -1, 0], [1, 3, -2])")
    sage: eigA = A.eigenvectors()
    sage: V = VectorSpace(QQ,3)
    sage: eigA
    [[[-2,-1,1],[1,1,1]],[[[0,0,1]],[[0,1,3]],[[1,1/2,5/6]]]]
    sage: v1 = V(sage_eval(repr(eigA[1][0][0]))); lambda1 = eigA[0][0][0]
    sage: v2 = V(sage_eval(repr(eigA[1][1][0]))); lambda2 = eigA[0][0][1]
    sage: v3 = V(sage_eval(repr(eigA[1][2][0]))); lambda3 = eigA[0][0][2]

    sage: M = MatrixSpace(QQ,3,3)
    sage: AA = M([[1,0,0],[1, - 1,0],[1,3, - 2]])
    sage: b1 = v1.base_ring()
    sage: AA*v1 == b1(lambda1)*v1
    True
    sage: b2 = v2.base_ring()
    sage: AA*v2 == b2(lambda2)*v2
    True
    sage: b3 = v3.base_ring()
    sage: AA*v3 == b3(lambda3)*v3
    True

Наконец, мы покажем, как строить графики средствами `openmath`. Многие примеры являются модифицированными примерами из руководства к Maxima.

2-мерные графики нескольких функций (не вводите `....:`):

    sage: maxima.plot2d('[cos(7*x),cos(23*x)^4,sin(13*x)^3]','[x,0,1]', # not tested
    ....:     '[plot_format,openmath]')

«Живой» трехмерный график, который вы можете вращать мышкой (не вводите `....:`):

    sage: maxima.plot3d ("2^(-u^2 + v^2)", "[u, -3, 3]", "[v, -2, 2]", # not tested
    ....:     '[plot_format, openmath]')
    sage: maxima.plot3d("atan(-x^2 + y^3/4)", "[x, -4, 4]", "[y, -4, 4]", # not tested
    ....:     "[grid, 50, 50]",'[plot_format, openmath]')

Следующий график — это знаменитая Лента Мёбиуса (не вводите `....:`):

    sage: maxima.plot3d("[cos(x)*(3 + y*cos(x/2)), sin(x)*(3 + y*cos(x/2)), y*sin(x/2)]", # not tested
    ....:     "[x, -4, 4]", "[y, -4, 4]",
    ....:     '[plot_format, openmath]')

Следующий график — это знаменитая Бутылка Клейна (не вводите `....:`):

    sage: maxima("expr_1: 5*cos(x)*(cos(x/2)*cos(y) + sin(x/2)*sin(2*y)+ 3.0) - 10.0")
    5*cos(x)*(sin(x/2)*sin(2*y)+cos(x/2)*cos(y)+3.0)-10.0
    sage: maxima("expr_2: -5*sin(x)*(cos(x/2)*cos(y) + sin(x/2)*sin(2*y)+ 3.0)")
    -5*sin(x)*(sin(x/2)*sin(2*y)+cos(x/2)*cos(y)+3.0)
    sage: maxima("expr_3: 5*(-sin(x/2)*cos(y) + cos(x/2)*sin(2*y))")
    5*(cos(x/2)*sin(2*y)-sin(x/2)*cos(y))
    sage: maxima.plot3d ("[expr_1, expr_2, expr_3]", "[x, -%pi, %pi]", # not tested
    ....:     "[y, -%pi, %pi]", "['grid, 40, 40]",
    ....:     '[plot_format, openmath]')