система символьной математики (Computer Algebra System)


  • функции
  • графики
  • файлы
  • решение уравнений
  • дифференцирование
  • интегрирование
  • пределы
  • матрицы
  • преобразования Лапласа
  • преобразования Фурье
  • тензоры
  • скрипты

  • максима — система работы с символьными и численными выражениями, включающая дифференцирование, интегрирование, разложение в ряд, преобразования Лапласа, обыкновенные дифференциальные уравнения, системы линейных уравнений, многочлены, множества, списки, векторы, матрицы и тензоры

    символьные вычисления — это преобразования и работа с математическими равенствами и формулами как с последовательностью символов. системы символьных вычислений (их также называют Системами Компьютерной Алгебры СКА, Computer Algebra System CAS) используются для символьного интегрирования и дифференцирования, подстановки одних выражений в другие, упрощения формул и т.д. аналитические решения чаще удаётся получить для грубых (простых) моделей, реже — для более точных (сложных)

    максима производит численные расчеты высокой точности, используя точные дроби, целые числа и числа с плавающей точкой произвольной точности

    система позволяет строить графики функций и статистических данных

    команда в максиме завершается точкой с запятой. вывод результата вычисления можно заглушить, завершив команду символом $ вместо ;

    -   +   *   /   ** или ^   sqrt()

    (%i2) 3 + 4 ;
    (%o2)                                  7
    (%i3) _ + 6 ;
    (%o3)                                 13
    

    каждая ячейка имеет свою метку — заключенное в скобки имя ячейки. ячейки ввода именуются как %i с номером (i от input — ввод), ячейки вывода — как %o с соответствующим номером (o от output — вывод). со знака % начинаются все встроенные служебные имена. внутренними именами %phi, %e и %pi обозначены математические постоянные; а через %c с номером обозначаются константы, используемые при интегрировании

    мы можем обращаться к любой из предыдущих ячеек по ее имени, подставляя его в любые выражения. последняя ячейка вывода обозначается % , а последняя ячейка ввода _

    мы присваиваем значения переменным и в виде значения может выступать любое математическое выражение. делается это с помощью двоеточия :

    (%i4) eq : x^3 - x ;
                                         3
    (%o4)                               x  - x
    

    если надо вычислить значение выведенного выражения с переменными, то это можно сделать указав либо имя выражения либо номер ячейки, а потом через запятую со знаками равенства - значения переменных

    (%i1) f : x*x + 2*x ;
                                        2
    (%o1)                              x  + 2 x
    (%i2) %o1,x=4 ;
    (%o2)                                 24
    (%i3) g : x * x + 3 * y ;
                                              2
    (%o3)                              3 y + x
    (%i4) %o3,x=4,y=1 ;
    (%o4)                                 19
    
    (%i6) f,x=1,y=2,z=3;
    (%o6)                                - 13
    (%i7) %i1,x=1,y=2,z=3 ;
    (%o7)                                - 13
      

    все функции и операторы максима работают не только с действительными, но и комплексными числами. комплексные числа записываются в виде a+b*%i

    имя name можно очистить от присвоенного ему выражения и освободить занимаемую этим выражением память. для этого нужно набрать kill (name) . можно очистить разом всю память и освободить все имена, введя kill (all)

    кроме переменных есть еще и структуры, аргументы которых (поля) идентифицируются именем поля, а сама структура идентифицируется именем структуры. оператор @ обращается по имени к полю и получает значение. оператор new (name) создает новую структуру :

    (%i3) defstruct (foo (a, b, c));
    (%o3)                           [foo(a, b, c)]
    (%i4) m : new (foo);
    (%o4)                            foo(a, b, c)
    (%i5) defstruct (bar (v, w, x = 123, y = %pi));
    (%o5)                    [bar(v, w, x = 123, y = %pi)]
    (%i6) structures;
    (%o6) [foo(a, b, c), bar(v, w, x = 123, y = %pi)]
    (%i7) kill (foo);
    (%o7)                                done
    (%i8) structures;
    (%o8) [bar(v, w, x = 123, y = %pi)]
    (%i9) m@a : 12
    (%o9)                                12
    (%i9) m@a;
    (%o10)                               12

    есть специальная функция — describe (name) , которая выдает информацию из документации по конкретным словам. существует сокращенная версия вызова этой функции: ? name

    знак апострофа введенный перед любым символом или выражением предотвращает его вычисление

    язык максимы - функциональный: вы можете передавать функции, как параметры и возвращать функции как значения. пример - файл "fp.mac":

    /* function, which first param is another function */
    h (k,a,b,c) := k (a,b,c) $
    f (x,y,z)   := x + y + z $   /* fun 01 */
    g (x,y,z)   := x * y * z $   /* fun 02 */
    
    /* function, which first param is expression */
    p (k,a,b) := ev (k, x=a,y=b) $
    r : x + y $                 /* expr 01 */
    s : x * y $                 /* expr 02 */
    
    и теперь в repl:
    (%i51) batchload ("fp.mac");
    (%o51)                /home/nomad/math/code/maxima/fp.mac
    (%i52) p(s,10,20);
    (%o52)                                200
    (%i53) p(r,10,20);
    (%o53)                                30
    (%i54) h(f,2,3,4) ;
    (%o54)                                 9
    (%i55) h(g,2,3,4) ;
    (%o55)                                24
    


    функции

    оператор задания функции обозначается через :=

    (%i10) func1 (x) := x + 2 ;
    (%o10)                         func1(x) := x + 2
    (%i11) func1 (12) ;
    (%o11)                                14
    

    оператор, обозначенный двумя апострофами, является синонимом к функции ev(выражение). сама функция ev() предоставляет широкие возможности: она может принимать произвольное число аргументов, первый из которых — вычисляемое выражение, а остальные - специальные опции, которые влияют на то, как именно будет производиться вычисление

    (%i13) '' 1  + 2 ;
    (%o13)                                  3
    (%i14) ev(4 + 7) ;
    (%o14)                                 11
    (%i15) 12 + 78, simp ;
    (%o15)                                 90

    точно так же, как двойной апостроф — сокращение для ev() без дополнительных опций, есть еще более упрощенная запись функции ev() с опциями: в этом случае вместо имени функции и скобок вообще ничего писать не нужно, т.е. ev(выражение,опц1,опц2,…) можно записать просто как «выражение,опц1,опц2,…»

    первая из таких опций связана с автоупрощением. глобально автоупрощение регулируется переключателем simp (от simplification), и по умолчанию оно включено; в любой момент его можно выключить, установив значение переключателя в false. опция функции ev, одноименная этому переключателю, позволяет включить упрощение для данного конкретного вычисления — вне зависимости от того, включено или выключено оно глобально

    функция rat(выражение) преобразовывает рациональное выражение к канонической форме (Canonical Rational Expression, CRE), то есть раскрывает все скобки, затем приводит все к общему знаменателю, суммирует и сокращает; кроме того, приводит все числа в конечной десятичной записи к рациональным. это удобно, если вам нужно пошагово проделать большое количество рациональных преобразований: вы можете, один раз вызвав rat() , ссылаться на предыдущие ячейки и далее видеть на каждом шаге итоговое выражение в канонической форме. если на каком-то этапе такое поведение станет вам мешать, вы можете вернуть выражение из канонической к общей форме с помощью функции ratdisrep(выражение) . каноническая форма автоматически отменяется и в случае любых преобразований, не являющихся рациональными

    ветвление

    if-then-else является не синтаксической конструкцией, а оператором: if условие then выражение1 else выражение2. при выполнении «условия» из двух «выражений» вычисляется только первое и возвращается как результат оператора; в противном случае выполняется только второе и оно же является значением всего выражения if-then-else . часть конструкции else выражение2 опциональна. если ее нет, а условие все-таки не выполнилось, результат оператора if будет равен false

    в условном операторе можно использовать любые предикаты, то есть функции, возвращающие логические значения true/false

    условия >, <, >=, <= записываются и расшифровываются традиционно, так же как и логические операторы and, or, not , а вот о равенствах-неравенствах нужно сказать пару слов. равенство в максима есть двух видов: синтаксическое и логическое. знаком = обозначается как раз первое, а второе вычисляется с помощью функции equal() . дополнительно используется предикат по имени is , которые проверяет на истинность свой аргумент

    ну и неравенств, соответственно, тоже существует два. синтаксическое неравенство обозначается достаточно непривычно — через #; видимо, этот символ разработчики сочли наиболее визуально схожим со знаком ≠. ну а логическое неравенство обозначено через notequal()

    итерация

    разновидности:

         for   переменная:начало   step шаг   thru    конец     do выражение
         for   переменная:начало   step шаг   while   условие   do выражение
         for   переменная:начало   step шаг   unless  условие   do выражение

    первая прокручивает цикл, изменяя переменную с заданным шагом от начала до конца

    вторая — от начала и пока выполняется условие

    третий — от начала и пока условие не выполняется

    'шаг' может быть опущен (по умолчанию он равен единице)

    опустить позволяется любую часть, кроме do. к примеру, опустив кроме step еще и for, мы получаем традиционные циклы while и unless, а проделав то же самое с первым вариантом, получим цикл без счетчика вида

        thru число do выражение  
    который просто повторится заданное число раз. можно, наоборот, опустить условие окончания и получить цикл с индексной переменной, но бесконечный. а оставив только do, получим самый простой вариант бесконечного цикла. из таких бесконечных циклов можно выйти с помощью оператора return(выражение) (точнее, конечно, конструкции из двух операторов вида if условие then return(выражение)), который прервет выполнение цикла и вместо done вернет заданное выражение. естественно, оператор return() можно применять во всех видах циклов, а не только в бесконечных

    кроме всех уже рассмотренных вариаций, цикл может принимать еще две ипостаси

    во-первых, вместо step может использоваться конструкция

        next выражение 

    после next может стоять любое вычислимое выражение относительно индекса цикла, и применяться эта конструкция может во всех трех вариантах цикла (thru/while/unless)

    а «во-вторых» — это еще один отдельный вариант цикла:

        for переменная in список do выражение 
    либо расширенная форма:
        for переменная in список условие do выражение 

    (%i26) allroots (x^2-3);
    (%o26)         [x = 1.732050807568877, x = - 1.732050807568877]
    (%i27) for x in %o26 do print (x);
    x = 1.732050807568877
    x = - 1.732050807568877
    (%o27)                               done  

    цикл будет прокручен с переменной, изменяющейся по всем элементам списка; можно задать еще и дополнительное условие на прерывание цикла


    графики

    основных функций всего две — plot2d и plot3d. возможности графической отрисовки не встроены в максима, а реализованы посредством внешних программ, в чем и прослеживается пресловутый Unix-way: «одна задача — одна программа». по умолчанию, построением графиков занимается gnuplot

    функция plot2d

    кратчайший вариант ее вызова такой:
    plot2d (выражение, [символ, начало, конец])
    где выражение задает функцию, график которой нужно построить, символ — неизвестное (он, понятное дело, должен быть единственным неопределенным символом, входящим в выражение), а начало и конец задают отрезок оси Х для построения графика; участок по оси Y в таком варианте записи выбирается автоматически, исходя из минимума и максимума функции на заданном промежутке

    обратите внимание, что неизвестное и концы промежутка нужно задавать не тремя отдельными параметрами, как, скажем, в integrate, а в виде списка. это связано с тем, что plot2d может принимать еще и дополнительные аргументы — в таком случае они перечисляются следом за таким списком, что исключает всякую путаницу

    (%i38) plot2d (x*cos(x), [x, -2*%pi, 2*%pi]) $

    после вызова функции plot2d в таком варианте откроется окно gnuplot, в котором будет отображен график. окно с графиком можно закрыть клавишей Q

    в некоторых случаях автоматический подбор отображаемого участка вертикальной оси может нас не устроить. например, он работает не очень хорошо, если функция имеет на заданном промежутке точку разрыва, хотя бы один из односторонних пределов в которой равен бесконечности: тогда промежуток по оси Y будет выбран слишком большим

    для этого предусмотрен такой вариант вызова функции:
    plot2d (выражение, [символ, начало, конец], [y, начало, конец])

    здесь буква y используется в качестве обозначения вертикальной оси, а остальные два параметра имеют тот же смысл, что и выше

    чтобы построить на одной и той же картинке одновременно два графика (или больше), просто передайте функции plot2d вместо отдельного выражения их список

    функция plot2d понимает еще одно ключевое слово: discrete. предназначено оно для отображения на плоскости дискретных множеств; точнее говоря, конечных наборов точек. по записи аргументов такой вариант распадается еще на два:

      plot2d ([discrete, x-список, y-список])
      plot2d ([discrete, [x, y]-список])

    в первом варианте координаты задаются как два отдельных списка

        [x1, x2, …, xn], [y1, y2, ,…, yn]  
    а во втором — как список пар координат отдельных точек
        [[x1, y1], [x2, y2], …, [xn, yn]] 
    если мы, к примеру, имеем набор статистических значений, зависящих от номера, мы можем отобразить его, задав в качестве x-координат сами эти номера

    функция plot3d

    имеет два варианта вызова: один для явного задания функции и один для параметрического. в обоих случаях функция принимает три аргумента. для явно заданной функции:

      plot3d (выражение, [переменная1, начало, конец], [переменная2, начало, конец])
      
    аргументы аналогичны plot2d, с той разницей, что здесь независимых переменных две


    файлы

    чтение

    функции чтения файлов с выражениями максима существует три:

    demo(имя-файла)
    batch(имя-файла)
    batchload(имя-файла)

    первая предназначена для загрузки демо-файлов, задуманных для демонстрационных примеров. она загружает демо-файл и выполняет его в пошаговом режиме, ожидая нажатия Enter после выполнения каждой строки. их расширение — .dem

    функция batch() загружает максима-файл с расширением .mac или .mc и выполняет содержащиеся в нем выражения так, как если бы они вводились прямо в текущей сессии, то есть с отображением результата каждого выражения и назначением меток %iN, %oN

    функция batchload() , напротив, подгружает пакетный файл «молча»: все назначенные в нем функции и переменные становятся доступны, но результаты не видны, и весь хранимый ввод-вывод, включая значения символов % и _ и результаты, возвращаемые функцией %th(), остается тем же, что и до вызова

    комментарии в скриптовых файлах максимы начинаются с /* и завершаются */ при этом они могут быть многострочными

    запись

    функция stringout() позволяет выгружать в файл любые выражения и функции максима в точно таком виде, в каком их загружают функции demo(), batch() и batchload() . с ее помощью можно писать выражения, которые вы хотите иметь во внешнем модуле, находясь непосредственно в интерфейсе максима

    stringout() может принимать несколько вариантов аргументов, первым из которых всегда выступает имя файла для записи, а остальные отвечают за то, что же именно будет туда записано

    в варианте stringout (имя-файла, [начало, конец]) записаны будут ячейки ввода с номерами от «начала» до «конца» включительно

    с помощью ключевого слова input можно выгрузить в файл все ячейки ввода разом:
    stringout (имя-файла, input)

    кроме input, есть еще два ключевых слова: functions и values

    первое позволяет записать определения всех функций, второе — присвоение всем символам выражений их текущих значений

    функцию stringout() можно вызвать с непосредственным перечислением в аргументах конкретных выражений. в этом случае будут сохраняться не ячейки, содержащие заданные выражения, а именно сами выражения

    с именами функций дело обстоит хуже: если просто написать имя функции, то вместо функции будет подставлен одноименный символ (или его значение, если оно задано). но из ситуации есть выход. функция fundef , которая принимает имя любой пользовательской функции и возвращает ее определение в точности в таком же виде, в каком оно было введено в командной строке максима, с точностью до пробелов


    решение уравнений

    solve (expr)
    solve (expr, x)
    solve ([expr1, ..., eqxpn], [x1, ..., xn])

    решает уравнение expr для переменной x, которое может быть опущено, если х - единственная переменная в уравенении

    (%i4) eq: x^3 - x ;
                                         3
    (%o4)                               x  - x
    
    (%i16) solve(eq) ;
    (%o16)                       [x = - 1, x = 1, x = 0]
    
    (%i17) a:%pi/4 $ b: %pi/2 ;
                                          %pi
    (%o17)                                ---
                                           2


    дифференцирование

    (%i4) eq : x^3 - x ;
                                         3
    (%o4)                               x  - x
    
    (%i18) diff (eq, x) ;
                                           2
    (%o18)                              3 x  - 1
    

    функция diff принимает один либо два аргумента. с двумя, diff(выражение, переменная), она возвращает производную от «выражения» по заданной переменной; с одним, diff(выражение) — полный дифференциал заданного выражения. запись diff(f, x) равнозначна математическому обозначению df/dx, а diff(f) равнозначна df


    интегрирование

    функция интегрирования называется integrate и имеет два варианта вызова: для нахождения неопределенного и определенного интегралов

    первый выглядит как integrate (выражение, переменная)

    второй — как integrate (выражение, переменная, нижний-предел, верхний-предел)

    (%i26) integrate (sin(x),x,a,b) ;
                                           1
    (%o26)                              -------
                                        sqrt(2)
    

    кроме определенных интегралов максима умеет искать также и несобственные интегралы, то есть такие, у которых неограничена либо область интегрирования, либо подынтегральная функция; и делается это все той же функцией integrate

    (%i27) integrate (1/(sqrt(x)), x, 0, 1) ;
    (%o27)                                   2
    


    пределы

    полноценных функций для нахождения предела существует в максима аж одна, но зато какая!

    она может принимать три различных варианта списка аргументов, и кроме того, на ее действие влияют еще и три флага. зовут эту функцию limit и в самом стандартном варианте ее вызов выглядит как

      limit (выражение, переменная, точка) 

    (%i29) limit ((x^2 - 1) / (2*x^2-x-1) , x , 1) ;
                                           2
    (%o29)                                 -
                                           3 

    максима может искать пределы не только в конечных точках, но и на бесконечности. среди стандартных обозначений программы существуют универсальные названия для разных бесконечностей:

  • плюс-бесконечность записывается через inf (от слова infinity)
  • минус-бесконечность — через minf (от minus infinity)
  • для комплексных чисел бесконечность, как известно, одна, и она (комплексная бесконечность) обозначается полным словом infinity
  • второй вариант вызова функции limit() — это расширенная версия первого:
    limit(выражение, переменная, точка, направление)
    для поиска односторонних пределов. для предела справа в качестве «направления» указывается plus, для предела слева — minus

    кроме упомянутых выше бесконечностей, на выходе возможно появление и еще двух обозначений, на случай, если заданный предел не существует:

  • ind (от слова indefinite — неопределенный)
  • und (от слова undefined — опять же неопределенный)
  • функция limit() в третьем варианте
    limit(выражение)
    предназначена уже не для поиска собственно пределов, а для упрощения выражений, содержащих символы inf и minf

    (%i35) limit(1/inf) ;
    (%o35)                                 0
    


    матричные вычисления

    точкой . обозначается матричное произведение

    (%i5) a : [1,2,3] ;
    (%o5)                              [1, 2, 3]
    (%i6) a . a ;
    (%o6)                                 14
    

    entermatrix (m, n)
      returns an m by n matrix, reading the elements interactively
    
    matrix (row_1, …, row_n)
      returns a rectangular matrix which has the rows row_1, …, row_n
      each row is a list of expressions
      all rows must be the same length
    
    copymatrix (M)
      returns a copy of the matrix M
      this is the only way to make a copy aside from copying M element by element
    
    
    
    ident (n)
      returns an n by n identity matrix
    
    zeromatrix (m, n)
      returns an m by n matrix, all elements of which are zero
    
    read_matrix ("/path/to/the/text.file")
      returns matrix with lines from the file as its rows
    
    
    
    transpose (M)
      returns the transpose of M
    
    invert (M)
      returns the inverse of the matrix M
    
    rank (M)
      computes the rank of the matrix M
    
    determinant (M)
      computes the determinant of M
    
    mattrace (M)
      returns the trace of the square matrix M
    
    
    
    (%i37) M : matrix ([1,2],[3,4]);
                                       [ 1  2 ]
    (%o37)                             [      ]
                                       [ 3  4 ]
    (%i38) K : triangularize (M);
                                      [ 1   2  ]
    (%o38)                            [        ]
                                      [ 0  - 2 ]
    
    
    ------------------------------------------------------
    
    diag_matrix (d, d2, ..., dn)
      return a diagonal matrix with diagonal entries d1, d2, ..., dn
    
    
    
    ematrix (m, n, x, i, j)
      returns an m-by-n matrix, all elements of which are zero
      except for the '[i, j]' element which is x
    
    
    
    vandermonde_matrix ([x1, ..., xn])
      return n-by-n matrix whose i-th row is [1, xi, xi^2, ... xi^(n-1)]
    
    
    
    jacobian ([f], [x])
      returns the Jacobian matrix of the list of functions f
      with respect to the list of variables x
      the (i,j)-th element of the Jacobian matrix is diff (f[i], x[j])
    
    
    
    hessian (f, [x])
      returns the Hessian matrix of f with respect to the list of variables x
      the (i,j)-th element of the Hessian matrix is diff (f, x[i], 1, x[j], 1)
    
    
    ------------------------------------------------------
    
    
    col (M, i)
      returns the i'th column of the matrix M
    
    row (M, i)
      returns the i'th row of the matrix M
    
    
    
    submatrix (i1 , ..., im , M , j1 , ... , jn)
    submatrix (i1 , ..., im , M)
    submatrix (M, j1 , ... , jn)
      returns a new matrix composed of the matrix M with
      rows i1, ..., im deleted, and columns j1, ..., jn deleted
    
    
    
    addrow (M, list_1, …, list_n)
      appends the row(s) given by the one or more lists (or matrices) onto the matrix M
    
    addcol (M, list_1, …, list_n)
      appends the column(s) given by the one or more lists (or matrices) onto the matrix M
    


    преобразования Лапласа

    (%i6) laplace (0,t,s) ;
    (%o6)                                  0
    
    (%i7) laplace (1,t,s) ;
                                           1
    (%o7)                                  -
                                           s
    
    (%i8) laplace (t,t,s) ;
                                          1
    (%o8)                                 --
                                           2
                                          s
    
    (%i9) laplace (exp(t),t,s) ;
                                           1
    (%o9)                                -----
                                         s - 1
    
    (%i10) laplace (cos(t),t,s) ;
                                          s
    (%o10)                              ------
                                         2
                                        s  + 1
    
    (%i11) laplace (sin(t),t,s) ;
                                          1
    (%o11)                              ------
                                         2
                                        s  + 1
    
    (%i12) laplace (exp(t)*sin(t),t,s) ;
                                          1
    (%o12)                           ------------
                                      2
                                     s  - 2 s + 2
    
    (%i13) laplace (exp(t)*cos(t),t,s) ;
                                        s - 1
    (%o13)                           ------------
                                      2
                                     s  - 2 s + 2
    
    (%i15) ilt (1/s^2,s,t) ;
    (%o15)                                 t
    
    (%i16) ilt (1/(s-1),s,t) ;
                                            t
    (%o16)                                %e
      


    преобразования Фурье

    (%i1) load (fft) $
    (%i2) fpprintprec : 4 $
    
    (%i3) L : [1, 2, 3, 4, -1, -2, -3, -4] $
    
    (%i4) L1 : fft (L) ;
    (%o4) [0.0, - 1.811 %i - .1036, 0.0, .6036 - .3107 %i, 0.0,
                             .3107 %i + .6036, 0.0, 1.811 %i - .1036]
    
    (%i5) L2 : inverse_fft (L1) ;
    (%o5) [1.0, 2.168L-19 %i + 2.0, 7.525L-20 %i + 3.0,
    4.256L-19 %i + 4.0, - 1.0, - 2.168L-19 %i - 2.0,
    - 7.525L-20 %i - 3.0, - 4.256L-19 %i - 4.0]
    
    (%i6) lmax (abs (L2 - L)) ;
    (%o6)                       3.545L-16
    


    тензоры

    за работу с абстрактными тензорами в максима отвечает модуль itensor

    kill(all);
    load(itensor);
    imetric(g);
    idim(3);
    

    мы чистим память (функция kill()), удаляя из неё все определения и загруженные модули,
    загружаем пакет itensor (функция load()),
    говорим, что метрический тензор будет именоваться g (функция imetric()),
    а так же, обязательно указываем размерность пространства (функция idim()), ибо по-умолчанию максима считает, что работает в 4-мерном пространстве-времени с метрикой Римана

    вводим тензор поворота:

    B00 : ishow ( u ([], [l]) * g ([j,k], []) * u ([], [j]) -
    cos (phi) * 'levi_civita ([], [l,q,i]) * u ([q], []) * 'levi_civita ([i,j,k], []) * u ([], [j]) +
    sin (phi) * g ([], [l,i]) * 'levi_civita ([i,j,k]) * u ([], [j]) ) $
    

    в itensor тензоры декларируются идентификатором, после которого в скобках указываются через запятую списки ковариантных и контравариантных индексов

    функция levi_civita() задает тензор Леви-Чивиты, а штрих перед ней означает, что данный тензор не надо вычислять

    упростим введенный тензор поворота:

    B01 : ishow (expand (lc2kdt (B00))) $
    

    функция lc2kdt() предназначена специально для упрощения выражений содержащих тензор Леви-Чивиты. она старается свернуть этот тензор там где это возможно, давая на выходе комбинацию сумм и произведений дельт Кронекера

    к результату lc2kdt() применяется функция expand(), раскрывающая скобки. без этого максима выполняет свертку очень неохотно

    теперь попытаемся вычислить полученное выражение, выполнив свертку. функция contract() один из наиболее надежных способов выполнения свертки:

    B02 : ishow (contract (B01)) $
    

    scripts

    /* -------------------------------------------------------------------------- 
       this is file critpts.max: 
       comments in maxima are like comments in C 
    --------------------------------------------------------------------------- */
    critpts():=(
       print("program to find critical points"),
    /*    asks for a function  */
       f:read("enter f(x,y)"),
    /*    echoes it, to make sure  */
       print("f = ",f),
    /*  produces a list with the two partial derivatives of f */
          eqs:[diff(f,x),diff(f,y)],
    /* produces a list of unknowns  */
          unk:[x,y],
    /* solves the system */
          solve(eqs,unk)   
    )$  
    

    now in repl:

    (%i1) batch ("critpts.max");