1 /** @file time_antipode.cpp
3 * This is a beautiful example that calculates the counterterm for the
4 * overall divergence of some special sorts of Feynman diagrams in a massless
5 * Yukawa theory. For this end it computes the antipode of the corresponding
6 * decorated rooted tree using dimensional regularization in the parameter
7 * x==-(D-4)/2, which leads to a Laurent series in x. The renormalization
8 * scheme used is the minimal subtraction scheme (MS). From an efficiency
9 * point of view it boils down to power series expansion. It also has quite
10 * an intriguing check for consistency, which is why we include it here.
12 * This program is based on work by
13 * Isabella Bierenbaum <bierenbaum@thep.physik.uni-mainz.de> and
14 * Dirk Kreimer <dkreimer@bu.edu>.
15 * For details, please see <http://www.arXiv.org/abs/hep-th/0111192>.
19 * GiNaC Copyright (C) 1999-2009 Johannes Gutenberg University Mainz, Germany
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
38 using namespace GiNaC;
48 // whether to run this beast or not:
49 static const bool do_test = true;
51 // regularization parameter:
52 static const symbol x("x");
54 typedef pair<unsigned, unsigned> ijpair;
55 typedef pair<class node, bool> child;
57 const constant TrOne("Tr[One]", numeric(4));
59 /* Extract only the divergent part of a series and discard the rest. */
60 static ex div_part(const ex &exarg, const symbol &x, unsigned grad)
62 const ex exser = exarg.series(x==0, grad);
63 if (exser.degree(x)<0)
64 throw runtime_error("divergent part truncation disaster");
66 for (int i=exser.ldegree(x); i<0; ++i)
67 exser_trunc += exser.coeff(x,i)*pow(x,i);
68 // NB: exser_trunc is by construction collected in x.
72 /* F_ab(a, i, b, j, "x") is a common pattern in all vertex evaluators. */
73 static ex F_ab(int a, int i, int b, int j, const symbol &x)
75 if ((i==0 && a<=0) || (j==0 && b<=0))
78 return (tgamma(2-a-(i+1)*x)*
80 tgamma(a+b-2+(1+i+j)*x)/
82 tgamma(b+j*x)/tgamma(4-a-b-(2+i+j)*x));
85 /* Abstract base class (ABC) for all types of vertices. */
88 vertex(ijpair ij = ijpair(0,0)) : indices(ij) { }
89 void increment_indices(const ijpair &ind) { indices.first += ind.first; indices.second += ind.second; }
91 virtual vertex* copy() const = 0;
92 virtual ijpair get_increment() const { return indices; }
93 virtual const ex evaluate(const symbol &x, const unsigned grad) const = 0;
94 bool operator==(const vertex &v) const { return (indices==v.indices); }
95 bool operator<(const vertex &v) const { return (indices<v.indices); }
102 * Class of vertices of type Sigma.
104 class Sigma : public vertex {
106 Sigma(ijpair ij = ijpair(0,0)) : vertex(ij) { }
107 vertex* copy() const { return new Sigma(*this); }
108 ijpair get_increment() const { return ijpair(indices.first+indices.second+1, 0); }
109 const ex evaluate(const symbol &x, const unsigned grad) const;
113 const ex Sigma::evaluate(const symbol &x, const unsigned grad) const
115 // c.f. comments in node::evaluate()
116 static map<Sigma,ex> catalog;
117 static unsigned prev_grad = 0;
118 if (grad>prev_grad) {
122 map<Sigma,ex>::iterator pos = catalog.find(*this);
123 if (pos==catalog.end()) {
124 int i = indices.first;
125 int j = indices.second;
126 const ex result = ((F_ab(0,i,1,j,x)+F_ab(1,i,1,j,x)-F_ab(1,i,0,j,x))/2).series(x==0, grad).expand();
127 pos = catalog.insert(map<Sigma,ex>::value_type(*this,result)).first;
135 /** Class of vertices of type Sigma_flipped, sitting in the upper fermionline of Vacuum; no consequences for Gamma. */
136 class Sigma_flipped : public Sigma {
138 Sigma_flipped(ijpair ij = ijpair(0,0)) : Sigma(ij) { }
139 vertex* copy() const { return new Sigma_flipped(*this); }
140 ijpair get_increment() const { return ijpair(0, indices.first+indices.second+1); }
141 const ex evaluate(const symbol &x, const unsigned grad) const { return Sigma::evaluate(x, grad); }
147 *Class of vertices of type Gamma.
149 class Gamma : public vertex {
151 Gamma(ijpair ij = ijpair(0,0)) : vertex(ij) { }
152 vertex* copy() const { return new Gamma(*this); }
153 ijpair get_increment() const { return ijpair(indices.first+indices.second+1, 0); }
154 const ex evaluate(const symbol &x, const unsigned grad) const;
158 const ex Gamma::evaluate(const symbol &x, const unsigned grad) const
160 // c.f. comments in node::evaluate()
161 static map<Gamma,ex> catalog;
162 static unsigned prev_grad = 0;
163 if (prev_grad<grad) {
167 map<Gamma,ex>::iterator pos = catalog.find(*this);
168 if (pos==catalog.end()) {
169 int i = indices.first;
170 int j = indices.second;
171 const ex result = (F_ab(1,i,1,j,x)).series(x==0,grad).expand();
172 pos = catalog.insert(map<Gamma,ex>::value_type(*this,result)).first;
181 * Class of vertices of type Vacuum.
183 class Vacuum : public vertex {
185 Vacuum(ijpair ij = ijpair(0,0)) : vertex(ij) { }
186 vertex* copy() const { return new Vacuum(*this); }
187 ijpair get_increment() const { return ijpair(0, indices.first+indices.second+1); }
188 const ex evaluate(const symbol &x, const unsigned grad) const;
192 const ex Vacuum::evaluate(const symbol &x, const unsigned grad) const
194 // c.f. comments in node::evaluate()
195 static map<Vacuum,ex> catalog;
196 static unsigned prev_grad = 0;
197 if (prev_grad<grad) {
201 map<Vacuum,ex>::iterator pos = catalog.find(*this);
202 if (pos==catalog.end()) {
203 int i = indices.first;
204 int j = indices.second;
205 const ex result = ((-TrOne*(F_ab(0,i,1,j,x)-F_ab(1,i,1,j,x)+F_ab(1,i,0,j,x)))/2).series(x==0,grad).expand();
206 pos = catalog.insert(map<Vacuum,ex>::value_type(*this,result)).first;
215 * Class of nodes (or trees or subtrees), including list of children.
219 node(const vertex &v) { vert = v.copy(); }
220 node(const node &n) { vert = (n.vert)->copy(); children = n.children; }
221 const node & operator=(const node &);
222 ~node() { delete vert; }
223 void add_child(const node &, bool = false);
224 const ex evaluate(const symbol &x, unsigned grad) const;
225 unsigned total_edges() const;
226 bool operator==(const node &) const;
227 bool operator<(const node &) const;
230 multiset<child> children;
233 const node & node::operator=(const node &n)
237 vert = (n.vert)->copy();
238 children = n.children;
243 void node::add_child(const node &childnode, bool cut)
245 children.insert(child(childnode, cut));
247 vert->increment_indices(childnode.vert->get_increment());
250 const ex node::evaluate(const symbol &x, unsigned grad) const
252 static map<node,ex> catalog; // lookup table for already evaluated subnodes
253 static unsigned prev_grad = 0; // grad argument that the catalog has been build for
254 if (grad>prev_grad) {
255 // We haven't computed far enough last time. Our catalog cannot cope with
256 // the demands for this value of grad so let's clear it.
260 ex product = 1; // accumulator for all the children
261 for (multiset<child>::const_iterator i=children.begin(); i!=children.end(); ++i) {
262 map<node,ex>::iterator pos = catalog.find(i->first);
263 if (pos==catalog.end()) {
264 pos = catalog.insert(map<node,ex>::value_type(i->first,i->first.evaluate(x,grad).series(x==0,grad).expand())).first;
265 if (grad<prev_grad) {
266 // We have just spoiled the catalog by inserting a series computed
267 // to lower grad than the others in it. So let's make sure next time
268 // we don't use one of the newly inserted ones by bumping prev_grad
269 // down to the current value of grad.
274 product *= pos->second;
276 product *= -div_part(pos->second,x,grad);
278 return (product * vert->evaluate(x,grad));
281 unsigned node::total_edges() const
284 for (multiset<child>::const_iterator i=children.begin(); i!=children.end(); ++i) {
285 accu += i->first.total_edges();
291 bool node::operator==(const node &n) const
293 // Are the types of the top-level node vertices equal?
294 if (typeid(*vert)!=typeid(*n.vert))
296 // Are the indices of the top-level nodes equal?
297 if (!(*vert==*n.vert))
299 // Are the sets of children equal, one by one?
300 return (children==n.children);
303 bool node::operator<(const node &n) const
305 // Are the types of the top-level node vertices different?
306 if (typeid(*vert)!=typeid(*n.vert))
307 return typeid(*vert).before(typeid(*n.vert));
308 // Are the indices of the top-level nodes different?
309 if (!(*vert==*n.vert))
310 return (vert<n.vert);
311 // Are the sets of children different, one by one?
312 return (children<n.children);
316 * These operators let us write down trees in an intuitive way, by adding
317 * arbitrarily complex children to a given vertex. The eye candy that can be
318 * produced with it makes detection of errors much simpler than with code
319 * written using calls to node's method add_child() because it allows for
320 * editor-assisted indentation.
322 const node operator+(const node &n, const child &c)
325 nn.add_child(c.first, c.second);
329 void operator+=(node &n, const child &c)
331 n.add_child(c.first, c.second);
339 static const node tree1(unsigned cuts=0)
348 * Vacuum Gamma Vacuum
352 static const node tree2(unsigned cuts=0)
356 + child(Sigma(), bool(cuts & 1))
357 + child(Sigma(), bool(cuts & 2))
358 + child(Sigma_flipped(), bool(cuts & 4)),
360 + child(Gamma(), bool(cuts & 16))
361 + child(Gamma(), bool(cuts & 32)));
374 static const node tree3(unsigned cuts=0)
380 + child(Sigma(), bool(cuts & 1)),
383 + child(Sigma(), bool(cuts & 4))
384 + child(Sigma(), bool(cuts & 8)),
394 * Sigma Sigma Sigma0 Sigma
396 static const node tree4(unsigned cuts=0)
400 + child(Sigma(), bool(cuts & 1))
401 + child(Sigma(), bool(cuts & 2)),
404 + child(Sigma_flipped(), bool(cuts & 8))
405 + child(Sigma(), bool(cuts & 16)),
411 * Sigma Vacuum Vacuum
415 static const node tree5(unsigned cuts=0)
418 + child(Sigma(), bool(cuts & 1))
420 + child(Sigma(), bool(cuts & 2))
421 + child(Sigma_flipped(), bool(cuts & 4)),
424 + child(Sigma(), bool(cuts & 16)),
436 static const node tree6(unsigned cuts=0)
440 + child(Sigma(), bool(cuts & 1)),
442 + child(Sigma_flipped()
444 + child(Vacuum(), bool(cuts & 4)),
449 static unsigned test_tree(const node tree_generator(unsigned))
451 const int edges = tree_generator(0).total_edges();
452 const int vertices = edges+1;
454 // fill a vector of all possible 2^edges combinations of cuts...
455 vector<node> counter;
456 for (unsigned i=0; i<(1U<<edges); ++i)
457 counter.push_back(tree_generator(i));
459 // ...the sum, when evaluated and reexpanded, is the antipode...
461 for (vector<node>::iterator i=counter.begin(); i!=counter.end(); ++i)
462 result = (result+i->evaluate(x,vertices-1)).series(x==0,vertices-1).expand();
464 // ...and has the nice property that in each term all the Eulers cancel:
465 if (result.has(Euler)) {
466 clog << "The antipode was miscalculated\nAntipode==" << result
467 << "\nshould not have any occurrence of Euler" << endl;
469 } else if (result.ldegree(x)!=-vertices || result.degree(x)!=0) {
470 clog << "The antipode was miscalculated\nAntipode==" << result
471 << "\nshould run from " << x << "^(" << -vertices << ") to "
472 << x << "^(0)" << "but it runs from " << x << "^("
473 << result.ldegree(x) << ")" << "to " << x << "^("
474 << result.degree(x) << ")" << endl;
480 unsigned time_antipode()
483 timer jaeger_le_coultre;
485 cout << "timing computation of antipodes in Yukawa theory" << flush;
488 jaeger_le_coultre.start();
489 result += test_tree(tree1); cout << '.' << flush;
490 result += test_tree(tree2); cout << '.' << flush;
491 result += test_tree(tree3); cout << '.' << flush;
492 result += test_tree(tree4); cout << '.' << flush;
493 result += test_tree(tree5); cout << '.' << flush;
494 result += test_tree(tree6); cout << '.' << flush;
496 cout << jaeger_le_coultre.read() << "s (total)" << endl;
498 cout << " disabled" << endl;
503 extern void randomify_symbol_serials();
505 int main(int argc, char** argv)
507 randomify_symbol_serials();
508 cout << setprecision(2) << showpoint;
509 return time_antipode();