Groovy Fun With Intregrals. Part 1 of n

Just for fun, I put together the code to calculate the area of a region with a curved boundary (Yes, I do things like this for fun). I figured I’d share it since it involves calculating a whole bunch of numbers and slapping them in a graph like throwing eggs at a wall…

As a programmer, I’m a lot more comfortable with algorithms expressed in code rather than a incomprehensible jumble of Greek symbols, but for the task of translating between the two I’ll use both here. The code in the example is of very little actual practical use as it stands, but it demonstrates the commonly encountered problem. Though I comment the code to a certain degree, I’m not going to teach anyone calculus, so don’t expect a full explanation.

We start with the simple function:
 \displaystyle y = f(x) = 1 - x^2

In Groovy, this is expressed as (note the use of ‘**’ to represent power):

1
1 - x**2

That being said, in the code example below, I use ‘^’ in the function variable to represent the exponent to maintain consistency (readability) between this and common mathematical notation in plain text (e.g. Like in common computer math programs like maxima, matlab, or a graphing calculator). In the actual calculation function, I use a String.replaceAll(replace, with) to switch the ‘^’ to ‘**’ prior to evaluating it.

Parameters

The boundaries for X will be 0 and 1
The number of subsections (n ): 100

Which results in the graph:
1 - x^2

And the sets


 C_1 \in [0, 1], C_2 \in [0.01, 0.9999], \ldots, C_{99} \in [0.975, 0.0494], C_{100} \in [0.985, 0.0298]



And the exciting integral used to calculate area:



 \displaystyle \sum_{k=0}^{n} f(C_k)*\Delta_k = \int_0^1  f(x)  dx  = \int_0^1 (1 - x^2)dx



OR…

 \displaystyle A \approx  f(c_1) * \Delta x + f(c_2) * \Delta x + \ldots + f(c_n) * \Delta x


Sooooo, how do we calculate the area?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
 
import groovy.swing.SwingBuilder
import java.awt.BorderLayout as BL
import javax.swing.*
import org.jfree.chart.ChartFactory
import org.jfree.chart.ChartPanel
import groovy.swing.SwingBuilder
import java.awt.*
import javax.swing.WindowConstants as WC
import org.jfree.data.category.DefaultCategoryDataset
import org.jfree.data.general.DefaultValueDataset
import org.jfree.chart.plot.PlotOrientation as Orientation
 
 
// Parameters
def function = "1 - x^2"
def maxX = 1.0F
def minX = 0.0F
def n = 100
def resolution = (maxX - minX) / n
 
// Evaluate the function y = f(x), returning y
def calcFunction(function, x) 
{
    // Groovy uses '**' for powers
    def function_tmp = function.replaceAll("\\^", "**")
    return Eval.x(x, function_tmp)
}
 
// Simple formatter for labels
def format(value)
{
    return String.format('%5.5f', value)
}
 
// Calculate the xy point sets
def x = minX
def xy = []
for (x = minX; x <= maxX; x+=resolution) {
    xy << [x:x, y:calcFunction(function,x)]
}
 
// Calculate area via upper sum
def upperSum = 0
for (def i = 0; i < (xy.size() - 1); i++) {
    def left = xy[i]
    def right = xy[i+1]
 
    upperSum += (left.y * (right.x - left.x)) 
}
 
// Calculate area via lower sum
def lowerSum = 0
for (def i = 0; i < (xy.size() - 1); i++) {
    def left = xy[i]
    def right = xy[i+1]
 
    lowerSum += (right.y * (right.x - left.x)) 
}
 
// Calculate area via midpoint rule
def midpointRule = 0
for (def i = 0; i < (xy.size() - 1); i++) {
    def left = xy[i]
    def right = xy[i+1]
    def dx =  (right.x - left.x)
    def midX = left.x + (dx / 2)
    def midY = calcFunction(function, midX)
 
    midpointRule += (midY * dx) 
}
 
// Add the values to the graph's dataset
def dataset = new DefaultCategoryDataset()
xy.each { point ->
    dataset.addValue(point.y, function, String.format('%1.3f', point.x))
}
 
// Build the graph & frame
def labels = [function, "X", "Y"]
def options = [true, true, true]
def chart = ChartFactory.createLineChart(*labels, dataset,
        Orientation.VERTICAL, *options)
def swing = new SwingBuilder()
def frame = swing.frame(title:'Function',
        defaultCloseOperation:WC.EXIT_ON_CLOSE) {
 
            panel(id:'canvas') { 
                borderLayout()
                widget(new ChartPanel(chart), constraints: BL.NORTH)
                textlabel = label(text:"Upper Sum: ${format(upperSum)}, Lower Sum: ${format(lowerSum)}, Midpoint Rule: ${format(midpointRule)}", constraints: BL.SOUTH)
            }
 
        }
frame.pack()
frame.show()
 
// Full values
println "X Limits: ${minX} - ${maxX}"
println "Y Limits: ${minY} - ${maxY}"
println "Upper Sum: ${upperSum}"
println "Midpoint Rule: ${midpointRule}"
println "Lower Sum: ${lowerSum}"
println "Resolution (delta): ${resolution}"
println "Number of points calculated: ${xy.size()}"

Result

The script results in the following dialog:

Screenshot

And the following console output:

1
2
3
4
5
6
X Limits: 0.0 - 1.0
Upper Sum: 0.6716499996658412
Midpoint Rule: 0.6666749999994406
Lower Sum: 0.6616500003363934
Resolution (delta): 0.01
Number of points calculated: 101

Leave a Reply