Подтвердить что ты не робот

Предложения по синтаксису для краткости выражения математической формулы

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

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

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

// 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)];

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

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

Спасибо.

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

alt textalt text

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

4b9b3361

Ответ 1

Если вы собираетесь писать это для мира 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 и product, сделайте синтаксис "наследовать" из того, что это цикл.

Итак, вместо

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 основе как можно ближе. Таким образом, легко перевести (и понять, что переводят).

ИЗМЕНИТЬ ДОБАВИТЬ

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

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

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

сумма (я, 0, п) сумма (J, 0, я) сумма (а, -j, J) сумма (Ь, -i, я)....

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

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

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

c2v (sigma_x, а, б) а + б

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

Ответ 2

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

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).

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

Ответ 3

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

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 fixed и 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)

Ответ 4

Я не знаком с Phoenix и могу только делать предположения об этом возможностях.

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

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

закончится как-то вроде:

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

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

Ответ 5

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)

Ответ 6

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

(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, но вы нашли способ в своем:-)

Ответ 7

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

Ответ 8

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

Ответ 10

мой прототип (по-прежнему требует много работы и комментариев)

// #include "tensor/tensor.hpp"
// #include "typename.hpp"

#include <boost/spirit/home/phoenix/core/argument.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#define BOOST_FUSION_INVOKE_FUNCTION_OBJECT_MAX_ARITY PHOENIX_ARG_LIMIT
#include <boost/fusion/functional/invocation/limits.hpp>
#include <boost/fusion/functional.hpp>

#include <boost/fusion/include/intrinsic.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/vector_tie.hpp>
#include <boost/fusion/include/make_vector.hpp>

#include <boost/typeof/typeof.hpp>

namespace range_ {
    namespace detail {

        namespace phoenix = boost::phoenix;
        namespace fusion = boost::fusion;

        /// undefined or implicit object
        struct undefined {};

        template<int N>
        struct index {
            // index(boost::phoenix::argument<N>) {}
            typedef phoenix::actor<phoenix::argument<N> > argument;
            argument arg() const { return argument(); }
        };

        template<typename T, typename U = void>
        struct functional {
            typedef phoenix::actor<phoenix::value<T> > type;
            static type form(const T& t) { return type(t); }
        };

        template<typename T, typename U>
        struct functional<phoenix::actor<T>, U> {
            typedef phoenix::actor<T> type;
            static type form(const T& t) { return type(t); }
        };

        template<typename T>
        struct functional<undefined,T> {
            typedef typename functional<T>::type type;
            static type form(const undefined&) { return type(T()); }
        };

        template<int N, class L, class U, class T = undefined>
        struct expression;

        template<int N, class L, class U, class C>
        struct expression {
            typedef functional<L,U> lower_function;
            typedef functional<U,L> upper_function;

            expression(const L &lower, const U& upper, const C &cdr)
                : lower_(lower), upper_(upper), cdr_(cdr) {}

            template<class E>
            expression<N, L, U, E> operator()(const E &c) const {
                return expression<N, L, U, E>(lower_,  upper_, c);
            }

            template<class F>
            void operator[](const F &f) const {
#define TEXT(z, n, text) text
#define UNDEFINED_ARGUMENTS BOOST_PP_ENUM(PHOENIX_ARG_LIMIT, TEXT, undefined())
                evaluate<int>(f, fusion::make_vector(UNDEFINED_ARGUMENTS));
#undef TEXT
#undef UNDEFINED_ARGUMENTS
            }

            L lower_;
            U upper_;
            C cdr_;

            const L& left() const { return lower_; }

            const C& cdr() const {return cdr_; }

            template<typename T>
            typename functional<L,T>::type begin() const {
                return functional<L,T>::form(lower_);
            }

            template<typename T>
            typename functional<U,T>::type end() const {
                return functional<U,T>::form(upper_);
            }

            template<typename T, class F, class A>
            void evaluate(const F &f, const A &arguments) const {
                T i = this->begin<T>()(arguments);
#define AS_VECTOR(var, expr) BOOST_AUTO(var, fusion::as_vector(expr))
#define ADVANCE_C(seq) fusion::advance_c<N>(fusion::begin(seq))
                AS_VECTOR(b, fusion::erase(arguments, ADVANCE_C(arguments)));
                AS_VECTOR(a, fusion::insert_range(b, ADVANCE_C(b),
                                                  fusion::vector_tie(i)));
#undef ADVANCE_C
#undef AS_VECTOR
                while (fusion::invoke_function_object(this->end<T>(), a)) {
                    this->apply<T>(cdr_, f, a);
                    ++i;
                }
            }

            template<typename T, class E, class F, class V>
            void apply(const E &e, const F &f, const V &variables) const {
                e.template evaluate<T>(f, variables);
            }

            template<typename T, class F, class V>
            void apply(const undefined&, const F &f, const V &variables) const {
                fusion::invoke_function_object(f, fusion::as_vector(variables));
            }

        };

        template<int N, class  L, class U>
        expression<N, L, U>
        make_expression(const L &lower, const U& upper) {
            return expression<N, L, U>(lower, upper, undefined());
        }

        template<int N, class  L, class U>
        expression<N, L, U>
        make_expression(const expression<N, L, undefined> &expr, const U& right) {
            return expression<N, L, U>(expr.left(), right, undefined());
        }

