sokobo
Loading...
Searching...
No Matches
calculus.cpp
1#include "include/calculus.h"
2#include "include/expression.h"
3#include "include/polynomial.h"
4#define _USE_MATH_DEFINES
5#include <cmath>
6#include <iostream>
7#include <random>
8#include <algorithm>
9#include <stdexcept>
10// #include <corecrt_math_defines.h>
11
12#define M_PI 3.14159265358979323846;
13
14// Symbolic differentiation
15std::shared_ptr<Expression> Calculus::differentiate(
16 std::shared_ptr<Expression> expr, const std::string& var)
17{
18 return partialDerivative(expr, var);
19}
20
21std::shared_ptr<Expression> Calculus::partialDerivative(
22 std::shared_ptr<Expression> expr, const std::string& var)
23{
24 if (!expr) {
25 return nullptr;
26 }
27
28 // Get the expression type
29Expression::Type
30 type = expr->getType();
31
32 switch (type) {
33 case Expression::Type::CONSTANT: {
34 // Derivative of constant is 0
35 return std::make_shared<Constant>(0.0);
36 }
37
38 case Expression::
39 Type::
40 VARIABLE: {
41 auto varExpr = std::dynamic_pointer_cast<Variable>(expr);
42 if (varExpr && varExpr->getName() == var) {
43 // d/dx (x) = 1
44 return std::make_shared<Constant>(1.0);
45 } else {
46 // d/dx (y) = 0 where y != x
47 return std::make_shared<Constant>(0.0);
48 }
49 }
50
51 case Expression::Type::BINARY_OP: {
52 auto binExpr = std::dynamic_pointer_cast<BinaryOp>(expr);
53 if (!binExpr) {
54 break;
55 }
56
57 auto left = binExpr->getLeft();
58 auto right = binExpr->getRight();
59 char op = binExpr->getOperator();
60
61 switch (op) {
62 case '+':
63 case '-': {
64 // d/dx (f � g) = df/dx � dg/dx
65 auto leftDeriv = partialDerivative(left, var);
66 auto rightDeriv = partialDerivative(right, var);
67 return std::make_shared<BinaryOp>(
68 leftDeriv, op, rightDeriv);
69 }
70
71 case '*': {
72 // Product rule: d/dx (f * g) = f' * g + f * g'
73 auto leftDeriv = partialDerivative(left, var);
74 auto rightDeriv = partialDerivative(right, var);
75
76 auto term1 = std::make_shared<BinaryOp>(leftDeriv, '*', right);
77 auto term2 = std::make_shared<BinaryOp>(left, '*', rightDeriv);
78
79 return std::make_shared<BinaryOp>(term1, '+', term2);
80 }
81
82 case '/': {
83 // Quotient rule: d/dx (f/g) = (f'*g - f*g') / g^2
84 auto leftDeriv = partialDerivative(left, var);
85 auto rightDeriv = partialDerivative(right, var);
86
87 auto numerator1 = std::make_shared<BinaryOp>(leftDeriv, '*', right);
88 auto numerator2 = std::make_shared<BinaryOp>(left, '*', rightDeriv);
89 auto numerator =
90 std::make_shared<BinaryOp>(numerator1, '-', numerator2);
91
92 auto denominator = std::make_shared<BinaryOp>(right, '*', right);
93
94 return std::make_shared<BinaryOp>(
95 numerator, '/', denominator);
96 }
97
98 case '^': {
99 // Power rule: d/dx (f^g) = f^g * (g'*ln(f) + g*f'/f)
100 // For simple case f^n where n is constant: d/dx (f^n) = n * f^(n-1) *
101 // f'
102
103 // Check if right side is constant
104 if (right->getType() == Expression::Type::CONSTANT) {
105 auto constExpr =
106 std::dynamic_pointer_cast<Constant>(right);
107 if (constExpr) {
108 double n = constExpr->getValue();
109 if (n == 0) {
110 return std::make_shared<Constant>(0.0);
111 }
112
113 auto leftDeriv = partialDerivative(left, var);
114 auto nMinus1 = std::make_shared<Constant>(n - 1);
115 auto nConst = std::make_shared<Constant>(n);
116
117 // n * f^(n-1) * f'
118 auto power = std::make_shared<BinaryOp>(left, '^', nMinus1);
119 auto temp = std::make_shared<BinaryOp>(nConst, '*', power);
120 return std::make_shared<BinaryOp>(temp, '*', leftDeriv);
121 }
122 }
123
124 // General case would require logarithmic differentiation
125 // For now, throw error for complex power rule cases
126 throw std::runtime_error(
127 "Complex power rule differentiation not implemented");
128 }
129 }
130 break;
131 }
132
133 case Expression::Type::
134 UNARY_OP: {
135 auto unaryExpr = std::dynamic_pointer_cast<UnaryOpExpression>(expr);
136 if (!unaryExpr) {
137 break;
138 }
139
140 auto operand = unaryExpr->getOperand();
141 char op = unaryExpr->getOperator();
142
143
144 switch (op) {
145 case '-': {
146 // d/dx (-f) = -df/dx
147 auto operandDeriv = partialDerivative(expr, var);
148 return std::make_shared<UnaryOpExpression>('-', operandDeriv);
149 }
150 case '+': {
151 // d/dx (+f) = df/dx
152 return partialDerivative(expr, var);
153 }
154 }
155 break;
156 }
157
158 case Expression::Type::
159 FUNCTION: {
160 auto funcExpr = std::dynamic_pointer_cast<Function>(expr);
161 if (!funcExpr) {
162 break;
163 }
164
165 std::string funcName = funcExpr->getFunctionName();
166 auto arg = funcExpr->getArgument();
167 auto argDeriv = partialDerivative(arg, var);
168
169 // Chain rule: d/dx f(g(x)) = f'(g(x)) * g'(x)
170 if (funcName == "sin") {
171 auto cosFunc = std::make_shared<Function>("cos", arg);
172 return std::make_shared<BinaryOp>(cosFunc, '*', argDeriv);
173 } else if (funcName == "cos") {
174 auto sinFunc = std::make_shared<Function>("sin", arg);
175 auto negSin = std::make_shared<UnaryOpExpression>('-', sinFunc);
176 return std::make_shared<BinaryOp>(negSin, '*', argDeriv);
177 } else if (funcName == "tan") {
178 auto cosFunc = std::make_shared<Function>("cos", arg);
179 auto cos2 = std::make_shared<BinaryOp>(cosFunc, '*', cosFunc);
180 auto one = std::make_shared<Constant>(1.0);
181 auto sec2 = std::make_shared<BinaryOp>(one, '/', cos2);
182 return std::make_shared<BinaryOp>(sec2, '*', argDeriv);
183 } else if (funcName == "ln" || funcName == "log") {
184 auto reciprocal = std::make_shared<BinaryOp>(
185 std::make_shared<Constant>(1.0), '/', arg);
186 return std::make_shared<BinaryOp>(reciprocal, '*', argDeriv);
187 } else if (funcName == "exp") {
188 auto expFunc = std::make_shared<Function>("exp", arg);
189 return std::make_shared<BinaryOp>(expFunc, '*', argDeriv);
190 } else if (funcName == "sqrt") {
191 // d/dx sqrt(f) = 1/(2*sqrt(f)) * f'
192 auto two = std::make_shared<Constant>(2.0);
193 auto sqrtFunc = std::make_shared<Function>("sqrt", arg);
194 auto denominator = std::make_shared<BinaryOp>(two, '*', sqrtFunc);
195 auto fraction = std::make_shared<BinaryOp>(
196 std::make_shared<Constant>(1.0), '/', denominator);
197 return std::make_shared<BinaryOp>(fraction, '*', argDeriv);
198 }
199
200 // For unknown functions, we can't differentiate
201 throw std::runtime_error("Differentiation of function '" + funcName
202 + "' not implemented");
203 }
204
205 default:
206 break;
207 }
208
209 throw std::runtime_error(
210 "Unable to differentiate expression of unknown type");
211}
212
213// Symbolic integration (limited cases)
214std::shared_ptr<Expression> Calculus::integrate(std::shared_ptr<Expression> expr, const std::string& var) {
215 // Similar to differentiation, this would need full expression analysis
216 // Would implement basic integration rules:
217 // - Power rule: x^n -> x^(n+1)/(n+1)
218 // - Sum rule: integral of sum = sum of integrals
219 // - Basic trigonometric and exponential integrals
220
221 throw std::runtime_error("Symbolic integration not fully implemented - requires complete expression tree structure");
222}
223
224// Numerical integration using trapezoidal rule
225double Calculus::trapezoidalRule(std::function<double(double)> f, double a, double b, int n) {
226 if (n <= 0) throw std::invalid_argument("Number of intervals must be positive");
227
228 double h = (b - a) / n;
229 double sum = 0.5 * (f(a) + f(b));
230
231 for (int i = 1; i < n; i++) {
232 sum += f(a + i * h);
233 }
234
235 return sum * h;
236}
237
238// Numerical integration using Simpson's rule
239double Calculus::simpsonsRule(std::function<double(double)> f, double a, double b, int n) {
240 if (n <= 0 || n % 2 != 0) throw std::invalid_argument("Number of intervals must be positive and even");
241
242 double h = (b - a) / n;
243 double sum = f(a) + f(b);
244
245 // Add odd-indexed terms (coefficient 4)
246 for (int i = 1; i < n; i += 2) {
247 sum += 4 * f(a + i * h);
248 }
249
250 // Add even-indexed terms (coefficient 2)
251 for (int i = 2; i < n; i += 2) {
252 sum += 2 * f(a + i * h);
253 }
254
255 return sum * h / 3.0;
256}
257
258// Gaussian quadrature integration
259double Calculus::gaussianQuadrature(std::function<double(double)> f, double a, double b, int n) {
260 // Gauss-Legendre quadrature points and weights for common values of n
261 static const std::vector<std::vector<double>> points = {
262 {}, // n=0 (unused)
263 {0.0}, // n=1
264 {-0.5773502691896257, 0.5773502691896257}, // n=2
265 {-0.7745966692414834, 0.0, 0.7745966692414834}, // n=3
266 {-0.8611363115940526, -0.3399810435848563, 0.3399810435848563, 0.8611363115940526}, // n=4
267 {-0.9061798459386640, -0.5384693101056831, 0.0, 0.5384693101056831, 0.9061798459386640} // n=5
268 };
269
270 static const std::vector<std::vector<double>> weights = {
271 {}, // n=0 (unused)
272 {2.0}, // n=1
273 {1.0, 1.0}, // n=2
274 {0.5555555555555556, 0.8888888888888888, 0.5555555555555556}, // n=3
275 {0.3478548451374538, 0.6521451548625461, 0.6521451548625461, 0.3478548451374538}, // n=4
276 {0.2369268850561891, 0.4786286704993665, 0.5688888888888889, 0.4786286704993665, 0.2369268850561891} // n=5
277 };
278
279 if (n < 1 || n > 5) {
280 throw std::invalid_argument("Gaussian quadrature only implemented for n=1 to 5");
281 }
282
283 double sum = 0.0;
284 double mid = (a + b) / 2.0;
285 double half_width = (b - a) / 2.0;
286
287 for (int i = 0; i < n; i++) {
288 double x = mid + half_width * points[n][i];
289 sum += weights[n][i] * f(x);
290 }
291
292 return half_width * sum;
293}
294
295// Monte Carlo integration
296double Calculus::monteCarloIntegration(std::function<double(double)> f, double a, double b, int samples) {
297 if (samples <= 0) throw std::invalid_argument("Number of samples must be positive");
298
299 std::random_device rd;
300 std::mt19937 gen(rd());
301 std::uniform_real_distribution<double> dis(a, b);
302
303 double sum = 0.0;
304 for (int i = 0; i < samples; i++) {
305 double x = dis(gen);
306 sum += f(x);
307 }
308
309 return (b - a) * sum / samples;
310}
311
312// Double integral using nested trapezoidal rule
313double Calculus::doubleIntegral(std::function<double(double, double)> f,
314 double x1, double x2, double y1, double y2, int nx, int ny) {
315 if (nx <= 0 || ny <= 0) throw std::invalid_argument("Number of intervals must be positive");
316
317 double hx = (x2 - x1) / nx;
318 double hy = (y2 - y1) / ny;
319 double sum = 0.0;
320
321 // Apply trapezoidal rule in both dimensions
322 for (int i = 0; i <= nx; i++) {
323 double x = x1 + i * hx;
324 double weight_x = (i == 0 || i == nx) ? 0.5 : 1.0;
325
326 for (int j = 0; j <= ny; j++) {
327 double y = y1 + j * hy;
328 double weight_y = (j == 0 || j == ny) ? 0.5 : 1.0;
329
330 sum += weight_x * weight_y * f(x, y);
331 }
332 }
333
334 return sum * hx * hy;
335}
336
337// Taylor series expansion
338Polynomial Calculus::taylorSeries(std::function<double(double)> f, double center, int degree) {
339 if (degree < 0) throw std::invalid_argument("Degree must be non-negative");
340
341 std::vector<double> coefficients(degree + 1);
342 double h = 1e-8; // Small step for numerical differentiation
343
344 // Calculate derivatives numerically and build Taylor series
345 coefficients[0] = f(center); // f(center)
346
347 for (int n = 1; n <= degree; n++) {
348 // Calculate nth derivative using finite differences
349 double derivative = 0.0;
350
351 // Use central difference formula for higher accuracy
352 if (n == 1) {
353 derivative = (f(center + h) - f(center - h)) / (2.0 * h);
354 } else {
355 // For higher derivatives, use recursive finite differences
356 // This is a simplified approach - more sophisticated methods exist
357 std::function<double(double)> fn = f;
358 for (int i = 0; i < n; i++) {
359 auto prev_fn = fn;
360 fn = [prev_fn, h](double x) {
361 return (prev_fn(x + h) - prev_fn(x - h)) / (2.0 * h);
362 };
363 }
364 derivative = fn(center);
365 }
366
367 // Calculate factorial
368 double factorial = 1.0;
369 for (int i = 1; i <= n; i++) {
370 factorial *= i;
371 }
372
373 coefficients[n] = derivative / factorial;
374 }
375
376 return Polynomial(coefficients);
377}
378
379// Chebyshev series expansion
380Polynomial Calculus::chebyshevSeries(std::function<double(double)> f, double a, double b, int degree) {
381 if (degree < 0) throw std::invalid_argument("Degree must be non-negative");
382
383 std::vector<double> coefficients(degree + 1, 0.0);
384
385 // Transform interval [a,b] to [-1,1] for Chebyshev polynomials
386 auto transform = [a, b](double t) { return 0.5 * ((b - a) * t + a + b); };
387
388 // Calculate Chebyshev coefficients
389 for (int k = 0; k <= degree; k++) {
390 double sum = 0.0;
391 int n_points = std::max(degree + 1, 50); // Use enough points for accuracy
392
393 for (int j = 0; j < n_points; j++) {
394 double t = std::cos(std::acos(-1.0) * (j + 0.5) / n_points); // Chebyshev points
395 double x = transform(t);
396 double T_k = 1.0; // T_0(t) = 1
397
398 if (k == 1) {
399 T_k = t; // T_1(t) = t
400 } else if (k > 1) {
401 // Use recurrence relation: T_{k+1}(t) = 2t*T_k(t) - T_{k-1}(t)
402 double T_prev = 1.0; // T_0
403 double T_curr = t; // T_1
404
405 for (int m = 2; m <= k; m++) {
406 double T_next = 2.0 * t * T_curr - T_prev;
407 T_prev = T_curr;
408 T_curr = T_next;
409 }
410 T_k = T_curr;
411 }
412
413 sum += f(x) * T_k;
414 }
415
416 coefficients[k] = (k == 0 ? 1.0 : 2.0) * sum / n_points;
417 }
418
419 // Convert Chebyshev coefficients to standard polynomial form
420 // This is a simplified conversion - full implementation would use proper basis transformation
421 return Polynomial(coefficients);
422}
423
424// Numerical limit calculation
425double Calculus::limit(std::function<double(double)> f, double x, double h) {
426 if (h <= 0) throw std::invalid_argument("Step size must be positive");
427
428 // Approach the limit from both sides
429 double left_limit = f(x - h);
430 double right_limit = f(x + h);
431
432 // Refine the estimate by reducing h
433 double prev_avg = (left_limit + right_limit) / 2.0;
434
435 for (int i = 0; i < 10; i++) { // Iterate to improve accuracy
436 h *= 0.1;
437 left_limit = f(x - h);
438 right_limit = f(x + h);
439 double current_avg = (left_limit + right_limit) / 2.0;
440
441 // Check for convergence
442 if (std::abs(current_avg - prev_avg) < 1e-12) {
443 break;
444 }
445 prev_avg = current_avg;
446 }
447
448 return prev_avg;
449}