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-2008 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
45 using namespace GiNaC;
47 // whether to run this beast or not:
48 static const bool do_test = true;
50 // regularization parameter:
51 static const symbol x("x");
53 typedef pair<unsigned, unsigned> ijpair;
54 typedef pair<class node, bool> child;
56 const constant TrOne("Tr[One]", numeric(4));
58 /* Extract only the divergent part of a series and discard the rest. */
59 static ex div_part(const ex &exarg, const symbol &x, unsigned grad)
61 const ex exser = exarg.series(x==0, grad);
62 if (exser.degree(x)<0)
63 throw runtime_error("divergent part truncation disaster");
65 for (int i=exser.ldegree(x); i<0; ++i)
66 exser_trunc += exser.coeff(x,i)*pow(x,i);
67 // NB: exser_trunc is by construction collected in x.
71 /* F_ab(a, i, b, j, "x") is a common pattern in all vertex evaluators. */
72 static ex F_ab(int a, int i, int b, int j, const symbol &x)
74 if ((i==0 && a<=0) || (j==0 && b<=0))
77 return (tgamma(2-a-(i+1)*x)*
79 tgamma(a+b-2+(1+i+j)*x)/
81 tgamma(b+j*x)/tgamma(4-a-b-(2+i+j)*x));
84 /* Abstract base class (ABC) for all types of vertices. */
87 vertex(ijpair ij = ijpair(0,0)) : indices(ij) { }
88 void increment_indices(const ijpair &ind) { indices.first += ind.first; indices.second += ind.second; }
90 virtual vertex* copy() const = 0;
91 virtual ijpair get_increment() const { return indices; }
92 virtual const ex evaluate(const symbol &x, const unsigned grad) const = 0;
93 bool operator==(const vertex &v) const { return (indices==v.indices); }
94 bool operator<(const vertex &v) const { return (indices<v.indices); }
101 * Class of vertices of type Sigma.
103 class Sigma : public vertex {
105 Sigma(ijpair ij = ijpair(0,0)) : vertex(ij) { }
106 vertex* copy() const { return new Sigma(*this); }
107 ijpair get_increment() const { return ijpair(indices.first+indices.second+1, 0); }
108 const ex evaluate(const symbol &x, const unsigned grad) const;
112 const ex Sigma::evaluate(const symbol &x, const unsigned grad) const
114 // c.f. comments in node::evaluate()
115 static map<Sigma,ex> catalog;
116 static unsigned prev_grad = 0;
117 if (grad>prev_grad) {
121 map<Sigma,ex>::iterator pos = catalog.find(*this);
122 if (pos==catalog.end()) {
123 int i = indices.first;
124 int j = indices.second;
125 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();
126 pos = catalog.insert(map<Sigma,ex>::value_type(*this,result)).first;
134 /** Class of vertices of type Sigma_flipped, sitting in the upper fermionline of Vacuum; no consequences for Gamma. */
135 class Sigma_flipped : public Sigma {
137 Sigma_flipped(ijpair ij = ijpair(0,0)) : Sigma(ij) { }
138 vertex* copy() const { return new Sigma_flipped(*this); }
139 ijpair get_increment() const { return ijpair(0, indices.first+indices.second+1); }
140 const ex evaluate(const symbol &x, const unsigned grad) const { return Sigma::evaluate(x, grad); }
146 *Class of vertices of type Gamma.
148 class Gamma : public vertex {
150 Gamma(ijpair ij = ijpair(0,0)) : vertex(ij) { }
151 vertex* copy() const { return new Gamma(*this); }
152 ijpair get_increment() const { return ijpair(indices.first+indices.second+1, 0); }
153 const ex evaluate(const symbol &x, const unsigned grad) const;
157 const ex Gamma::evaluate(const symbol &x, const unsigned grad) const
159 // c.f. comments in node::evaluate()
160 static map<Gamma,ex> catalog;
161 static unsigned prev_grad = 0;
162 if (prev_grad<grad) {
166 map<Gamma,ex>::iterator pos = catalog.find(*this);
167 if (pos==catalog.end()) {
168 int i = indices.first;
169 int j = indices.second;
170 const ex result = (F_ab(1,i,1,j,x)).series(x==0,grad).expand();
171 pos = catalog.insert(map<Gamma,ex>::value_type(*this,result)).first;
180 * Class of vertices of type Vacuum.
182 class Vacuum : public vertex {
184 Vacuum(ijpair ij = ijpair(0,0)) : vertex(ij) { }
185 vertex* copy() const { return new Vacuum(*this); }
186 ijpair get_increment() const { return ijpair(0, indices.first+indices.second+1); }
187 const ex evaluate(const symbol &x, const unsigned grad) const;
191 const ex Vacuum::evaluate(const symbol &x, const unsigned grad) const
193 // c.f. comments in node::evaluate()
194 static map<Vacuum,ex> catalog;
195 static unsigned prev_grad = 0;
196 if (prev_grad<grad) {
200 map<Vacuum,ex>::iterator pos = catalog.find(*this);
201 if (pos==catalog.end()) {
202 int i = indices.first;
203 int j = indices.second;
204 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();
205 pos = catalog.insert(map<Vacuum,ex>::value_type(*this,result)).first;
214 * Class of nodes (or trees or subtrees), including list of children.
218 node(const vertex &v) { vert = v.copy(); }
219 node(const node &n) { vert = (n.vert)->copy(); children = n.children; }
220 const node & operator=(const node &);
221 ~node() { delete vert; }
222 void add_child(const node &, bool = false);
223 const ex evaluate(const symbol &x, unsigned grad) const;
224 unsigned total_edges() const;
225 bool operator==(const node &) const;
226 bool operator<(const node &) const;
229 multiset<child> children;
232 const node & node::operator=(const node &n)
236 vert = (n.vert)->copy();
237 children = n.children;
242 void node::add_child(const node &childnode, bool cut)
244 children.insert(child(childnode, cut));
246 vert->increment_indices(childnode.vert->get_increment());
249 const ex node::evaluate(const symbol &x, unsigned grad) const
251 static map<node,ex> catalog; // lookup table for already evaluated subnodes
252 static unsigned prev_grad = 0; // grad argument that the catalog has been build for
253 if (grad>prev_grad) {
254 // We haven't computed far enough last time. Our catalog cannot cope with
255 // the demands for this value of grad so let's clear it.
259 ex product = 1; // accumulator for all the children
260 for (multiset<child>::const_iterator i=children.begin(); i!=children.end(); ++i) {
261 map<node,ex>::iterator pos = catalog.find(i->first);
262 if (pos==catalog.end()) {
263 pos = catalog.insert(map<node,ex>::value_type(i->first,i->first.evaluate(x,grad).series(x==0,grad).expand())).first;
264 if (grad<prev_grad) {
265 // We have just spoiled the catalog by inserting a series computed
266 // to lower grad than the others in it. So let's make sure next time
267 // we don't use one of the newly inserted ones by bumping prev_grad
268 // down to the current value of grad.
273 product *= pos->second;
275 product *= -div_part(pos->second,x,grad);
277 return (product * vert->evaluate(x,grad));
280 unsigned node::total_edges() const
283 for (multiset<child>::const_iterator i=children.begin(); i!=children.end(); ++i) {
284 accu += i->first.total_edges();
290 bool node::operator==(const node &n) const
292 // Are the types of the top-level node vertices equal?
293 if (typeid(*vert)!=typeid(*n.vert))
295 // Are the indices of the top-level nodes equal?
296 if (!(*vert==*n.vert))
298 // Are the sets of children equal, one by one?
299 return (children==n.children);
302 bool node::operator<(const node &n) const
304 // Are the types of the top-level node vertices different?
305 if (typeid(*vert)!=typeid(*n.vert))
306 return typeid(*vert).before(typeid(*n.vert));
307 // Are the indices of the top-level nodes different?
308 if (!(*vert==*n.vert))
309 return (vert<n.vert);
310 // Are the sets of children different, one by one?
311 return (children<n.children);
315 * These operators let us write down trees in an intuitive way, by adding
316 * arbitrarily complex children to a given vertex. The eye candy that can be
317 * produced with it makes detection of errors much simpler than with code
318 * written using calls to node's method add_child() because it allows for
319 * editor-assisted indentation.
321 const node operator+(const node &n, const child &c)
324 nn.add_child(c.first, c.second);
328 void operator+=(node &n, const child &c)
330 n.add_child(c.first, c.second);
338 static const node tree1(unsigned cuts=0)
347 * Vacuum Gamma Vacuum
351 static const node tree2(unsigned cuts=0)
355 + child(Sigma(), bool(cuts & 1))
356 + child(Sigma(), bool(cuts & 2))
357 + child(Sigma_flipped(), bool(cuts & 4)),
359 + child(Gamma(), bool(cuts & 16))
360 + child(Gamma(), bool(cuts & 32)));
373 static const node tree3(unsigned cuts=0)
379 + child(Sigma(), bool(cuts & 1)),
382 + child(Sigma(), bool(cuts & 4))
383 + child(Sigma(), bool(cuts & 8)),
393 * Sigma Sigma Sigma0 Sigma
395 static const node tree4(unsigned cuts=0)
399 + child(Sigma(), bool(cuts & 1))
400 + child(Sigma(), bool(cuts & 2)),
403 + child(Sigma_flipped(), bool(cuts & 8))
404 + child(Sigma(), bool(cuts & 16)),
410 * Sigma Vacuum Vacuum
414 static const node tree5(unsigned cuts=0)
417 + child(Sigma(), bool(cuts & 1))
419 + child(Sigma(), bool(cuts & 2))
420 + child(Sigma_flipped(), bool(cuts & 4)),
423 + child(Sigma(), bool(cuts & 16)),
435 static const node tree6(unsigned cuts=0)
439 + child(Sigma(), bool(cuts & 1)),
441 + child(Sigma_flipped()
443 + child(Vacuum(), bool(cuts & 4)),
448 static unsigned test_tree(const node tree_generator(unsigned))
450 const int edges = tree_generator(0).total_edges();
451 const int vertices = edges+1;
453 // fill a vector of all possible 2^edges combinations of cuts...
454 vector<node> counter;
455 for (unsigned i=0; i<(1U<<edges); ++i)
456 counter.push_back(tree_generator(i));
458 // ...the sum, when evaluated and reexpanded, is the antipode...
460 for (vector<node>::iterator i=counter.begin(); i!=counter.end(); ++i)
461 result = (result+i->evaluate(x,vertices-1)).series(x==0,vertices-1).expand();
463 // ...and has the nice property that in each term all the Eulers cancel:
464 if (result.has(Euler)) {
465 clog << "The antipode was miscalculated\nAntipode==" << result
466 << "\nshould not have any occurrence of Euler" << endl;
468 } else if (result.ldegree(x)!=-vertices || result.degree(x)!=0) {
469 clog << "The antipode was miscalculated\nAntipode==" << result
470 << "\nshould run from " << x << "^(" << -vertices << ") to "
471 << x << "^(0)" << "but it runs from " << x << "^("
472 << result.ldegree(x) << ")" << "to " << x << "^("
473 << result.degree(x) << ")" << endl;
479 unsigned time_antipode()
482 timer jaeger_le_coultre;
484 cout << "timing computation of antipodes in Yukawa theory" << flush;
487 jaeger_le_coultre.start();
488 result += test_tree(tree1); cout << '.' << flush;
489 result += test_tree(tree2); cout << '.' << flush;
490 result += test_tree(tree3); cout << '.' << flush;
491 result += test_tree(tree4); cout << '.' << flush;
492 result += test_tree(tree5); cout << '.' << flush;
493 result += test_tree(tree6); cout << '.' << flush;
495 cout << jaeger_le_coultre.read() << "s (total)" << endl;
497 cout << " disabled" << endl;
502 extern void randomify_symbol_serials();
504 int main(int argc, char** argv)
506 randomify_symbol_serials();
507 cout << setprecision(2) << showpoint;
508 return time_antipode();