8.1.3. More on 1d integration¶
Now let us repeat the previous study with several changes:
Try binning with variable widths as well as variable integration orders.
Check that changing variables affects the integration.
Use an alternative way of bin edges specification.
Use an alternative way of binding the function output.
Use an alternative integration method.
Here is the code:
1import load
2from gna.env import env
3import gna.constructors as C
4import numpy as np
5from matplotlib import pyplot as plt
6import matplotlib.gridspec as gridspec
7from gna.bindings import common
8import ROOT as R
9
10# Make a variable for global namespace
11ns = env.globalns
12
13# Create a parameter in the global namespace
14pa = ns.defparameter('a', central=1.0, free=True, label='weight 1')
15pb = ns.defparameter('b', central=0.2, free=True, label='weight 2')
16pk = ns.defparameter('k', central=8.0, free=True, label='argument scale')
17
18# Print the list of parameters
19ns.printparameters(labels=True)
20print()
21
22# Define binning and integration orders
23nbins = 20
24x_edges = -np.pi + 2.0*np.pi*np.linspace(0.0, 1.0, nbins+1, dtype='d')**0.6
25x_widths = x_edges[1:]-x_edges[:-1]
26x_width_ratio = x_widths/x_widths.min()
27orders = np.ceil(x_width_ratio*4).astype('i')
28
29# Initialize histogram
30hist = C.Histogram(x_edges)
31
32# Initialize integrator
33integrator = R.IntegratorTrap(nbins, orders)
34integrator.points.edges(hist.hist.hist)
35int_points = integrator.points.x
36
37# Create integrable: a*sin(x) + b*cos(k*x)
38cos_arg = C.WeightedSum( ['k'], [int_points] )
39sin_t = R.Sin(int_points)
40cos_t = R.Cos(cos_arg.sum.sum)
41fcn = C.WeightedSum(['a', 'b'], [sin_t.sin.result, cos_t.cos.result])
42
43integrator.add_input(fcn.sum.sum)
44
45integrator.print()
46print()
47
48# Label transformations
49hist.hist.setLabel('Input histogram\n(bins definition)')
50integrator.points.setLabel('Sampler\n(trapezoidal)')
51integrator.hist.setLabel('Integrator\n(convolution)')
52cos_arg.sum.setLabel('kx')
53sin_t.sin.setLabel('sin(x)')
54cos_t.cos.setLabel('cos(kx)')
55fcn.sum.setLabel('a sin(x) + b cos(kx)')
56
57# Compute the integral analytically as a cross check
58# int| a*sin(x) + b*cos(k*x) = a*cos(x) - b/k*sin(kx) + C
59a, b, k = (p.value() for p in (pa, pb, pk))
60F = - a*np.cos(x_edges) + (b/k)*np.sin(k*x_edges)
61integral_a = F[1:] - F[:-1]
62bar_width = (x_edges[1:]-x_edges[:-1])*0.5
63bar_left = x_edges[:-1]+bar_width
64
65integral_n = integrator.hist.hist.data()
66
67# Do some plotting
68fig = plt.figure()
69ax = plt.subplot(111)
70ax.minorticks_on()
71# ax.grid()
72ax.set_ylabel( 'f(x)' )
73ax.set_title(r'$a\,\sin(x)+b\,\sin(kx)$')
74
75# Draw the function and integrals
76fcn.sum.sum.plot_vs(int_points, 'o-', label='function at integration points', alpha=0.5, markerfacecolor='none', markersize=4.0)
77integrator.hist.hist.plot_bar(label='integral: numerical', divide=2, shift=0, alpha=0.6)
78ax.bar(bar_left, integral_a, bar_width, label='integral: analytical', align='edge', alpha=0.6)
79
80# Freeze axis limits and draw bin edges
81ax.autoscale(enable=True, axis='y')
82ymin, ymax = ax.get_ylim()
83ax.vlines(integrator.points.xedges.data(), ymin, ymax, linestyle='--', alpha=0.4, linewidth=0.5)
84
85ax.legend(loc='lower right')
86
87ymin, ymax = ax.get_ylim()
88
89# Save figure and graph as images
90savefig(tutorial_image_name('png', suffix='1'))
91
92# Do more plotting
93fig = plt.figure()
94ax = plt.subplot(111)
95ax.minorticks_on()
96# ax.grid()
97ax.set_ylabel( 'f(x)' )
98ax.set_title(r'$a\,\sin(x)+b\,\sin(kx)$')
99ax.autoscale(enable=True, axis='y')
100ax.set_ylim(ymin, ymax)
101ax.axhline(0.0, linestyle='--', color='black', linewidth=1.0, alpha=0.5)
102
103def plot_sample():
104 label = 'a={}, b={}, k={}'.format(*(p.value() for p in (pa,pb,pk)))
105 lines = fcn.sum.sum.plot_vs(int_points, '--', alpha=0.5, linewidth=1.0)
106 color = lines[0].get_color()
107 integrator.hist.hist.plot_hist(alpha=0.6, color=color, label=label)
108
109plot_sample()
110pa.set(-0.5)
111pk.set(16)
112plot_sample()
113pa.set(0)
114pb.set(0.1)
115plot_sample()
116
117# Freeze axis limits and draw bin edges
118ymin, ymax = ax.get_ylim()
119ax.vlines(integrator.points.xedges.data(), ymin, ymax, linestyle='--', alpha=0.4, linewidth=0.5)
120ax.legend(loc='lower right')
121
122plt.show()
First of all, instead of passing the bin edges as an argument we make a histogram first.
hist = C.Histogram(x_edges)
Usually we would like to have only one bins definition for the computational chain, thus, if several integrations occur, it is more convenient to provide bin edges via input.
integrator = R.IntegratorTrap(nbins, orders)
integrator.points.edges(hist.hist.hist)
Note that the integrator is created without bins passed as an argument. Only in this case the points.edges input is created. The variable orders now contains a numpy array with integers. Instead of Gauss-Legendre in this example we will use the trapezoidal integration.
Then we use the similar function as we have used in the previous example. Instead of binding it directly we use a method
add_input(output)
. This method may be used several times. On each execution it will create and bind a new input. A
new output is created for each input. Thus for the same bins and orders the transformation may calculate several
integrals within one execution.
integrator.add_input(fcn.sum.sum)
The method returns the corresponding output.
Note, that because of the taintflag propagation, even in case only one input is tainted, all the integrals will be recalculated. Multiple transformations should be used in case such a behaviour is not desired.
The code produces the following computational chain:
Then we repeat the same procedure with cross checking the calculation. The plot below illustrates the variable bin widths as well as variable integration orders.
In order to illustrate the impact of the parameters on the results let us create a function, that plots function (1), its integrals for the current values of the parameters:
def plot_sample():
label = 'a={}, b={}, k={}'.format(*(p.value() for p in (pa,pb,pk)))
lines = fcn.sum.sum.plot_vs(int_points, '--', alpha=0.5, linewidth=1.0)
color = lines[0].get_color()
integrator.hist.hist.plot_hist(alpha=0.6, color=color, label=label)
Now we make three plots for different values of the parameters:
plot_sample()
pa.set(-0.5)
pk.set(16)
plot_sample()
pa.set(0)
pb.set(0.1)
plot_sample()
The result is shown on the figure below: