Предложения по синтаксису для краткого выражения математической формулы - PullRequest
30 голосов
/ 24 мая 2010

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

Я разместил прототип в комментариях, его длина составляет около двухсот строк.

Прямо сейчас мой язык выглядит примерно так (ну, на самом деле будет выглядеть так):

// implies two nested loops j=0:N, i=0,j
(range(i) < j < N)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)];

// implies summation over above expression
sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];

Я ищу возможные улучшения / расширения синтаксиса или просто разные идеи о выражении математических формул в видеясно и точно, насколько это возможно (на любом языке, не только C ++).

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

Спасибо.

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

alt textalt text

выражение кратко, где значения в <> уже вычислены как 4-мерные массивы.

Ответы [ 13 ]

6 голосов
/ 24 мая 2010

Если вы собираетесь писать это для мира ab-initio (который я предполагаю из вашего уравнения MP2), вы хотите, чтобы было очень легко и ясно выразить вещи как можно ближе к математическому определению, которое вы можете.

Во-первых, у меня не было бы сложной функции range.Пусть он определяет цикл, но если вы хотите использовать вложенные циклы, укажите их оба:

Поэтому вместо

(range(i) < j < N)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)];

используйте

loop(j,0,N)[loop(i,0,j)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)]]

И для таких вещей, как сумма и произведение, сделайте синтаксис «наследуемым» от факта, что это цикл.

Так что вместо

sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];

используйте

sum(j,0,n)[loop(i,0,j)[(T(i,j) - T(j,i))/e(i+j)]]

или если вам нужна двойная сумма

sum(j,0,n)[sum(i,0,j)[(T(i,j) - T(j,i))/e(i+j)]]

Поскольку, похоже, вы пытаетесьпредставьте квантово-механические операторы, затем постарайтесь, чтобы ваши языковые конструкции соответствовали оператору на основе 1-1, насколько это возможно.Таким образом, его легко перевести (и понять, что переводится).

EDITED TO ADD

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

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

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

sum (i, 0, n) sum (j, 0, i) сумма (a, -j, j) сумма (b, -i, i) ....

Поскольку каждый оператор работает вправо, его переменные известны, поэтому j может знать о i, a может знать о i и j, а b может знать о i, j и a.

Из моего опыта работы с квантовыми химиками (я тоже один!) Им не нравится сложный синтаксис, который сильно отличается от того, что они пишут.Они с радостью разделяют двойные и тройные суммы и интегралы в набор синглов, потому что в любом случае это просто условные обозначения.

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

c2v (sigma_x, a, b) a + b

Thisговорит, что a и b могут рассматриваться как идентичные частицы при операции c2v.Это означает, что любое уравнение с a и b (например, a + b после него) должно быть преобразовано в линейную комбинацию преобразований c2v.sigma_x - это операция в c2v, которую вы хотите применить к своей функции (a + b).Если я правильно помню, это 1 / sqrt (2) ((a + b) + (b + a)).Но у меня здесь нет моей книги симметрии, так что это может быть неправильно.

3 голосов
/ 03 июня 2010

Если вы ищете простоту, вы должны пойти дальше. Например, что-то вроде этого

T( i < j , j < N ) = ( T(i,j) - T(j,i) )/e(i+j)

будет работать, если вы переписываете оператор присваивания =, чтобы вести себя нормально для чего-то вроде a(i) = b(i) + c(i), но вести себя как суммирование для a(i<5) = b(i) + c(i). Предположим, что суммирование начинается с 0, если не указан нижний предел, например, a(3<i<5), проверьте символические верхние / нижние пределы, которые отображаются в виде индексов суммирования, и при необходимости сделайте двойные суммы. Если вы хотите, чтобы синтаксис вызывал явность, вы можете определить отдельный оператор сумм s=

T( i < j , j < N ) s= ( T(i,j) - T(j,i) )/e(i+j)

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

E_MP2 s= EV( i < n1 , j < n2 , a < n3 , b < n4 ) *
         2 ( EV(a,b,i,j) - EV(a,b,j,i) ) / ( e(i)+e(j)-e(a)-e(b) )

, где вы явно заявляете, что это сумма (используя s=), заставляя этот оператор затем брать индексы суммирования и ограничивающие значения из первого экземпляра, где появляется индекс. В частности, вы могли бы также использовать синтаксис вроде (предполагая, что теперь a, b фиксированы и i, j согласно вашему примеру)