        template<int N1, class L1, class U1, class T1,
                 int N2, class L2, class U2>
        expression<N2, L2, U2, expression<N1, L1, U1, T1> >
        operator,(const expression<N1, L1, U1, T1> &e1,
                  const expression<N2, L2, U2> &e2)  {
            return e2(e1);
        }

#define ARGUMENT(N) phoenix::actor<phoenix::argument<N> >
#define ACTOR_COMPOSITE(O,L,R)                                          \
        phoenix::actor<typename phoenix::as_composite<O, L, R>::type>


#define LOWER_BOUND_OPERATOR(op, eval, param)                           \
        template <typename T, int N>                                    \
        expression<N, param, undefined>                                 \
        operator op (const param& left, const index<N> &i) {            \
            return make_expression<N>(left, undefined());               \
        }


#define UPPER_BOUND_OPERATOR_INDEX(op, eval, param)             \
        template <typename T, int N>                            \
        expression<N, undefined,                                \
                   ACTOR_COMPOSITE(eval, ARGUMENT(N), param)>   \
        operator op (const index<N> &i, const param& e) {       \
            return make_expression<N>(undefined(),              \
                                      (ARGUMENT(N)() op e));    \
        }

#define UPPER_BOUND_OPERATOR_EXPRESSION(op, eval)                       \
        template <typename T, int N, class E>                           \
        expression<N, E, ACTOR_COMPOSITE(eval, ARGUMENT(N), T)>         \
        operator op (const expression<N, E, undefined> &left,           \
                     const T& right) {                                  \
            return make_expression(left, (ARGUMENT(N)() op right));     \
        }

#define UPPER_BOUND_OPERATOR(op, eval)                                  \
        UPPER_BOUND_OPERATOR_INDEX(op, eval, T)                         \
        UPPER_BOUND_OPERATOR_INDEX(op, eval, phoenix::actor<T>)         \
        UPPER_BOUND_OPERATOR_EXPRESSION(op, eval)

        LOWER_BOUND_OPERATOR( < , phoenix::less_eval, T)
        LOWER_BOUND_OPERATOR( < , phoenix::less_eval, phoenix::actor<T>)
        LOWER_BOUND_OPERATOR( <= , phoenix::less_equal_eval, T)
        LOWER_BOUND_OPERATOR( <= , phoenix::less_equal_eval, phoenix::actor<T>)

        UPPER_BOUND_OPERATOR( < , phoenix::less_eval)
        UPPER_BOUND_OPERATOR( <= , phoenix::less_equal_eval)

    }

}


namespace index {
    using namespace boost::phoenix;
    boost::phoenix::actor<boost::phoenix::argument<0> > const i;
    boost::phoenix::actor<boost::phoenix::argument<1> > const j;
    boost::phoenix::actor<boost::phoenix::argument<2> > const k;
    boost::phoenix::actor<boost::phoenix::argument<3> > const l;

    template<int N>
    range_::detail::index<N> range(const boost::phoenix::actor<
                                   boost::phoenix::argument<N> >&) {
        return range_::detail::index<N>();
    }
    template<int N, class F>
    range_::detail::index<N> range(const boost::phoenix::actor<
                                   boost::phoenix::argument<N> >&,
                                   const F &f) {
        // return range_::detail::index<N>();
        throw std::exception("not implemented");
    }



}

int main(){

    using namespace index;

    // formula domain language rough prototype
    // stuff in brackets can be any valid phoenix lambda expression

    // physicist notation, lower bound may be implicit
    (range(i) <= j, 3 <= range(j) < 4)[std::cout << i << " " << j << std::endl];

    // implicit physicist notation, not done yet
    //(range(i) < range(j) < 4)[...]

    // programmer notation, same as above , but order is reversed
    (3 <= range(j) < 4)(range(i) <= j)[std::cout << i << " " << j << std::endl];

    // ignore, some early prototype for lambda tensor
    // size_t N = 4;
    // tensor::tensor<4> T(N,N,N,N);

     // tensor::function<tensor::tensor<4> > T_(T);
    // (range(j) < 4)(range(i) <= j)[std::cout << T_(i,j,0,0)];

    // (range(i) < j, range(j) < N)[T_(i,j,0,0) = T_(j,i,0,0)];
    // sum(j < val(N))[T_(i,j,0,0)];

}

Ответ 11

Вы можете захотеть взглянуть на языки APLish для вдохновения. Работая с матрицей/массивом в целом, вы можете немного сократить краткость. Я собираюсь использовать Q ниже, и если я неправильно прочитаю ваш пост, ваш второй оператор можно записать так:

sum raze (T-flip T) div e

Чтобы понять T минус flip T, вам нужно посмотреть пример. Предположим, что T задано следующей матрицей:

0 1 2
3 4 5
6 7 8

flip T заменяет верхние два измерения, что приводит к:

0 3 6
1 4 7
2 5 8

Итак, T-flip T в основном T(i,j)-T(j,i), но гораздо меньше печатается и, следовательно, намного меньше подвержено ошибкам.

raze уменьшает массив до единственного измерения, поэтому сбрасывая это:

0 3 6
1 4 7
2 5 8

превращает его в это:

0 3 6 1 4 7 2 5 8

Наконец, sum суммируется; он в основном объединяет всех членов своего списка, поэтому

sum 0 3 6 1 4 7 2 5 8

означает:

0+3+6+1+4+7+2+5+8

Реализация такого рода вещей на С++ не так уж трудна; вы могли:

sum(raze((T-flip(T))/e))

с правильным уровнем перегрузки оператора.

Кстати, K еще более краток:

+/,/(T-+:T)%e

Ответ 12

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

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

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 C/С++