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.

$\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:

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:

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