23 января 2012

Embeddable Maxima. Советы и хитрости.

Updated: 23.01.12

Почему максиму сложно интегрировать в веб

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

Посмотреть справку

(pushnew "/path/to/embeddable-maxima" asdf:*central-registry*)
(ql:quickload :embeddable-maxima)
(in-package :maxima)

Для просмотра справки необходимо вызвать функцию describe("function-name"). Данная функция в окружении лиспа определена, как $describe. В лиспе вызывать ее следует с помощью mfuncall (maxima function caller). Например, просмотр справки для функции ratsimp.

MAXIMA> (mfuncall '$describe "ratsimp")

 -- Function: ratsimp ()
 -- Function: ratsimp (, , ..., )
     Simplifies the expression  and all of its subexpressions,
     including the arguments to non-rational functions.  The result is
     returned as the quotient of two polynomials in a recursive form,
     that is, the coefficients of the main variable are polynomials in
     the other variables.  Variables may include non-rational functions
     (e.g., `sin (x^2 + 1)') and the arguments to any such functions
     are also rationally simplified.

     `ratsimp (, , ..., )' enables rational
     simplification with the specification of variable ordering as in
     `ratvars'.

     When `ratsimpexpons' is `true', `ratsimp' is applied to the
     exponents of expressions during simplification.

     See also `ratexpand'.  Note that `ratsimp' is affected by some of
     the flags which affect `ratexpand'.

     Examples:

          (%i1) sin (x/(x^2 + x)) = exp ((log(x) + 1)^2 - log(x)^2);
                                                   2      2
                             x         (log(x) + 1)  - log (x)
          (%o1)        sin(------) = %e
                            2
                           x  + x
          (%i2) ratsimp (%);
                                       1          2
          (%o2)                  sin(-----) = %e x
                                     x + 1
          (%i3) ((x - 1)^(3/2) - (x + 1)*sqrt(x - 1))/sqrt((x - 1)*(x + 1));
                                 3/2
                          (x - 1)    - sqrt(x - 1) (x + 1)
          (%o3)           --------------------------------
                               sqrt((x - 1) (x + 1))
          (%i4) ratsimp (%);
                                     2 sqrt(x - 1)
          (%o4)                    - -------------
                                           2
                                     sqrt(x  - 1)
          (%i5) x^(a + 1/a), ratsimpexpons: true;
                                         2
                                        a  + 1
                                        ------
                                          a
          (%o5)                        x


  There are also some inexact matches for `ratsimp'.
  Try `?? ratsimp' to see them.

Выполнить строку

(pushnew "/path/to/embeddable-maxima" asdf:*central-registry*)
(ql:quickload :embeddable-maxima)
(in-package :maxima)

Делай ast с помощью специальной функции macsyma-read-string, раз:

Важно не забыть один из терминальных символов ";" или "$".

MAXIMA> (macsyma-read-string "ratsimp( (2*x^2 + 3*x + 1) - (x^2 + x^2 - 2*x + 4*x + 1) );")

(($RATSIMP)
 ((MPLUS) ((MPLUS) ((MTIMES) 2 ((MEXPT) $X 2)) ((MTIMES) 3 $X) 1)
  ((MMINUS)
   ((MPLUS) ((MEXPT) $X 2) ((MEXPT) $X 2) ((MTIMES) ((MMINUS) 2) $X)
    ((MTIMES) 4 $X) 1))))

Выполняй ast с помощью вызова максимы-функции ev(expression,arg_1,arg_2,...,arg_n), два:

MAXIMA> (mfuncall '$ev *)

$X

Отображай полученное ast в строчку, три:

MAXIMA> (displa *)

x
NIL

В данном примере мы упростили выражение ((2*x^2 + 3*x + 1) - (x^2 + x^2 - 2*x + 4*x + 1)) с помощью функции ratsimp.

Простой json-rpc для maxima

Осторожно, американский стиль повествования! Верите ли вы, что я сделаю это за 42 строчки? А вот да, сделаю. Конечно это лисп, и здесь в рамках одной строки вмещается раза в два больше информации, чем в алгольных языках. При этом я еще и поиспользую кучу библиотек, хотя считается, что под cl их нет. Ладно, если не считать пустых строк, то их всего 32. Ну что поверили? А вот я опять вас обманул. Действительно полезных строк 27. Итак json-rpc сервис для maxima, для того, чтобы вы из любого языка, который умеет http и json могли решить дифференциальное уравнение второго порядка. Только здесь и только сейчас.

Загружаем три библиотеки, 1-ая строка:

(mapcar #'ql:quickload '(:embeddable-maxima :restas :cl-json))

Обозначаем restas модуль для обработки http запросов, 4-ая строка:

(restas:define-module :maxima-json-rpc
  (:use #:cl #:restas #:json #:json-rpc))
(in-package :maxima-json-rpc)

Создаем функцию для проверки: заканчивается ли переданная максима команда с помощью терминальных символов $ или ;, 11-ая строка:

(defun ensure-valid-maxima-input (input)
  "Returns string with appended ';', if input string does not have maxima command terminator at the end."
  (let* ((input-trimmed (string-trim '(#\Space #\Newline #\Tab) input))
        (last-char (char input-trimmed (1- (length input-trimmed)))))
    (if (and (not (char= #\; last-char)) (not (char= #\$ last-char)))
        (concatenate 'string input-trimmed ";")
        input-trimmed)))

Создаем функцию, которая преобразовывает строковое выражение в АСТ для максимы, 13-ая строка:

(defun maxima-ast-from-string (input)
  (maxima::macsyma-read-string (ensure-valid-maxima-input input)))

Макрос, который заставляет максиму, выводить результат в "компьютерном" синтаксисе (дроби через /, степени - ^, и т.д.), 16-ая строка:

(defmacro with-2d-output (&body body)
  `(let ((maxima::$display2d nil))
     ,@body))

Экспортируемая функция доступная из удаленных источников. Создается с помощью json-rpc:defun-json-rpc, 24-ая строка:

(defun-json-rpc evaluate :explicit (text)
  "Evaluate maxima expression"
  (let ((result (make-array '(0) :element-type 'base-char :fill-pointer 0 :adjustable t)))
    (with-output-to-string (*standard-output* result)
      (with-2d-output
        (maxima::displa
         (maxima::mfuncall 'maxima::$ev (maxima-ast-from-string text)))))
    result))

Обработчик http маршрута, например, http://127.0.0.01/jsonrpc. 31-ая строка:

(define-route jsonrpc ("jsonrpc"
                            :method :post 
                            ;;:content-type "application/json"
                            )
  "json rpc route"
  (let ((*json-rpc-version* +json-rpc-2.0+))
    (invoke-rpc (hunchentoot:raw-post-data :force-text t))))

Ну а здесь мы предоставляет простую страничку, которая умеет с помощью jquery, ajax, json-rpc решать те самые диффуры:

(define-route example ("example")
   (merge-pathnames "examples/js-maxima-rpc-client.html" (asdf:component-pathname (asdf:find-system :maxima-json-rpc))))

Последняя строка для запуска:

(restas:start '#:maxima-json-rpc :port 8080)

Да, это все оформлено и в репозитарии лежит. https://github.com/filonenko-mikhail/maxima-json-rpc

Но это еще не всё. В дополнение вы получаете простой пример на php для того, чтобы моментально находить ответы на непростые математические вопросы. Внимание, данный пример требует наличия JSON-RPC PHP!

<?php
  require_once 'jsonRPCClient.php';
  $maxima = new jsonRPCClient('http://127.0.0.1:8080/jsonrpc');
  print "Maxima evaluator\n";
  print "Evaluate ratsimp(x^2 + 2*x + 1 - (x + 1)^2)\n";
  print $maxima->evaluate("ratsimp(x^2 + 2*x + 1 - (x + 1)^2)");

  print "Evaluate x^2 + 2*x + 1 + (x + 1)^2 in environment x=2\n";
  print $maxima->evaluate("ev(x^2 + 2*x + 1 + (x + 1)^2, x=2)");
?>

И напоследок, sh и решение диффуров. Диффуры такие:

(%i1) 'diff(f,x,2) = sin(x) + 'diff(g,x);

                                2
                               d f            dg
(%o1)                          --- = sin(x) + --
                                 2            dx
                               dx
(%i2) 'diff(f,x) + x^2 - f = 2*'diff(g,x,2);

                                               2
                               2   df         d g
(%o2)                         x  + -- - f = 2 ---
                                   dx           2
                                              dx

sh файлик такой:

#!/bin/sh
curl -i -X POST -d "{\"jsonrpc\": \"2.0\", \"method\": \"evaluate\", \"params\": [\"desolve(['diff(f(x),x,2) = 'diff(g(x),x,1)+sin(x), 'diff(f(x),x,1)-f(x)+x^2 = 2*'diff(g(x),x,2)], [f(x),g(x)]);\"], \"id\": 1}" http://linkfly.ru:8181/jsonrpc

А теперь, кто готов все повторить, но уже на пайтоне?

P.S. А Firemax, что расширение для Firefox, очень даже неплохо справляется с задачей эмуляции emacs под броузером.

Комментариев нет:

Отправить комментарий