Wolfram Language JavaScript Frontend
- суббота, 14 октября 2023 г. в 00:00:14
Wolfram Language JavaScript Frontend - это проект, цель которого в создании бесплатной альтернативы для Mathematica с открытым исходным кодом, но со своими особенностями и преимуществами, а если точнее то в реализации пользовательского интерфейса для ядра Wolfram Language (WL). Ядро входит в состав Математики либо распространяется в виде бесплатной утилиты командной строки - Wolfram Engine. То есть данное приложение это именно пользовательский интерфейс для WL, а не попытка полностью переписать язык. Ниже будет демонстрация возможностей, а так же отличия от Mathematica и других приложений. Вам это точно будет интересно, если вам нравится подход к программированию, который используют такие платформы как Mathematica, Jupyter, NTeract, JupyterLab, DeepNote, ObservableHQ, Google Collab и некоторые другие.
Внимание! В статье много изображений!
Главным системным требованием является наличие Wolfram Engine или Mathematica. Если ни одного из перечисленных приложений все еще не установлено на ваш компьютер по каким-то необъяснимым причинам, то вы всегда можете получить бесплатную версию Wolfram Engine следуя инструкциям на странице: https://www.wolfram.com/engine/. А еще можно установить ядро используя пакетный менеджер или скачав docker-образ вот так:
brew cask install wolfram-engine # macOS
winget install WolframEngine # Windows
docker pull wolframresearch/wolframengine # docker
После установки необходимо получить бесплатную лицензию и активировать свою копию ведя свои учетные данные в интерфейсе командной строки. Для этого достаточно выполнить команду:
wolframscript
После успешной активации переходим на страницу с релизами WLJS и загружаем установочный файл. Запускаем установщик - сначала приложение распакуется в директорию пользователя, а затем запустится. Примерно так же, как это происходит с другими приложениями на electron. Далее нужно немного подождать пока загрузятся все зависимости и откроется вот такое окно:
Оно интуитивно понятно и работает примерно так, как этого ожидает пользователь от большинства приложений рабочего стола.
File - позволяет создать, открыть, сохранить, экспортировать блокнот...
Edit - вырезать, вставить, удалить, выбрать...
Window - перезагружает окно и управляет размерами...
Evaluation - выполняет код ячеек, останавливает вычисление и управляет ядром...
Misc - настройки и информация...
Особенно стоит отметить пункт File > Share, который позволяет экспортировать блокнот в HTML-файл, где будет сохранена вся разметка и интерактивные элементы. Конечно же только те, которые не требуют подключения к ядру.
Чтобы создать блокнот с демонстрацией возможностей перейдем в меню File > New или нажмем сочетание клавиш CTRL + N
. После чего слева - в области навигации появится новый файл со случайным именем и расширением .wln - Wolfram Language Notebook.
В навигации по файлам и директориям тоже все очень просто и интуитивно понятно. Директории можно открывать. Файлы после нажатия левой кнопкой мыши открываются на главной панели, а после нажатия правой кнопкой мыши - появляется контекстное меню, где файл можно открыть в отдельном окне, переименовать, копировать, удалить, показать в файловом менеджере. Боковую панель навигатора сворачивается при помощи кнопки "<<". После перехода в любую директорию вернуться на уровень вверх можно нажав на верхнюю строчку в списке навигатора с именем "../". На скриншоте наш новый файл уже открыт на главной панели окна. В нем пока ничего нет, кроме одной пустой ячейке с небольшой подсказкой - как в редакторе статей Хабра.
Все блокноты состоят из списка ячеек и внутренних метаданных. Больше в них ничего нет. Ячейки бывают разных типов. В первую очередь они делятся на ячейки ввода (input) и ячейки вывода (output). Пользователь при помощи клавиатуры и мыши по умолчанию может создавать только ячейки ввода. Делается это либо при помощи клика мышью в область между ячейками документа, либо двойным нажатием клавиши ↓
в последней ячейке. Еще один способ создания новой ячейки - нажатие на всплывающую кнопочку +
при наведении на область слева от существующей ячейки. Для удаления сначала нужно удалить весь текст, а затем нажать клавишу BACKSPACE
.
В ячейках с выводом появляется результат выполнения вашего кода. Что там будет - зависит от того, что пользователь ввел. Это может быть график, текст, разметка и многое другое. Чтобы создать такую ячейку - достаточно нажать сочетание клавиш SHIFT + ENTER
в любой ячейке ввода (если там есть код конечно). После чего приложение прочитает код ячейки, отправит его интерпретатору, дождется ответа и ниже напечатает результат. Вот например какой результат будет при выполнении вот такого кода:
Graphics3D[Table[{RandomColor[],
Sphere[RandomReal[1, 3], 0.05 + RandomReal[]/10]}, {25}]]
Обратите внимание на другие всплывающие кнопки. В левом нижнем углу есть стрелочка вниз, которая позволяет скрыть input, а в правом верхнем углу есть еще 4 кнопки по порядку: удалить output, сделать ячейку инициализирующей, открепить результат от окна и выполнить код (альтернатива SHIFT + ENTER
). Если выполнить код повторно - то результат будет заменен на новый. В общем-то это и есть основная функциональность приложения - интерактивные ячейки с кодом, которые сразу же показывают пользователю результат. На этом мы и остановимся более подробно.
Это основной язык программирования, который здесь поддерживается. Ведь на нем написан весь бэкенд, а также изначально цель была именно в поиске бесплатного аналога Mathematica. Чтобы писать на этом языке нечего дополнительно делать не надо - просто выполнить код в ячейке. Например ниже демонстрация того, как можно решить уравнение:
Solve[x^2 + 9 * x - 1 == 0, x]
На анимации выше видно, что текстовый вывод печатается в форматированном виде. Корни уравнения содержат знак радикала, а рациональные числа отображаются в виде дробей. Здесь стоит отметить, что все это возможно благодаря использованию JavaScript библиотек CodeMirror и wljs-interpreter. Именно она позволяет отображать форматированный вывод так, что под этим выводом прячется совсем другой код без форматирования - т.е. позволяют создавать синтаксический сахар. Причем математические формулы - это лишь малая и самая очевидная часть этого синтаксического сахара.
Стоит отметить, что концепция отображения математических формул здесь наиболее похоже на то, как это же происходит в Mathematica, но отличается от других инструментов с "ноутбучным" интерфейсом таких как Jupyter, DeepNote, Observablehq, nteract и т.д. Все формулы представляют собой не просто красивую разметку, а являются редактируемыми выражениями на WL, которые можно изменять прямо в ячейках вывода.
С помощью такого же синтаксического сахара реализованы не только формулы, но и любые другие объекты и они тоже поддерживаются редактором:
Кроме поддержки Wolfram Language те же самые ячейки ввода работают и с другими языками программирования и языками разметки. Для того, чтобы указать приложению, что в ячейке находится что-то, что нужно интерпретировать другим образом - в начале ячейке нужно ввести точку, затем имя языка и на следующей строке вводить уже сам код или разметку. Например, для markdown это будет выглядеть вот так:
.md
# Demo notebook for Habr
## Markdown cells
_you_ *can* __hide__ **the** input cell using the ~pop-up~ `buttons`
on the [left side]()
## Supported languages
* Wolfram
* ect..
## Wolfram code example
```wolfram
Plot[Sin[x], {x, 1, 2}]
```
Далее нужно просто выполнить эту ячейку и в output появится форматированный текст:
Такой способ ввода можно представить так, будто бы блокнот - это редактор файлов и указывая в начале "расширение" мы создаем анонимный файл, текст которого начинается со следующей строки. Ниже мы еще раз вернемся к этому сравнению, а пока перейдем к другим поддерживаемым языкам.
Этот язык поддерживается по очевидным причинам - ведь приложение на самом деле является веб-страницей упакованной в electron. Как и в предыдущем случае достаточно ввести "расширение" в первой строке, а далее можно писать код на JS. Ниже пример рисования окружностей по клику мыши:
.js
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 400;
let x, y
const handleDrawCircle = (event) => {
x = event.pageX;
y = event.pageY;
drawCircle(x, y);
};
const drawCircle = (x, y) => {
context.beginPath();
context.arc(x, y, 50, 0, 2 * Math.PI);
context.strokeStyle = 'red';
context.stroke();
};
canvas.addEventListener('click', handleDrawCircle);
return canvas;
Вот как это выглядит в самом блокноте:
Важные особенности выполнения JavaScript внутри блокнота:
В конце ячейки нужно писать return
чтобы указать, что печатать в output
Весь код по сути оборачивается в function и имеет локальную область видимости только внутри ячейки. Т.е. любые объявленные функций и переменных видны только в одной ячейке
Чтобы создать глобальные функции и переменные используйте объект window
.
JS дает полный доступ ко всему документу. С помощью скриптов в ячейках можно изменить весь интерфейс до неузнаваемости. Но можно и полностью сломать первым же скриптом.
Если markdown разметка не позволяет сделать то, что вы хотите - в ячейках можно использовать HTML. Все работает точно так же при помощи указания в первой строке .html
. Вся разметка из ячейки ввода вставляет в ячейку вывода как фрагмент страницы:
.html
<button>click me!</button>
У HTML ячеек есть одно очень важное преимущество. Код, который встраивается в разметку выполняется к глобальной области видимости. Это может быть полезно для создания JavaScript функций доступных во всем документе. Но еще полезнее то, что из HTML разметки можно импортировать сторонние скрипты и стили, а так же изменять стили текущего документа. Вот так например я могу добавить закругленную рамку для всех ячеек с кодом:
.html
<style>
.cm-editor {
border-radius: 5px;
}
</style>
И самая важная особенность HTML-ячеек. Они по умолчанию поддерживают синтаксис WolramScript Pages (WSP). Более подробно про саму технологию WSP можно почитать на странице проекта, но если коротко - то это расширение синтаксиса XML специальным тегом <?wsp code[] ?>
, который вставляет в разметку выражение на WL - т.е. примерно так же, как это работает в PHP.
В WSP можно использовать почти любые выражения на языке Wolfram. Вставлять определенные внутри блокнота функции, но самое важно - можно разделять выражения тегами разметки и тогда текст смешивается с кодом прямо внутри страницы. Вот так например можно создать таблицы из нескольких случайных фигур:
randomFigure[] :=
Graphics[{{RandomColor[], RandomChoice[{Disk, Circle, Rectangle}][]}},
ImageSize -> 60
]
.html
<ul><?wsp Table[ ?><li><?wsp randomFigure[] ?></li><?wsp , {i, 1, 4}] ?></ul>
Еще одна технология, которая смешивает код на WL с разметкой страницы. Только теперь это похоже на на PHP, а на JSX. Т.е. все это работает наоборот - мы пишем WL-код и можем добавлять в него разметку. Более подробно об этой технологи можно почитать на странице документации, а чтобы создать ячейку в первой строке пишем .wlx
. Ниже пример графика с заголовком и все это в одной ячейке:
.wlx
Heading[Text_] := <h2 class="tracking-tight"><Text/></h2>;
Graphics1 = Plot[Sin[x], {x, -4, 4}, ImageSize -> 350];
<body>
<Heading>
Hello World!
</Heading>
<Graphics1 />
</body>
Обратите внимание на важные особенности:
Объявленные в документе функции на WL доступны внутри разметки WLX
Возвращать нужно результат заключенный в один общий тег
Все теги разметки HTML всегда начинаются со строчной буквы
Все теги, которые преобразуются в результат выполнения WL - с большой
Для вставки переменной используется <Name />
Для вставки функции:<Func>arg1 arg2</Func>
Вы можете легко импортировать скрипты и стили
Здесь все просто. Поддержка диаграмм на языке Mermaid работает на основе JavaScript библиотеки. Вот например схема, которая описывает backend приложения:
.mermaid
flowchart TD
A[Frontend]-- HTTP+WS ---B(marster kernel)
B---|WSTP+JTP|C[[sub kernel]]
C-->|WS|A
Еще один "язык" разметки, который поддерживается при помощи библиотеки RevalJS. В первой строке указываем тип ячейки .slide
или .slides
, а дальше можно использовать правила написания презентаций RevalJS. Самые простые презентации создаются при помощи .md разметки и использования HTML. Вот пример:
Graphics1 = Plot[Sin[x], {x, -2, 2}];
Graphics2 = Plot3D[Sin[x] + Cos[y], {x, -2, 2},
{y, -2, 2}];
.slide
# Slide 1
2D graphics
<Graphics1 />
---
# Slide 2
3D graphics
<Graphics2 />
Обратите внимание, что HTML в разметке слайдов по умолчанию поддерживает WLX, а это значит, что в процессе написания презентации можно создавать различные объекты: графики, картинки и диаграммы на языке Wolfram и вставлять их прямо в презентацию. После того как ячейка с результатом напечатается - ее нужно "открепить" чтобы перейти в режим показа слайдов:
Мы не смогли совладать с соблазном и не присоединиться к общей волне хайпа - поэтому добавили возможность общения с моделью GPT3.5 прямо из ячеек блокнота. Для этого моно просто ввести в первой строке .llm а на следующей строке любой текст:
.llm
how to sort array in Mathematica?
В ячейках-диалогах с нейросетью есть пара интересных особенностей:
Ответ нейросети печатается в виде набора пар ячеек: input + output
Если где-то текст плохой - вы легко можете отредактировать input и перевыполнить
Если нейросеть отвечает кодом в разметке .md - он превращается в input без output
Весь вывод печатается выше ячейки ввода, тем самым симулирую работу ассистента, который заполняет вместе с вами сверху вниз input-ячеки и выполняет в них код.
Когда первая ячейка ввода смещается в самый низ - это становится похоже на обычный диалог или интерфейс ChatGPT.
В самом начале обзора было сравнение с анонимными файлами внутри ячейки. Так вот ячейки могут не только создавать "анонимные файлы в блокноте", но и читать существующие файлы! Для этого в первой строке необходимо ввести имя файла и выполнить ячейку. Например, ниже мы экспортируем картинку, а затем напечатаем ее в следующе ячейке:
Export["bubbleChart.png",BubbleChart[RandomReal[1, {5, 7, 3}]]]
bubbleChart.png
А еще можно записывать текстовые файлы вот так:
file.txt
col1, col2, col3
1, 2, 3
4, 5, 6
Затем читать их
file.txt
И после этого они будут доступны для импорта функциями Mathematica:
Import["file.txt", "CSV", HeaderLines -> 1]
На этом мы закончим обзор возможностей бесплатного интерфейса для Wolfram Language. В статье удалось рассмотреть далеко не все возможности приложения, а только самые основные (даже не все поддерживаемые типы ячеек). Кроме создания самого интерфейса мы (разработчики) так же добавляем свои полезные функции и плагины. Результат такой функции уважаемые читатели могли видеть на КДПВ. На рисунке кроме логотипа и текста есть реальный скриншот блокнота с 3D изображением Спайки - многогранника-символа языка Wolfram. Его различные вариации уже более 35 лет являются символом Wolfram Mathematica и компании Wolfam Research. Так что же такого особенного в этом многограннике? Дело в том, что на скриншоте построен этот многогранник при помощи Graphics3D, но при этом с использованием трассировки лучей и собственного освещения (чего никогда до сих пор не было в WL без использования OpenCL или CUDA). Вот код, который позволяет построить 3D график где будет Спайки и трассировка лучшей:
Graphics3D[{{Red, N@PolyhedronData["Spikey", "Polygons"]}},
Boxed->False,"RTX"->True,"Lighting"->None,
ImageSize->{500, 400},"ViewProjection"->"Perspective",
"Lightmap"->"https://raw.githubusercontent.com/JerryI/Mathematica-ThreeJS-graphics-engine/master/assets/PureSky.hdr"
]
На этом точно все! Всем спасибо за внимание и увидимся в следующих статьях и обзорах! Наш проект находится в стадии активной разработки и постоянно обновляется. Вы можете следить за ним на GitHub и на странице документации. С вами были разработчики Wolfram Language JavaScript Frontend - Кирилл Васин (https://github.com/jerryi) и Кирилл Белов (https://github.com/kirilbellovtest)!