E_MP2 s=(i<j,j<N) EV(i,j,a,b) *
                  2 ( EV(a,b,i,j) - EV(a,b,j,i) ) / ( e(i)+e(j)-e(a)-e(b) )

, что довольно ясно обозначено.

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

F i=(0<x<Pi) x^-1 * exp(-I x^2)

аналогично суммированию вы можете указать предел при первом появлении x

F i= x[0,Pi]^-1 * exp(-I x^2)

, где квадратные скобки служат для отличия записи от суммы, чтобы вам не приходилось использовать i= или s= и может одновременно использовать суммирование и интеграцию:

F(i) = G(i,j<10) * x[0,inf]^-1 * H(i,j,x)
3 голосов
/ 24 мая 2010

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

sum(range(j) < N)[sum(range(i) < j)[(T(i,j) - T(j,i))/e(i+j)]]

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

j = range_iter(0,N)

Это откроет для range x(0,N); j = range.begin(); или альтернативы, которые я сейчас не могу придумать.

Вы могли бы даже:

j = range_iter(inc(0) => exc(N)); для j повторяется по [0, N).

Во всяком случае, интересная идея. Можно ли составить ваши результирующие функции? Вы можете запросить информацию о домене?

2 голосов
/ 24 мая 2010

Я не знаком с Фениксом и могу только предполагать его возможности.

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

[ i + j | i <- [1..10], j <- [1..10] ]

будет выглядеть примерно так:

[ i + j | i = range(1,10), j = range(1,10) ]

К сожалению, я действительно не знаю, возможно ли что-то подобное с Фениксом.

1 голос
/ 04 июня 2010

Я бы неплохо прочитал блог Project Fortress , в котором есть несколько вдохновляющих постов, посвященных кратким математическим обозначениям языка программирования.

1 голос
/ 27 мая 2010

Я не уверен, насколько сложными будут эти формулы, но если дойти до того момента, когда ваш API будет больше похож на этот математический домен, чем на стандартный C ++ (который использует перегрузку операторов и метапрограммирование шаблонов выполняется довольно легко), я думаю, вы следует рассмотреть возможность разработки предметно-ориентированного языка (DSL). Когда вы пытаетесь сделать это на языке (как в вашем случае), он называется внутренним DSL и, хотя он имеет некоторые преимущества, он имеет много недостатков. Вы должны лучше знать свои требования, однако я хочу предложить вам взглянуть на инструменты для внешних DSL, которые являются небольшими внешними языками, специализированными для определенного домена. Посмотрите на Jetbrains MPS и Eclipse Xtext, это два инструмента с открытым исходным кодом, которые можно использовать для быстрой разработки внешних DSL.

1 голос
/ 24 мая 2010

Мне не нравится, как вы указываете этот "треугольный" 2-мерный диапазон. Я хотел бы видеть что-то вроде:

(i,j) in range(0..j,0..N)

например, что может привести к:

X = sum(f(i,j) for (i,j) in range(0..j,0..N));

AFAIK Это не существующий язык, но так как вы создаете свой собственный синтаксис ... Я сомневаюсь в возможности использовать j в выражении диапазона для i, но вы нашли способ в своем :-)

1 голос
/ 24 мая 2010
T = T - T'/e;

или, если вы используете не все T

T(0:i,0:j) = T(0:i,0:j) - T(0:i,0:j)'/e(0:i,0:j)
0 голосов
/ 04 июня 2010

Вы должны взглянуть на синтаксис, используемый для понимания списка Haskell .

0 голосов
/ 04 июня 2010

Инкапсуляция!

Я уверен, что вы найдете некоторый синтаксис, который уравновешивает краткость и ясность. Как бы хорошо это ни было, оно будет значительно улучшено за счет инкапсуляции. Так что вместо

qty = sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];

Вы могли бы иметь

Texpr = (T(i,j) - T(j,i))/e(i+j);
RangeExpr = (range(i) < j < N);
qty = sum(RangeExpr)[ Texpr ];

Это может дать вам больше многословия в синтаксисе.

PS: Разве это не Mathematica? Языковой интерфейс Mathematica C / C ++

...