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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
|
package bisection;
import java.util.function.DoubleUnaryOperator;
/*
* Benjamin Culkin
* 1/16/2018
* CS 479
* Bisection
*
* Use the bisection method to find bracketed roots of arbitrary real-valued functions.
*/
public class NeoBisection {
/**
* Maximum number of iterations to attempt.
*/
public static final int MAXITR = 500;
public static void main(String[] args) {
/* Bisection Method. */
/* The functions we're approximating a root for. */
DoubleUnaryOperator functionA = x -> Math.cos(x) - x;
DoubleUnaryOperator functionB = x -> {
double valA = Math.pow(x, 5);
double valB = 3 * Math.pow(x, 4);
double valC = 4 * Math.pow(x, 3);
return valA - valB + valC - 1;
};
DoubleUnaryOperator functionC = x -> (x * x) - 3;
/* The approximated roots. */
double answerA = bisect(functionA, 0, 1, 0.0001);
double answerB = bisect(functionB, 0, 1, 0.0001);
double answerC = bisect(functionC, 1, 2, 0.0000001);
/* Print out the answers + their function. */
System.out.printf("Bisection Method:\n");
System.out.printf("\tx = cos(x) => %.4f\n", answerA);
System.out.printf("\tx^5 - 3x^4 + 4x^2 - 1 => %.4f\n", answerB);
System.out.printf("\tx^2 - 3 => %.7f\n\n", answerC);
/* Newton's / Secant Method. */
/* The variable to manipulate in the expressions. */
Dual varX = new Dual();
DualExpr varXExpr = new DualExpr(varX);
/* The functions to find the roots of. */
DualExpr dualA = new DualExpr(DualExpr.ExprType.SUBTRACTION,
new DualExpr(DualExpr.ExprType.COS, varXExpr), varXExpr);
DualExpr dualB;
{
/* Construct the second function. */
DualExpr tempA = new DualExpr(varXExpr, 5);
DualExpr tempB = new DualExpr(DualExpr.ExprType.MULTIPLICATION, new DualExpr(new Dual(3)),
new DualExpr(varXExpr, 4));
DualExpr tempC = new DualExpr(DualExpr.ExprType.MULTIPLICATION, new DualExpr(new Dual(4)),
new DualExpr(varXExpr, 3));
dualB = new DualExpr(DualExpr.ExprType.ADDITION,
new DualExpr(DualExpr.ExprType.SUBTRACTION, tempA, tempB),
new DualExpr(DualExpr.ExprType.SUBTRACTION, tempC, new DualExpr(new Dual(1))));
}
DualExpr dualC = new DualExpr(DualExpr.ExprType.SUBTRACTION, new DualExpr(varXExpr, 2),
new DualExpr(new Dual(3)));
/* Print out dualized expressions. */
System.out.printf("Expressions:\n");
System.out.printf("\t%s\n", dualA);
System.out.printf("\t%s\n", dualB);
System.out.printf("\t%s\n", dualC);
/* The approximated roots, using Newton's method. */
double newtonA = newton(dualA, varX, 0, 1, 0.0001);
double newtonB = newton(dualB, varX, 0, 1, 0.0001);
double newtonC = newton(dualC, varX, 1, 2, 0.0000001);
/* Print out the answers + their function. */
System.out.printf("Newton's Method:\n");
System.out.printf("\tx = cos(x) => %.4f\n", newtonA);
System.out.printf("\tx^5 - 3x^4 + 4x^2 - 1 => %.4f\n", newtonB);
System.out.printf("\tx^2 - 3 => %.7f\n\n", newtonC);
/* The approximated roots, using the secant method. */
double secantA = secant(dualA, varX, 0, 1, 0.0001);
double secantB = secant(dualB, varX, 0, 1, 0.0001);
double secantC = secant(dualC, varX, 1, 2, 0.0000001);
/* Print out the answers + their function. */
System.out.printf("Secant Method:\n");
System.out.printf("\tx = cos(x) => %.4f\n", secantA);
System.out.printf("\tx^5 - 3x^4 + 4x^2 - 1 => %.4f\n", secantB);
System.out.printf("\tx^2 - 3 => %.7f\n", secantC);
}
/*
* Calculate the number of iterations to approximate to a specified tolerance.
*/
private static double iterCount(double a, double b, double tol) {
double inside = Math.log((b - a) / tol);
return Math.ceil(inside / Math.log(2));
}
/* Calculate a root for an equation by the bisection method. */
private static double bisect(DoubleUnaryOperator func, double a, double b, double tol) {
/* Calculate the number of iterations. */
double N = iterCount(a, b, tol);
double val = 0.0;
/* Set our values. */
double newa = a;
double newb = b;
double newc = (a + b) / 2;
/*
* Continue onwards until we get the approximation to within the right
* tolerance.
*/
for (int i = 0; i < N; i++) {
newc = (newa + newb) / 2;
val = func.applyAsDouble(newc);
/* Pick the right direction to bisect in. */
if (func.applyAsDouble(newa) * val < 0) {
newb = newc;
} else {
newa = newc;
}
}
/* Return the right value. */
return newc;
}
/**
* Use Newton's method to find the root of an equation.
*
* @param func
* The function to find a root for.
*
* @param var
* The variable in the function.
*
* @param lo
* The lower bound for the root
*
* @param hi
* The higher bound for the root
*
* @param tol
* The tolerance for the answer
*
* @return The estimated value for the equation.
*/
public static double newton(DualExpr func, Dual var, double lo, double hi, double tol) {
/* Initial guess for root. */
double newmid = (lo + hi) / 2;
for (int i = 0; i < MAXITR; i++) {
/* Previous root guess. */
double prevmid = newmid;
/* Set the variable properly. */
var.real = newmid;
var.dual = 1;
/* Evaluate the function and its derivative. */
Dual res = func.evaluate();
/* Use Newton's method to refine our solution. */
newmid = newmid - (res.real / res.dual);
System.out.printf("\tTRACE: prevmid: %f\t\tnewmid: %f\n", prevmid, newmid);
/* Hand back the solution if it's good enough. */
if (Math.abs(newmid - prevmid) < tol) {
return newmid;
}
}
System.out.println("Newton's method iteration limit reached.");
/* Give back the solution. */
return newmid;
}
public static double secant(DualExpr func, Dual var, double lo, double hi, double tol) {
/* Initial guesses for root. */
double guess1 = (lo + hi) / 3; // 1/3 into the range
double guess2 = ((lo + hi) / 3) * 2; // 2/3 into the range
for (int i = 0; i < MAXITR; i++) {
var.real = guess1;
var.dual = 1;
/* Evaluate the first guess. */
Dual res1 = func.evaluate();
var.real = guess2;
var.dual = 1;
/* Evaluate the first guess. */
Dual res2 = func.evaluate();
double oldGuess1 = guess1;
/* Use the secant method to refine our guesses. */
guess1 = guess2;
guess2 = ((oldGuess1 * res2.real) - (guess1 * res1.real)) / (res1.real - res2.real);
System.out.printf("\tTRACE: guess1: %f\t\tguess2: %f\n", guess1, guess2);
/* Hand back the solution if it's good enough. */
if (Math.abs(guess1 - guess2) < tol) {
return guess2;
}
}
System.out.println("Secant method iteration limit reached.");
/* Give back the solution. */
return guess2;
}
/**
* Represents a 'dual' number.
*
* Think imaginary numbers, where instead of i, we add a value d such that d^2 =
* 0.
*/
public static class Dual {
/**
* The real part of the dual number.
*/
public double real;
/**
* The dual part of the dual number.
*/
public double dual;
/**
* Create a new dual with both parts zero.
*/
public Dual() {
real = 0;
dual = 0;
}
/**
* Create a new dual number with a zero dual part.
*
* @param real
* The real part of the number.
*/
public Dual(double real) {
this.real = real;
this.dual = 0;
}
/**
* Create a new dual number with a specified dual part.
*
* @param real
* The real part of the number.
* @param dual
* The dual part of the number.
*/
public Dual(double real, double dual) {
this.real = real;
this.dual = dual;
}
@Override
public String toString() {
return String.format("<%f, %f>", real, dual);
}
}
/**
* Represents an expression using dual numbers.
*
* Useful for automatically differentiating expressions.
*/
public static class DualExpr {
/**
* Represents the various types of dual expressions.
*/
public static enum ExprType {
/**
* A fixed number.
*/
CONSTANT,
/**
* An addition operation.
*/
ADDITION,
/**
* A subtraction operation.
*/
SUBTRACTION,
/**
* A multiplication operation.
*/
MULTIPLICATION,
/**
* A division operation.
*/
DIVISION,
/**
* A sine operation.
*/
SIN,
/**
* A cosine operation.
*/
COS,
/**
* An exponential function.
*/
EXPONENTIAL,
/**
* A logarithm function.
*/
LOGARITHM,
/**
* A power operation.
*/
POWER,
/**
* An absolute value.
*/
ABSOLUTE
}
/**
* The type of the expression.
*/
public final ExprType type;
/**
* The dual number value, for constants.
*/
public Dual number;
/**
* The left (or first) part of the expression.
*/
public DualExpr left;
/**
* The right (or second) part of the expression.
*/
public DualExpr right;
/**
* The power to use, for power operations.
*/
public int power;
/**
* Create a new constant dual number.
*
* @param num
* The value of the dual number.
*/
public DualExpr(Dual num) {
this.type = ExprType.CONSTANT;
number = num;
}
/**
* Create a new unary dual number.
*
* @param type
* The type of operation to perform.
* @param val
* The parameter to the value.
*/
public DualExpr(ExprType type, DualExpr val) {
this.type = type;
left = val;
}
/**
* Create a new binary dual number.
*
* @param type
* The type of operation to perform.
* @param val
* The parameter to the value.
*/
public DualExpr(ExprType type, DualExpr left, DualExpr right) {
this.type = type;
this.left = left;
this.right = right;
}
/**
* Create a new power expression.
*
* @param left
* The expression to raise.
* @param power
* The power to raise it by.
*/
public DualExpr(DualExpr left, int power) {
this.type = ExprType.POWER;
this.left = left;
this.power = power;
}
/**
* Evaluate an expression to a number.
*
* Uses the rules provided in
* https://en.wikipedia.org/wiki/Automatic_differentiation
*
* @return The evaluated expression.
*/
public Dual evaluate() {
/* The evaluated dual numbers. */
Dual lval, rval;
/* Perform the right operation for each type. */
switch (type) {
case CONSTANT:
return number;
case ADDITION:
lval = left.evaluate();
rval = right.evaluate();
return new Dual(lval.real + rval.real, lval.dual + rval.dual);
case SUBTRACTION:
lval = left.evaluate();
rval = right.evaluate();
return new Dual(lval.real - rval.real, lval.real - rval.real);
case MULTIPLICATION:
lval = left.evaluate();
rval = right.evaluate();
{
double lft = lval.dual * rval.real;
double rght = lval.real * rval.dual;
return new Dual(lval.real * rval.real, lft + rght);
}
case DIVISION:
lval = left.evaluate();
rval = right.evaluate();
{
if (rval.real == 0) {
throw new IllegalArgumentException("ERROR: Attempted to divide by zero.");
}
double lft = lval.dual * rval.real;
double rght = lval.real * rval.dual;
double val = (lft - rght) / (rval.real * rval.real);
return new Dual(lval.real / rval.real, val);
}
case SIN:
lval = left.evaluate();
return new Dual(Math.sin(lval.real), lval.dual * Math.cos(lval.real));
case COS:
lval = left.evaluate();
return new Dual(Math.cos(lval.real), -lval.dual * Math.sin(lval.real));
case EXPONENTIAL:
lval = left.evaluate();
{
double val = Math.exp(lval.real);
return new Dual(val, lval.dual * val);
}
case LOGARITHM:
lval = left.evaluate();
if (lval.real <= 0) {
throw new IllegalArgumentException(
"ERROR: Attempted to take non-positive log.");
}
return new Dual(Math.log(lval.real), lval.dual / lval.real);
case POWER:
lval = left.evaluate();
if (lval.real == 0) {
throw new IllegalArgumentException("ERROR: Raising zero to a power.");
}
{
double rl = Math.pow(lval.real, power);
double lft = Math.pow(lval.real, power - 1);
return new Dual(rl, power * lft * lval.dual);
}
case ABSOLUTE:
lval = left.evaluate();
return new Dual(Math.abs(lval.real), lval.dual * Math.signum(lval.real));
default:
String msg = "ERROR: Unknown expression type %s";
throw new IllegalArgumentException(String.format(msg, type));
}
}
@Override
public String toString() {
switch (type) {
case ABSOLUTE:
return String.format("abs(%s)", left.toString());
case ADDITION:
return String.format("(%s + %s)", left.toString(), right.toString());
case CONSTANT:
return String.format("%s", number.toString());
case COS:
return String.format("cos(%s)", left.toString());
case DIVISION:
return String.format("(%s / %s)", left.toString(), right.toString());
case EXPONENTIAL:
return String.format("exp(%s)", left.toString());
case LOGARITHM:
return String.format("log(%s)", left.toString());
case MULTIPLICATION:
return String.format("(%s * %s)", left.toString(), right.toString());
case POWER:
return String.format("(%s ^ %d)", left.toString(), power);
case SIN:
return String.format("sin(%s)", left.toString());
case SUBTRACTION:
return String.format("(%s - %s)", left.toString(), right.toString());
default:
return String.format("UNKNOWN_EXPR");
}
}
}
}
|