If you're going to be writing this for the ab-initio world (which I'm guessing from your MP2 equation) you want to make it very easy and clear to express things as close to the mathematical definition that you can For one, I wouldn't have the complicated range function. Have it define a loop, but if you want nested loops, specify them both: So instead of (range(i) You define operators that always work on what's to the right of them and then the only other thing you need are parenthesis to group where an operator stops Einstein notation is fun where you don't specify the indices or bounds and they're implied because of convention, however that doesn't make clear code and it's harder to think about For sums, even if the bounds implied, they're always easy to figure out based on the context, so you should always make people specify them sum(i,0,n)sum(j,0,i)sum(a,-j,j)sum(b,-i,i) Since each operator works to the right, its variables are known, so j can know about i, a can know about I and j and be can know about i,j, and a From my experience with quantum chemists (I am one too! ) they don't like complicated syntax that differs much from what they write.
They are happy to separate double and triple sums and integrals into a collection of singles because those are just shorthand anyway Symmetry isn't going to be that hard either. It's just a collection of swaps and adds or multiplies. I'd do something where you specify the operation which contains a list of the elements that are the same and can be swapped: c2v(sigma_x,a,b)a+b This says that a and be are can be considered identical particles under a c2v operation.
That means that any equation with a and be (such as the a+b after it) should be transformed into a linear combination of the c2v transformations. The sigma_x is the operation in c2v that you want applied to your function, (a+b). If I remember correctly, that's 1/sqrt(2)((a+b)+(b+a)).
But I don't have my symmetry book here, so that could be wrong.
If you're going to be writing this for the ab-initio world (which I'm guessing from your MP2 equation) you want to make it very easy and clear to express things as close to the mathematical definition that you can. For one, I wouldn't have the complicated range function. Have it define a loop, but if you want nested loops, specify them both: So instead of (range(i) So instead of sum(range(i) You define operators that always work on what's to the right of them and then the only other thing you need are parenthesis to group where an operator stops.
Einstein notation is fun where you don't specify the indices or bounds and they're implied because of convention, however that doesn't make clear code and it's harder to think about. For sums, even if the bounds implied, they're always easy to figure out based on the context, so you should always make people specify them. Sum(i,0,n)sum(j,0,i)sum(a,-j,j)sum(b,-i,i).... Since each operator works to the right, its variables are known, so j can know about i, a can know about I and j and be can know about i,j, and a.
From my experience with quantum chemists (I am one too! ) they don't like complicated syntax that differs much from what they write. They are happy to separate double and triple sums and integrals into a collection of singles because those are just shorthand anyway.
Symmetry isn't going to be that hard either. It's just a collection of swaps and adds or multiplies. I'd do something where you specify the operation which contains a list of the elements that are the same and can be swapped: c2v(sigma_x,a,b)a+b This says that a and be are can be considered identical particles under a c2v operation.
That means that any equation with a and be (such as the a+b after it) should be transformed into a linear combination of the c2v transformations. The sigma_x is the operation in c2v that you want applied to your function, (a+b). If I remember correctly, that's 1/sqrt(2)((a+b)+(b+a)).
But I don't have my symmetry book here, so that could be wrong.
1, clarity is much more important than "smart". – Matthieu M. May 25 '10 at 6:56 yes, quantum chemistry.
I was actually aiming syntax to reproduce how formulas are written in shorthand notation with implied limits. Operators are my next project, main difficulty with them would be to introduce some sort of symmetry mechanism – Anycorn May 30 '10 at 19:45 see my edits to my answer – miked Jun 3 '10 at 20:25.
I would prefer a more solid separation between loops. For example, I'd prefer this alternative notation to your second example: sum(range(j) exc(N)); for j iterates over 0,N). Anyway, interesting idea.
Can your resulting functions be composed? Can you request domain information?
A function is defined as a mapping from one set, a domain, to another set, the range such that for any element in the domain there's one single element in the range that maps to it (not necessarily the reverse). – Crazy Eddie May 24 '10 at 18:15 hello. Sorry for late response.
I posted rough prototype as answer, maybe it will find it interesting. As far as domain, I think it might possible, but that have not been implemented. Composition sorts of works, but not the way I want to eventually make it look like – Anycorn May 30 '10 at 19:09.
If you're looking for simplicity you should take the implicitness of the loops even further. E.g. , something like this T( I Assume summation starts from 0 unless the lower limit is specified, e.g. A(3As for your short term goal, using the notion of specifying the summation index at the same time that the index first appears you could write.
E_MP2 s= EV( I You could then go on and take this concept even further by, e.g. , defining an integration operator i= that does the same thing.I.e.It looks for instances of variables that are marked down with limits and then proceeds to integrate the expression with respect to those variables F i=(0.
The last formula is just beautiful. – Matthieu M. Jun 4 '10 at 6:18 Thanks!
The more I think about this notation the more I like it. Hmm, I've been looking for a project to teach myself python... – Timo Jun 4 '10 at 6:48 The only thing missing is derivation and you could use d= and curly braces like this: F = Log(x{x0}/A). – Timo Jun 5 '10 at 20:25.
I'm not familiar with Phoenix and can only make assumptions about it's capabilities. I've always liked the way Haskell allows me to express ranges as a list comprehension. It translates nicely from the actual mathematical notation into the programming language construct.
I + j | I.
T = T - T'/e; or, if you're not operating on all of T T(0:i,0:j) = T(0:i,0:j) - T(0:i,0:j)'/e(0:i,0:j).
Unfortunately I am having really hard time figuring out how to implement : operator in C++. How about summation type formulas, any particular ideas? Just slight clarification ( it is there on purpose) denominator is e(i+j) – Anycorn May 24 '10 at 17:33 @aaa: You simply can't implement that, Mark probably overlooked the embedded part of DSEL.
– Georg Fritzsche May 24 '10 at 17:40 @gf oh no, this is fine. I am looking at And interested in all syntax and styles and possibilities to expand my mind.By the way, I have looked at ROSE as a possible crutch to implement range operator. Do you have any feelings about that?
– Anycorn May 24 '10 at 17:47 @aaa: Ah, now I get you - my misunderstanding :) – Georg Fritzsche May 24 '10 at 18:40 @Georg: Mark didn't overlook the embedded part, but has, perhaps, unrealistic expectations of the power of embedded devices. OP asked for suggestions on syntax, that's what I provided (well, I tried). – gh Performance Mark May 24 '10 at 20:25.
I don't like the way you specify that "triangular" 2d range. I'd like to see something like: (i,j) in range(0..j,0..N) for example, which could then lead to: X = sum(f(i,j) for (i,j) in range(0..j,0..N)); AFAIK This is not an existing language, but since you're creating your own syntax... I'm doubtful about the ability to use j in the range expression for i, but you found a way in yours :-).
I posted rough prototype was some examples, slightly modified. If you would like to, can you comment on changes? – Anycorn May 30 '10 at 19:11 this is very similar to what could be written in python using list comprehension.
X = sum(f(i,j) for i, j in zip(range(0,j),range(0,N))) (and defining a 2D range function to avoid zip is very easy). – kriss Jun 4 '10 at 10:11 @kriss: yes, I was aiming for Python syntax am still new to it so I unknowingly added the parens around i,j. Also, his need is for a 2D range where 0I now think you're right that it can probably be done very cleanly in python.
– phkahler Jun 4 '10 at 14:12 oups, you're right my zip is bogus... – kriss Jun 4 '10 at 17:02.
I am not sure how complicated those formulas will be, but if get to the point when your API looks more like that math domain than standard C++ (which is using operator overloading and template metaprogramming done quite easily), I would guess you should consider developing a Domain Specific Language (DSL). When you are trying to do it in a language (like in your case) it is called internal DSL and although it has some advantages, it has many disadvantages. You should know your requirements best, however I want to suggest you looking at tools for external DSLs, which are small external languages specialized for certain domain.
Look at Jetbrains MPS and Eclipse Xtext, those are two open source tools, which can be used for rapid external DSL development.
I would give a good read to the Project Fortress blog, it has some inspiring posts on mathematical concise notations for a programming language.
I would consider expressing math formulas as they are done in LaTeX. After all, LaTeX is already well-defined and well-documented in this area.
I wouldn't be so sure, after all, LaTeX formulae are meant for display, not for structuring the maths... – fortran Jun 4 '10 at 9:56.
My prototype (still needs lots of work obviously, and comments) // #include "tensor/tensor. Hpp" // #include "typename. Hpp" #include #include #include #define BOOST_FUSION_INVOKE_FUNCTION_OBJECT_MAX_ARITY PHOENIX_ARG_LIMIT #include #include #include #include #include #include #include #include namespace range_ { namespace detail { namespace phoenix = boost::phoenix; namespace fusion = boost::fusion; /// undefined or implicit object struct undefined {}; template struct index { // index(boost::phoenix::argument) {} typedef phoenix::actor argument; argument arg() const { return argument(); } }; template struct functional { typedef phoenix::actor > type; static type form(const T& t) { return type(t); } }; template struct functional, U> { typedef phoenix::actor type; static type form(const T& t) { return type(t); } }; template struct functional { typedef typename functional::type type; static type form(const undefined&) { return type(T()); } }; template struct expression; template struct expression { typedef functional lower_function; typedef functional upper_function; expression(const L &lower, const U& upper, const C &cdr) : lower_(lower), upper_(upper), cdr_(cdr) {} template expression operator()(const E &c) const { return expression(lower_, upper_, c); } template void operator(const F &f) const { #define TEXT(z, n, text) text #define UNDEFINED_ARGUMENTS BOOST_PP_ENUM(PHOENIX_ARG_LIMIT, TEXT, undefined()) evaluate(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 functional::type begin() const { return functional::form(lower_); } template typename functional::type end() const { return functional::form(upper_); } template void evaluate(const F &f, const A &arguments) const { T I = this->begin()(arguments); #define AS_VECTOR(var, expr) BOOST_AUTO(var, fusion::as_vector(expr)) #define ADVANCE_C(seq) fusion::advance_c(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(), a)) { this->apply(cdr_, f, a); ++i; } } template void apply(const E &e, const F &f, const V &variables) const { e.
Template evaluate(f, variables); } template void apply(const undefined&, const F &f, const V &variables) const { fusion::invoke_function_object(f, fusion::as_vector(variables)); } }; template expression make_expression(const L &lower, const U& upper) { return expression(lower, upper, undefined()); } template expression make_expression(const expression &expr, const U& right) { return expression(expr.left(), right, undefined()); } template expression > operator,(const expression &e1, const expression &e2) { return e2(e1); } #define ARGUMENT(N) phoenix::actor #define ACTOR_COMPOSITE(O,L,R) \ phoenix::actor::type> #define LOWER_BOUND_OPERATOR(op, eval, param) \ template \ expression \ operator op (const param& left, const index &i) { \ return make_expression(left, undefined()); \ } #define UPPER_BOUND_OPERATOR_INDEX(op, eval, param) \ template \ expression \ operator op (const index &i, const param& e) { \ return make_expression(undefined(), \ (ARGUMENT(N)() op e)); \ } #define UPPER_BOUND_OPERATOR_EXPRESSION(op, eval) \ template \ expression \ operator op (const expression &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) \ UPPER_BOUND_OPERATOR_EXPRESSION(op, eval) LOWER_BOUND_OPERATOR( const i; boost::phoenix::actor > const j; boost::phoenix::actor > const k; boost::phoenix::actor > const l; template range_::detail::index range(const boost::phoenix::actor&) { return range_::detail::index(); } template range_::detail::index range(const boost::phoenix::actor&, const F &f) { // return range_::detail::index(); 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) T(N,N,N,N); // tensor::function > T_(T); // (range(j).
You may want to look at APLish languages for inspiration. By working with the matrix/array as a whole, you can get the brevity down quite a bit. I'm going to use Q below, and unless I misread your post, your second statement can be written as: sum raze (T-flip T) div e To understand T minus flip T, you need to look at an example.
Imagine we have T set to the following matrix: 0 1 2 3 4 5 6 7 8 flip T swaps the top two dimensions, which results in: 0 3 6 1 4 7 2 5 8 So T-flip T is basically T(i,j)-T(j,i) but a lot less typing, and thus a lot less error-prone. The raze reduces an array to a single dimension, so razing this: 0 3 6 1 4 7 2 5 8 turns it into this: 0 3 6 1 4 7 2 5 8 Finally, the sum is sum-over; it basically adds up all of the members of its list, so sum 0 3 6 1 4 7 2 5 8 means: 0+3+6+1+4+7+2+5+8 Implementing this kind of thing in C++ isn't that difficult; you could: sum(raze((T-flip(T))/e)) with the right level of operator overloading. By the way, K is even more brief: +/,/(T-+:T)%e.
Encapsulate! I'm sure you'll find some syntax that balances concision and clarity. However good it is, it will be vastly improved by providing encapsulation.So instead of qty = sum(range(i).
You should have a look at syntax used for Haskell's list comprehension.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.