GiNaC 1.8.10
inifcns_elliptic.cpp
Go to the documentation of this file.
1
25/*
26 * GiNaC Copyright (C) 1999-2026 Johannes Gutenberg University Mainz, Germany
27 *
28 * This program is free software; you can redistribute it and/or modify
29 * it under the terms of the GNU General Public License as published by
30 * the Free Software Foundation; either version 2 of the License, or
31 * (at your option) any later version.
32 *
33 * This program is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 * GNU General Public License for more details.
37 *
38 * You should have received a copy of the GNU General Public License
39 * along with this program. If not, see <https://www.gnu.org/licenses/>.
40 */
41
42#include "inifcns.h"
43
44#include "add.h"
45#include "constant.h"
46#include "lst.h"
47#include "mul.h"
48#include "numeric.h"
49#include "operators.h"
50#include "power.h"
51#include "pseries.h"
52#include "relational.h"
53#include "symbol.h"
54#include "utils.h"
55#include "wildcard.h"
56
57#include "integration_kernel.h"
59
60#include <cln/cln.h>
61#include <sstream>
62#include <stdexcept>
63#include <vector>
64#include <cmath>
65
66namespace GiNaC {
67
68
70//
71// Complete elliptic integrals
72//
73// helper functions
74//
76
77
78// anonymous namespace for helper function
79namespace {
80
81// Computes the arithmetic geometric of two numbers a_0 and b_0
82cln::cl_N arithmetic_geometric_mean(const cln::cl_N & a_0, const cln::cl_N & b_0)
83{
84 cln::cl_N a_old = a_0 * cln::cl_float(1, cln::float_format(Digits));
85 cln::cl_N b_old = b_0 * cln::cl_float(1, cln::float_format(Digits));
86 cln::cl_N a_new;
87 cln::cl_N b_new;
88 cln::cl_N res = a_old;
89 cln::cl_N resbuf;
90 do {
91 resbuf = res;
92
93 a_new = (a_old+b_old)/2;
94 b_new = sqrt(a_old*b_old);
95
96 if ( ( abs(a_new-b_new) > abs(a_new+b_new) )
97 ||
98 ( (abs(a_new-b_new) == abs(a_new+b_new)) && (imagpart(b_new/a_new) <= 0) ) ) {
99 b_new *= -1;
100 }
101
102 res = a_new;
103 a_old = a_new;
104 b_old = b_new;
105
106 } while (res != resbuf);
107 return res;
108}
109
110// Computes
111// a0^2 - sum_{n=0}^infinity 2^{n-1}*c_n^2
112// with
113// c_{n+1} = c_n^2/4/a_{n+1}
114//
115// Needed for the complete elliptic integral of the second kind.
116//
117cln::cl_N agm_helper_second_kind(const cln::cl_N & a_0, const cln::cl_N & b_0, const cln::cl_N & c_0)
118{
119 cln::cl_N a_old = a_0 * cln::cl_float(1, cln::float_format(Digits));
120 cln::cl_N b_old = b_0 * cln::cl_float(1, cln::float_format(Digits));
121 cln::cl_N c_old = c_0 * cln::cl_float(1, cln::float_format(Digits));
122 cln::cl_N a_new;
123 cln::cl_N b_new;
124 cln::cl_N c_new;
125 cln::cl_N res = square(a_old)-square(c_old)/2;
126 cln::cl_N resbuf;
127 cln::cl_N pre = cln::cl_float(1, cln::float_format(Digits));
128 do {
129 resbuf = res;
130
131 a_new = (a_old+b_old)/2;
132 b_new = sqrt(a_old*b_old);
133
134 if ( ( abs(a_new-b_new) > abs(a_new+b_new) )
135 ||
136 ( (abs(a_new-b_new) == abs(a_new+b_new)) && (imagpart(b_new/a_new) <= 0) ) ) {
137 b_new *= -1;
138 }
139
140 c_new = square(c_old)/4/a_new;
141
142 res -= pre*square(c_new);
143
144 a_old = a_new;
145 b_old = b_new;
146 c_old = c_new;
147 pre *= 2;
148
149 } while (res != resbuf);
150 return res;
151}
152
153
154} // end of anonymous namespace
155
156
158//
159// Complete elliptic integrals
160//
161// GiNaC function
162//
164
165static ex EllipticK_evalf(const ex& k)
166{
167 if ( !k.info(info_flags::numeric) ) {
168 return EllipticK(k).hold();
169 }
170
171 cln::cl_N kbar = sqrt(1-square(ex_to<numeric>(k).to_cl_N()));
172
173 ex result = Pi/2/numeric(arithmetic_geometric_mean(1,kbar));
174
175 return result.evalf();
176}
177
178
179static ex EllipticK_eval(const ex& k)
180{
181 if (k == _ex0) {
182 return Pi/2;
183 }
184
185 if ( k.info(info_flags::numeric) && !k.info(info_flags::crational) ) {
186 return EllipticK(k).evalf();
187 }
188
189 return EllipticK(k).hold();
190}
191
192
193static ex EllipticK_deriv(const ex& k, unsigned deriv_param)
194{
195 return -EllipticK(k)/k + EllipticE(k)/k/(1-k*k);
196}
197
198
199static ex EllipticK_series(const ex& k, const relational& rel, int order, unsigned options)
200{
201 const ex k_pt = k.subs(rel, subs_options::no_pattern);
202
203 if (k_pt == _ex0) {
204 const symbol s;
205 ex ser;
206 // manually construct the primitive expansion
207 for (int i=0; i<(order+1)/2; ++i)
208 {
209 ser += Pi/2 * numeric(cln::square(cln::binomial(2*i,i))) * pow(s/4,2*i);
210 }
211 // substitute the argument's series expansion
212 ser = ser.subs(s==k.series(rel, order), subs_options::no_pattern);
213 // maybe that was terminating, so add a proper order term
214 epvector nseq { expair(Order(_ex1), order) };
215 ser += pseries(rel, std::move(nseq));
216 // reexpanding it will collapse the series again
217 return ser.series(rel, order);
218 }
219
220 if ( (k_pt == _ex1) || (k_pt == _ex_1) ) {
221 throw std::runtime_error("EllipticK_series: don't know how to do the series expansion at this point!");
222 }
223
224 // all other cases
225 throw do_taylor();
226}
227
228static void EllipticK_print_latex(const ex& k, const print_context& c)
229{
230 c.s << "\\mathrm{K}(";
231 k.print(c);
232 c.s << ")";
233}
234
235
237 evalf_func(EllipticK_evalf).
238 eval_func(EllipticK_eval).
239 derivative_func(EllipticK_deriv).
240 series_func(EllipticK_series).
241 print_func<print_latex>(EllipticK_print_latex).
242 do_not_evalf_params());
243
244
245static ex EllipticE_evalf(const ex& k)
246{
247 if ( !k.info(info_flags::numeric) ) {
248 return EllipticE(k).hold();
249 }
250
251 cln::cl_N kbar = sqrt(1-square(ex_to<numeric>(k).to_cl_N()));
252
253 ex result = Pi/2 * numeric( agm_helper_second_kind(1,kbar,ex_to<numeric>(k).to_cl_N()) / arithmetic_geometric_mean(1,kbar) );
254
255 return result.evalf();
256}
257
258
259static ex EllipticE_eval(const ex& k)
260{
261 if (k == _ex0) {
262 return Pi/2;
263 }
264
265 if ( (k == _ex1) || (k == _ex_1) ) {
266 return 1;
267 }
268
269 if ( k.info(info_flags::numeric) && !k.info(info_flags::crational) ) {
270 return EllipticE(k).evalf();
271 }
272
273 return EllipticE(k).hold();
274}
275
276
277static ex EllipticE_deriv(const ex& k, unsigned deriv_param)
278{
279 return -EllipticK(k)/k + EllipticE(k)/k;
280}
281
282
283static ex EllipticE_series(const ex& k, const relational& rel, int order, unsigned options)
284{
285 const ex k_pt = k.subs(rel, subs_options::no_pattern);
286
287 if (k_pt == _ex0) {
288 const symbol s;
289 ex ser;
290 // manually construct the primitive expansion
291 for (int i=0; i<(order+1)/2; ++i)
292 {
293 ser -= Pi/2 * numeric(cln::square(cln::binomial(2*i,i)))/(2*i-1) * pow(s/4,2*i);
294 }
295 // substitute the argument's series expansion
296 ser = ser.subs(s==k.series(rel, order), subs_options::no_pattern);
297 // maybe that was terminating, so add a proper order term
298 epvector nseq { expair(Order(_ex1), order) };
299 ser += pseries(rel, std::move(nseq));
300 // reexpanding it will collapse the series again
301 return ser.series(rel, order);
302 }
303
304 if ( (k_pt == _ex1) || (k_pt == _ex_1) ) {
305 throw std::runtime_error("EllipticE_series: don't know how to do the series expansion at this point!");
306 }
307
308 // all other cases
309 throw do_taylor();
310}
311
312static void EllipticE_print_latex(const ex& k, const print_context& c)
313{
314 c.s << "\\mathrm{K}(";
315 k.print(c);
316 c.s << ")";
317}
318
319
321 evalf_func(EllipticE_evalf).
322 eval_func(EllipticE_eval).
323 derivative_func(EllipticE_deriv).
324 series_func(EllipticE_series).
325 print_func<print_latex>(EllipticE_print_latex).
326 do_not_evalf_params());
327
328
330//
331// Iterated integrals
332//
333// helper functions
334//
336
337// anonymous namespace for helper function
338namespace {
339
340// performs the actual series summation for an iterated integral
341cln::cl_N iterated_integral_do_sum(const std::vector<int> & m, const std::vector<const integration_kernel *> & kernel, const cln::cl_N & lambda, int N_trunc)
342{
343 if ( cln::zerop(lambda) ) {
344 return 0;
345 }
346
347 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
348
349 const int depth = m.size();
350
351 cln::cl_N res = 0;
352 cln::cl_N resbuf;
353 cln::cl_N subexpr;
354
355 if ( N_trunc == 0 ) {
356 // sum until precision is reached
357 bool flag_accidental_zero = false;
358
359 int N = 1;
360
361 do {
362 resbuf = res;
363
364 if ( depth > 1 ) {
365 subexpr = 0;
366 multi_iterator_ordered_eq<int> i_multi(1,N+1,depth-1);
367 for( i_multi.init(); !i_multi.overflow(); i_multi++) {
368 cln::cl_N tt = one;
369 for (int j=1; j<depth; j++) {
370 if ( j==1 ) {
371 tt = tt * kernel[0]->series_coeff(N-i_multi[depth-2]) / cln::expt(cln::cl_I(i_multi[depth-2]),m[1]);
372 }
373 else {
374 tt = tt * kernel[j-1]->series_coeff(i_multi[depth-j]-i_multi[depth-j-1]) / cln::expt(cln::cl_I(i_multi[depth-j-1]),m[j]);
375 }
376 }
377 tt = tt * kernel[depth-1]->series_coeff(i_multi[0]);
378 subexpr += tt;
379 }
380 }
381 else {
382 // depth == 1
383 subexpr = kernel[0]->series_coeff(N) * one;
384 }
385 flag_accidental_zero = cln::zerop(subexpr);
386 res += cln::expt(lambda, N) / cln::expt(cln::cl_I(N),m[0]) * subexpr;
387 N++;
388
389 } while ( (res != resbuf) || flag_accidental_zero );
390 }
391 else {
392 // N_trunc > 0, sum up the first N_trunc terms
393 for (int N=1; N<=N_trunc; N++) {
394 if ( depth > 1 ) {
395 subexpr = 0;
396 multi_iterator_ordered_eq<int> i_multi(1,N+1,depth-1);
397 for( i_multi.init(); !i_multi.overflow(); i_multi++) {
398 cln::cl_N tt = one;
399 for (int j=1; j<depth; j++) {
400 if ( j==1 ) {
401 tt = tt * kernel[0]->series_coeff(N-i_multi[depth-2]) / cln::expt(cln::cl_I(i_multi[depth-2]),m[1]);
402 }
403 else {
404 tt = tt * kernel[j-1]->series_coeff(i_multi[depth-j]-i_multi[depth-j-1]) / cln::expt(cln::cl_I(i_multi[depth-j-1]),m[j]);
405 }
406 }
407 tt = tt * kernel[depth-1]->series_coeff(i_multi[0]);
408 subexpr += tt;
409 }
410 }
411 else {
412 // depth == 1
413 subexpr = kernel[0]->series_coeff(N) * one;
414 }
415 res += cln::expt(lambda, N) / cln::expt(cln::cl_I(N),m[0]) * subexpr;
416 }
417 }
418
419 return res;
420}
421
422// figure out the number of basic_log_kernels before a non-basic_log_kernel
423cln::cl_N iterated_integral_prepare_m_lst(const std::vector<const integration_kernel *> & kernel_in, const cln::cl_N & lambda, int N_trunc)
424{
425 size_t depth = kernel_in.size();
426
427 std::vector<int> m;
428 m.reserve(depth);
429
430 std::vector<const integration_kernel *> kernel;
431 kernel.reserve(depth);
432
433 int n = 1;
434
435 for (const auto & it : kernel_in) {
436 if ( is_a<basic_log_kernel>(*it) ) {
437 n++;
438 }
439 else {
440 m.push_back(n);
441 kernel.push_back( &ex_to<integration_kernel>(*it) );
442 n = 1;
443 }
444 }
445
446 cln::cl_N result = iterated_integral_do_sum(m, kernel, lambda, N_trunc);
447
448 return result;
449}
450
451// shuffle to remove trailing zeros,
452// integration kernels, which are not basic_log_kernels, are treated as regularised kernels
453cln::cl_N iterated_integral_shuffle(const std::vector<const integration_kernel *> & kernel, const cln::cl_N & lambda, int N_trunc)
454{
455 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
456
457 const size_t depth = kernel.size();
458
459 size_t i_trailing = 0;
460 for (size_t i=0; i<depth; i++) {
461 if ( !(is_a<basic_log_kernel>(*(kernel[i]))) ) {
462 i_trailing = i+1;
463 }
464 }
465
466 if ( i_trailing == 0 ) {
467 return cln::expt(cln::log(lambda), depth) / cln::factorial(depth) * one;
468 }
469
470 if ( i_trailing == depth ) {
471 return iterated_integral_prepare_m_lst(kernel, lambda, N_trunc);
472 }
473
474 // shuffle
475 std::vector<const integration_kernel *> a,b;
476 for (size_t i=0; i<i_trailing; i++) {
477 a.push_back(kernel[i]);
478 }
479 for (size_t i=i_trailing; i<depth; i++) {
480 b.push_back(kernel[i]);
481 }
482
483 cln::cl_N result = iterated_integral_prepare_m_lst(a, lambda, N_trunc) * cln::expt(cln::log(lambda), depth-i_trailing) / cln::factorial(depth-i_trailing);
484 multi_iterator_shuffle_prime<const integration_kernel *> i_shuffle(a,b);
485 for( i_shuffle.init(); !i_shuffle.overflow(); i_shuffle++) {
486 std::vector<const integration_kernel *> new_kernel;
487 new_kernel.reserve(depth);
488 for (size_t i=0; i<depth; i++) {
489 new_kernel.push_back(i_shuffle[i]);
490 }
491
492 result -= iterated_integral_shuffle(new_kernel, lambda, N_trunc);
493 }
494
495 return result;
496}
497
498} // end of anonymous namespace
499
501//
502// Iterated integrals
503//
504// GiNaC function
505//
507
508static ex iterated_integral_evalf_impl(const ex& kernel_lst, const ex& lambda, const ex& N_trunc)
509{
510 // sanity check
511 if ((!kernel_lst.info(info_flags::list)) || (!lambda.evalf().info(info_flags::numeric)) || (!N_trunc.info(info_flags::nonnegint))) {
512 return iterated_integral(kernel_lst,lambda,N_trunc).hold();
513 }
514
515 lst k_lst = ex_to<lst>(kernel_lst);
516
517 bool flag_not_numeric = false;
518 for (const auto & it : k_lst) {
519 if ( !is_a<integration_kernel>(it) ) {
520 flag_not_numeric = true;
521 }
522 }
523 if ( flag_not_numeric) {
524 return iterated_integral(kernel_lst,lambda,N_trunc).hold();
525 }
526
527 for (const auto & it : k_lst) {
528 if ( !(ex_to<integration_kernel>(it).is_numeric()) ) {
529 flag_not_numeric = true;
530 }
531 }
532 if ( flag_not_numeric) {
533 return iterated_integral(kernel_lst,lambda,N_trunc).hold();
534 }
535
536 // now we know that iterated_integral gives a number
537
538 int N_trunc_int = ex_to<numeric>(N_trunc).to_int();
539
540 // check trailing zeros
541 const size_t depth = k_lst.nops();
542
543 std::vector<const integration_kernel *> kernel_vec;
544 kernel_vec.reserve(depth);
545
546 for (const auto & it : k_lst) {
547 kernel_vec.push_back( &ex_to<integration_kernel>(it) );
548 }
549
550 size_t i_trailing = 0;
551 for (size_t i=0; i<depth; i++) {
552 if ( !(kernel_vec[i]->has_trailing_zero()) ) {
553 i_trailing = i+1;
554 }
555 }
556
557 // split integral into regularised integrals and trailing basic_log_kernels
558 // non-basic_log_kernels are treated as regularised kernels in call to iterated_integral_shuffle
559 cln::cl_F one = cln::cl_float(1, cln::float_format(Digits));
560 cln::cl_N lambda_cln = ex_to<numeric>(lambda.evalf()).to_cl_N();
562
563 cln::cl_N result;
564 if ( is_a<basic_log_kernel>(*(kernel_vec[depth-1])) ) {
565 result = 0;
566 }
567 else {
568 result = iterated_integral_shuffle(kernel_vec, lambda_cln, N_trunc_int);
569 }
570
571 cln::cl_N coeff = one;
572 for (size_t i_plus=depth; i_plus>i_trailing; i_plus--) {
573 coeff = coeff * kernel_vec[i_plus-1]->series_coeff(0);
574 kernel_vec[i_plus-1] = &L0;
575 if ( i_plus==i_trailing+1 ) {
576 result += coeff * iterated_integral_shuffle(kernel_vec, lambda_cln, N_trunc_int);
577 }
578 else {
579 if ( !(is_a<basic_log_kernel>(*(kernel_vec[i_plus-2]))) ) {
580 result += coeff * iterated_integral_shuffle(kernel_vec, lambda_cln, N_trunc_int);
581 }
582 }
583 }
584
585 return numeric(result);
586}
587
588static ex iterated_integral2_evalf(const ex& kernel_lst, const ex& lambda)
589{
590 return iterated_integral_evalf_impl(kernel_lst,lambda,0);
591}
592
593static ex iterated_integral3_evalf(const ex& kernel_lst, const ex& lambda, const ex& N_trunc)
594{
595 return iterated_integral_evalf_impl(kernel_lst,lambda,N_trunc);
596}
597
598static ex iterated_integral2_eval(const ex& kernel_lst, const ex& lambda)
599{
600 if ( lambda.info(info_flags::numeric) && !lambda.info(info_flags::crational) ) {
601 return iterated_integral(kernel_lst,lambda).evalf();
602 }
603
604 return iterated_integral(kernel_lst,lambda).hold();
605}
606
607static ex iterated_integral3_eval(const ex& kernel_lst, const ex& lambda, const ex& N_trunc)
608{
609 if ( lambda.info(info_flags::numeric) && !lambda.info(info_flags::crational) ) {
610 return iterated_integral(kernel_lst,lambda,N_trunc).evalf();
611 }
612
613 return iterated_integral(kernel_lst,lambda,N_trunc).hold();
614}
615
617 function::register_new(function_options("iterated_integral", 2).
618 eval_func(iterated_integral2_eval).
619 evalf_func(iterated_integral2_evalf).
620 do_not_evalf_params().
621 overloaded(2));
622
624 function::register_new(function_options("iterated_integral", 3).
625 eval_func(iterated_integral3_eval).
626 evalf_func(iterated_integral3_evalf).
627 do_not_evalf_params().
628 overloaded(2));
629
630} // namespace GiNaC
631
Interface to GiNaC's sums of expressions.
The basic integration kernel with a logarithmic singularity at the origin.
const basic & hold() const
Stop further evaluation.
Definition basic.cpp:886
ex evalf() const override
Evaluate object numerically.
Definition constant.cpp:149
Wrapper template for making GiNaC classes out of STL containers.
Definition container.h:72
size_t nops() const override
Number of operands/members.
Definition container.h:117
Exception class thrown by classes which provide their own series expansion to signal that ordinary Ta...
Definition function.h:667
Lightweight wrapper for GiNaC's symbolic objects.
Definition ex.h:72
ex evalf() const
Definition ex.h:121
ex series(const ex &r, int order, unsigned options=0) const
Compute the truncated series expansion of an expression.
Definition pseries.cpp:1271
ex subs(const exmap &m, unsigned options=0) const
Definition ex.h:841
bool info(unsigned inf) const
Definition ex.h:132
A pair of expressions.
Definition expair.h:37
ex evalf() const override
Evaluate object numerically.
static unsigned register_new(function_options const &opt)
This class is a wrapper around CLN-numbers within the GiNaC class hierarchy.
Definition numeric.h:81
Base class for print_contexts.
Definition print.h:101
This class holds a extended truncated power series (positive and negative integer powers).
Definition pseries.h:35
This class holds a relation consisting of two expressions and a logical relation between them.
Definition relational.h:34
@ no_pattern
disable pattern matching
Definition flags.h:50
Basic CAS symbol.
Definition symbol.h:38
ex series(const relational &s, int order, unsigned options=0) const override
Implementation of ex::series() for symbols.
Definition pseries.cpp:655
Interface to GiNaC's constant types and some special constants.
unsigned options
Definition factor.cpp:2473
vector< int > k
Definition factor.cpp:1434
size_t n
Definition factor.cpp:1431
size_t c
Definition factor.cpp:756
umodpoly one
Definition factor.cpp:1430
mvec m
Definition factor.cpp:757
#define REGISTER_FUNCTION(NAME, OPT)
Definition function.h:119
Interface to GiNaC's initially known functions.
Interface to GiNaC's integration kernels for iterated integrals.
Definition of GiNaC's lst.
Interface to GiNaC's products of expressions.
Definition add.cpp:35
static ex iterated_integral3_evalf(const ex &kernel_lst, const ex &lambda, const ex &N_trunc)
static ex iterated_integral2_eval(const ex &kernel_lst, const ex &lambda)
const numeric pow(const numeric &x, const numeric &y)
Definition numeric.h:250
static ex EllipticE_evalf(const ex &k)
static void EllipticK_print_latex(const ex &k, const print_context &c)
static ex EllipticE_series(const ex &k, const relational &rel, int order, unsigned options)
static ex iterated_integral3_eval(const ex &kernel_lst, const ex &lambda, const ex &N_trunc)
static ex EllipticK_series(const ex &k, const relational &rel, int order, unsigned options)
std::vector< expair > epvector
expair-vector
Definition expairseq.h:32
const numeric abs(const numeric &x)
Absolute value.
Definition numeric.cpp:2319
const ex _ex1
Definition utils.cpp:384
function iterated_integral(const T1 &kernel_lst, const T2 &lambda)
Definition inifcns.h:189
const numeric sqrt(const numeric &x)
Numeric square root.
Definition numeric.cpp:2479
static ex iterated_integral_evalf_impl(const ex &kernel_lst, const ex &lambda, const ex &N_trunc)
const ex _ex_1
Definition utils.cpp:351
static void EllipticE_print_latex(const ex &k, const print_context &c)
static ex EllipticE_eval(const ex &k)
const constant Pi("Pi", PiEvalf, "\\pi", domain::positive)
Pi.
Definition constant.h:84
static ex EllipticK_eval(const ex &k)
static ex EllipticE_deriv(const ex &k, unsigned deriv_param)
static ex EllipticK_evalf(const ex &k)
static ex EllipticK_deriv(const ex &k, unsigned deriv_param)
_numeric_digits Digits
Accuracy in decimal digits.
Definition numeric.cpp:2590
ex coeff(const ex &thisex, const ex &s, int n=1)
Definition ex.h:757
static ex iterated_integral2_evalf(const ex &kernel_lst, const ex &lambda)
const ex _ex0
Definition utils.cpp:368
Makes the interface to the underlying bignum package available.
Interface to GiNaC's overloaded operators.
Interface to GiNaC's symbolic exponentiation (basis^exponent).
Interface to class for extended truncated power series.
Interface to relations between expressions.
Interface to GiNaC's symbolic objects.
Interface to several small and furry utilities needed within GiNaC but not of any interest to the use...
Utilities for summing over multiple indices.
Interface to GiNaC's wildcard objects.

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.