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

Обратите внимание, что (.|\n)* может быть менее эффективным, чем (например) [\s\S]* (если регулярные выражения вашего языка поддерживают такие escape-последовательности), а не поиск того, как указать модификатор, который делает. также соответствуют новостям. Или вы можете пойти с альтернативами POSIXy, такими как [[:space:][:^space:]]*.

30
задан 10 revs, 3 users 65% 8 February 2017 в 14:25
поделиться

13 ответов

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

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

Итак, вместо

(range (i)

используйте

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

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

Итак, вместо

sum (range (i)

используйте

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

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

РЕДАКТИРОВАТЬ ДОБАВИТЬ

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

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

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

sum (i, 0, n) sum (j, 0, i) sum (a, -j, j) sum (b, -i, i) ....

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

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

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

c2v (sigma_x, a, b) a + b

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

6
ответ дан 28 November 2019 в 00:28
поделиться

Мне не нравится, как вы указываете этот "треугольный" 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
ответ дан 28 November 2019 в 00:28
поделиться
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)
1
ответ дан 28 November 2019 в 00:28
поделиться

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

sum (range (j)

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

j = range_iter (0, N)

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

Вы можете даже:

j = range_iter (inc (0) => exc (N)); for j выполняет итерацию по [0, N).

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

3
ответ дан 28 November 2019 в 00:28
поделиться

Я бы подумал, что выражает математические формулы так, как они делаются в LaTeX . В конце концов, LaTeX уже четко определен и хорошо задокументирован в этой области.

0
ответ дан 28 November 2019 в 00:28
поделиться

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

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

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

в итоге получится что-то вроде:

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

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

2
ответ дан 28 November 2019 в 00:28
поделиться

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

1
ответ дан 28 November 2019 в 00:28
поделиться

мой прототип (все еще требует много работы, очевидно, и комментариев)

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

}
0
ответ дан 28 November 2019 в 00:28
поделиться

Encapsulate!

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

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++

0
ответ дан 28 November 2019 в 00:28
поделиться

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

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 , проверьте символические верхние / нижние пределы, которые появляются как индексы суммирования, и при необходимости сделайте двойные суммы. Если вы хотите, чтобы синтаксис был явным, вы могли бы определить отдельный оператор суммы 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
ответ дан 28 November 2019 в 00:28
поделиться

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

sum raze (T-flip T) div e

Чтобы понять T минус переворот 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 0 3 6 1 4 7 2 5 8

означает:

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

Реализовать такого рода вещи в C ++ не так уж и сложно; вы можете:

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

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

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

+/,/(T-+:T)%e
0
ответ дан 28 November 2019 в 00:28
поделиться

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

1
ответ дан 28 November 2019 в 00:28
поделиться

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

0
ответ дан 28 November 2019 в 00:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: