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-2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44 // whether to run this beast or not:
45 static const bool do_test = true;
47 // regularization parameter:
48 static const symbol x("x");
50 typedef pair<unsigned, unsigned> ijpair;
51 typedef pair<class node, bool> child;
53 const constant TrOne("Tr[One]", numeric(4));
55 /* Extract only the divergent part of a series and discard the rest. */
56 static ex div_part(const ex &exarg, const symbol &x, unsigned grad)
58 const ex exser = exarg.series(x==0, grad);
59 if (exser.degree(x)<0)
60 throw runtime_error("divergent part truncation disaster");
62 for (int i=exser.ldegree(x); i<0; ++i)
63 exser_trunc += exser.coeff(x,i)*pow(x,i);
64 // NB: exser_trunc is by construction collected in x.
68 /* F_ab(a, i, b, j, "x") is a common pattern in all vertex evaluators. */
69 static ex F_ab(int a, int i, int b, int j, const symbol &x)
71 if ((i==0 && a<=0) || (j==0 && b<=0))
74 return (tgamma(2-a-(i+1)*x)*
76 tgamma(a+b-2+(1+i+j)*x)/
78 tgamma(b+j*x)/tgamma(4-a-b-(2+i+j)*x));
81 /* Abstract base class (ABC) for all types of vertices. */
84 vertex(ijpair ij = ijpair(0,0)) : indices(ij) { }
85 void increment_indices(const ijpair &ind) { indices.first += ind.first; indices.second += ind.second; }
87 virtual vertex* copy() const = 0;
88 virtual ijpair get_increment() const { return indices; }
89 virtual const ex evaluate(const symbol &x, const unsigned grad) const = 0;
90 bool operator==(const vertex &v) const { return (indices==v.indices); }
91 bool operator<(const vertex &v) const { return (indices<v.indices); }
98 * Class of vertices of type Sigma.
100 class Sigma : public vertex {
102 Sigma(ijpair ij = ijpair(0,0)) : vertex(ij) { }
103 vertex* copy() const { return new Sigma(*this); }
104 ijpair get_increment() const { return ijpair(indices.first+indices.second+1, 0); }
105 const ex evaluate(const symbol &x, const unsigned grad) const;
109 const ex Sigma::evaluate(const symbol &x, const unsigned grad) const
111 // c.f. comments in node::evaluate()
112 static map<Sigma,ex> catalog;
113 static unsigned prev_grad = 0;
114 if (grad>prev_grad) {
118 map<Sigma,ex>::iterator pos = catalog.find(*this);
119 if (pos==catalog.end()) {
120 int i = indices.first;
121 int j = indices.second;
122 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();
123 pos = catalog.insert(map<Sigma,ex>::value_type(*this,result)).first;
131 /** Class of vertices of type Sigma_flipped, sitting in the upper fermionline of Vacuum; no consequences for Gamma. */
132 class Sigma_flipped : public Sigma {
134 Sigma_flipped(ijpair ij = ijpair(0,0)) : Sigma(ij) { }
135 vertex* copy() const { return new Sigma_flipped(*this); }
136 ijpair get_increment() const { return ijpair(0, indices.first+indices.second+1); }
137 const ex evaluate(const symbol &x, const unsigned grad) const { return Sigma::evaluate(x, grad); }
143 *Class of vertices of type Gamma.
145 class Gamma : public vertex {
147 Gamma(ijpair ij = ijpair(0,0)) : vertex(ij) { }
148 vertex* copy() const { return new Gamma(*this); }
149 ijpair get_increment() const { return ijpair(indices.first+indices.second+1, 0); }
150 const ex evaluate(const symbol &x, const unsigned grad) const;
154 const ex Gamma::evaluate(const symbol &x, const unsigned grad) const
156 // c.f. comments in node::evaluate()
157 static map<Gamma,ex> catalog;
158 static unsigned prev_grad = 0;
159 if (prev_grad<grad) {
163 map<Gamma,ex>::iterator pos = catalog.find(*this);
164 if (pos==catalog.end()) {
165 int i = indices.first;
166 int j = indices.second;
167 const ex result = (F_ab(1,i,1,j,x)).series(x==0,grad).expand();
168 pos = catalog.insert(map<Gamma,ex>::value_type(*this,result)).first;
177 * Class of vertices of type Vacuum.
179 class Vacuum : public vertex {
181 Vacuum(ijpair ij = ijpair(0,0)) : vertex(ij) { }
182 vertex* copy() const { return new Vacuum(*this); }
183 ijpair get_increment() const { return ijpair(0, indices.first+indices.second+1); }
184 const ex evaluate(const symbol &x, const unsigned grad) const;
188 const ex Vacuum::evaluate(const symbol &x, const unsigned grad) const
190 // c.f. comments in node::evaluate()
191 static map<Vacuum,ex> catalog;
192 static unsigned prev_grad = 0;
193 if (prev_grad<grad) {
197 map<Vacuum,ex>::iterator pos = catalog.find(*this);
198 if (pos==catalog.end()) {
199 int i = indices.first;
200 int j = indices.second;
201 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();
202 pos = catalog.insert(map<Vacuum,ex>::value_type(*this,result)).first;
211 * Class of nodes (or trees or subtrees), including list of children.
215 node(const vertex &v) { vert = v.copy(); }
216 node(const node &n) { vert = (n.vert)->copy(); children = n.children; }
217 const node & operator=(const node &);
218 ~node() { delete vert; }
219 void add_child(const node &, bool = false);
220 const ex evaluate(const symbol &x, unsigned grad) const;
221 unsigned total_edges() const;
222 bool operator==(const node &) const;
223 bool operator<(const node &) const;
226 multiset<child> children;
229 const node & node::operator=(const node &n)
233 vert = (n.vert)->copy();
234 children = n.children;
239 void node::add_child(const node &childnode, bool cut)
241 children.insert(child(childnode, cut));
243 vert->increment_indices(childnode.vert->get_increment());
246 const ex node::evaluate(const symbol &x, unsigned grad) const
248 static map<node,ex> catalog; // lookup table for already evaluated subnodes
249 static unsigned prev_grad = 0; // grad argument that the catalog has been build for
250 if (grad>prev_grad) {
251 // We haven't computed far enough last time. Our catalog cannot cope with
252 // the demands for this value of grad so let's clear it.
256 ex product = 1; // accumulator for all the children
257 for (multiset<child>::const_iterator i=children.begin(); i!=children.end(); ++i) {
258 map<node,ex>::iterator pos = catalog.find(i->first);
259 if (pos==catalog.end()) {
260 pos = catalog.insert(map<node,ex>::value_type(i->first,i->first.evaluate(x,grad).series(x==0,grad).expand())).first;
261 if (grad<prev_grad) {
262 // We have just spoiled the catalog by inserting a series computed
263 // to lower grad than the others in it. So let's make sure next time
264 // we don't use one of the newly inserted ones by bumping prev_grad
265 // down to the current value of grad.
270 product *= pos->second;
272 product *= -div_part(pos->second,x,grad);
274 return (product * vert->evaluate(x,grad));
277 unsigned node::total_edges() const
280 for (multiset<child>::const_iterator i=children.begin(); i!=children.end(); ++i) {
281 accu += i->first.total_edges();
287 bool node::operator==(const node &n) const
289 // Are the types of the top-level node vertices equal?
290 if (typeid(*vert)!=typeid(*n.vert))
292 // Are the indices of the top-level nodes equal?
293 if (!(*vert==*n.vert))
295 // Are the sets of children equal, one by one?
296 return (children==n.children);
299 bool node::operator<(const node &n) const
301 // Are the types of the top-level node vertices different?
302 if (typeid(*vert)!=typeid(*n.vert))
303 return typeid(*vert).before(typeid(*n.vert));
304 // Are the indices of the top-level nodes different?
305 if (!(*vert==*n.vert))
306 return (vert<n.vert);
307 // Are the sets of children different, one by one?
308 return (children<n.children);
312 * These operators let us write down trees in an intuitive way, by adding
313 * arbitrarily complex children to a given vertex. The eye candy that can be
314 * produced with it makes detection of errors much simpler than with code
315 * written using calls to node's method add_child() because it allows for
316 * editor-assisted indentation.
318 const node operator+(const node &n, const child &c)
321 nn.add_child(c.first, c.second);
325 void operator+=(node &n, const child &c)
327 n.add_child(c.first, c.second);
335 static const node tree1(unsigned cuts=0)
344 * Vacuum Gamma Vacuum
348 static const node tree2(unsigned cuts=0)
352 + child(Sigma(), bool(cuts & 1))
353 + child(Sigma(), bool(cuts & 2))
354 + child(Sigma_flipped(), bool(cuts & 4)),
356 + child(Gamma(), bool(cuts & 16))
357 + child(Gamma(), bool(cuts & 32)));
370 static const node tree3(unsigned cuts=0)
376 + child(Sigma(), bool(cuts & 1)),
379 + child(Sigma(), bool(cuts & 4))
380 + child(Sigma(), bool(cuts & 8)),
390 * Sigma Sigma Sigma0 Sigma
392 static const node tree4(unsigned cuts=0)
396 + child(Sigma(), bool(cuts & 1))
397 + child(Sigma(), bool(cuts & 2)),
400 + child(Sigma_flipped(), bool(cuts & 8))
401 + child(Sigma(), bool(cuts & 16)),
407 * Sigma Vacuum Vacuum
411 static const node tree5(unsigned cuts=0)
414 + child(Sigma(), bool(cuts & 1))
416 + child(Sigma(), bool(cuts & 2))
417 + child(Sigma_flipped(), bool(cuts & 4)),
420 + child(Sigma(), bool(cuts & 16)),
432 static const node tree6(unsigned cuts=0)
436 + child(Sigma(), bool(cuts & 1)),
438 + child(Sigma_flipped()
440 + child(Vacuum(), bool(cuts & 4)),
445 static unsigned test_tree(const node tree_generator(unsigned))
447 const int edges = tree_generator(0).total_edges();
448 const int vertices = edges+1;
450 // fill a vector of all possible 2^edges combinations of cuts...
451 vector<node> counter;
452 for (unsigned i=0; i<(1U<<edges); ++i)
453 counter.push_back(tree_generator(i));
455 // ...the sum, when evaluated and reexpanded, is the antipode...
457 for (vector<node>::iterator i=counter.begin(); i!=counter.end(); ++i)
458 result = (result+i->evaluate(x,vertices)).series(x==0,vertices).expand();
460 // ...and has the nice property that in each term all the Eulers cancel:
461 if (result.has(Euler)) {
462 clog << "The antipode was miscalculated\nAntipode==" << result
463 << "\nshould not have any occurrence of Euler" << endl;
465 } else if (result.ldegree(x)!=-vertices || result.degree(x)!=0) {
466 clog << "The antipode was miscalculated\nAntipode==" << result
467 << "\nshould run from " << x << "^(" << -vertices << ") to "
468 << x << "^(0)" << "but it runs from " << x << "^("
469 << result.ldegree(x) << ")" << "to " << x << "^("
470 << result.degree(x) << ")" << endl;
476 unsigned time_antipode()
479 timer jaeger_le_coultre;
481 cout << "timing computation of antipodes in Yukawa theory" << flush;
482 clog << "-------computation of antipodes in Yukawa theory:" << endl;
485 jaeger_le_coultre.start();
486 result += test_tree(tree1); cout << '.' << flush;
487 result += test_tree(tree2); cout << '.' << flush;
488 result += test_tree(tree3); cout << '.' << flush;
489 result += test_tree(tree4); cout << '.' << flush;
490 result += test_tree(tree5); cout << '.' << flush;
491 result += test_tree(tree6); cout << '.' << flush;
495 clog << "(no output)" << endl;
499 cout << int(1000*jaeger_le_coultre.read())*0.001 << "s (total)" << endl;
501 cout << " disabled" << endl;
502 clog << "(no output)" << endl;