Coverage for /home/runner/work/AutoDiff/AutoDiff/autodiff/dual.py: 100%
Generated by Amelia Li for AutoDiff. (GitHub Profile)
92 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 04:22 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 04:22 +0000
1# File : dual.py
2# Description: Dual class to store dual numbers and overload operators and
3# functions to perform basic artihmetic
4import numpy as np
6class Dual():
7 """Dual number implementation to perform basic arithmetic and geometric operations."""
9 _supported_scalars = (int, float)
11 def __init__(self, real, dual = 1.0):
12 """
13 Initialize a dual number based on inputs 'real' and 'dual'.
15 Parameters
16 ----------
17 real : integer or float
18 Input number to initialize the real part of a dual number.
20 dual : integer or float
21 Input number to initialize the dual part of a dual number.
23 """
24 self.real = real
25 self.dual = dual
27 ### Elementary Functions ###
28 def __add__(self, other):
29 """
30 Compute the addition of two dual numbers or of one dual number and one real number.
32 Parameters
33 ----------
34 other : Dual, Scalar
35 Input number which is added to a dual number.
37 Returns
38 -------
39 Dual
40 The method returns the value of adding two dual numbers or one dual number and one real number.
42 Raises
43 ------
44 TypeError
45 This method raises a `TypeError` if the type of input number 'other' is not supported.
47 """
48 # check if other is of a supported type
49 if not isinstance(other, (*self._supported_scalars, Dual)):
50 raise TypeError(f"Unsupported type '{type(other)}'")
51 if isinstance(other, self._supported_scalars):
52 # scalar
53 return Dual(other + self.real, self.dual)
54 else:
55 # dual
56 return Dual(self.real + other.real, self.dual + other.dual)
58 def __radd__(self, other):
59 """
60 Overload the addition function to compute the addition of two real numbers.
62 Parameters
63 ----------
64 other : Scalar
65 Input number which is added to a real number.
67 Returns
68 -------
69 Dual
70 The method returns the value of adding two real numbers.
72 """
73 return self.__add__(other)
75 def __sub__(self, other):
76 """
77 Compute the subtraction of 'other' dual number from 'self' dual number.
79 Parameters
80 ----------
81 other : Dual, Scalar
82 Input number which is subtracted from a dual number.
84 Returns
85 -------
86 Dual
87 The method returns the resulting value of subtaction.
89 Raises
90 ------
91 TypeError
92 This method raises a `TypeError` if the type of input number other is not supported.
94 """
95 # check if other is of a supported type
96 if not isinstance(other, (*self._supported_scalars, Dual)):
97 raise TypeError(f"Unsupported type '{type(other)}'")
98 if isinstance(other, self._supported_scalars):
99 # scalar
100 return Dual(self.real - other, self.dual)
101 else:
102 # dual
103 return Dual(self.real - other.real, self.dual - other.dual)
105 def __rsub__(self, other):
106 """
107 Compute the subtraction of 'self' dual number from 'other' dual number.
109 Parameters
110 ----------
111 other : Dual, Scalar
112 Input number which is subtracted by a dual number.
114 Returns
115 -------
116 Dual
117 The method returns the resulting value of subtaction.
119 """
120 return -self + other
122 def __mul__(self, other):
123 """
124 Compute the multiplication of two dual numbers or of one dual number and one real number.
126 Parameters
127 ----------
128 other : Dual, Scalar
129 Input number which is multiplied by a dual number.
131 Returns
132 -------
133 Dual
134 The method returns the value of multiplying two dual numbers or of one dual number and one real number.
136 Raises
137 ------
138 TypeError
139 This method raises a `TypeError` if the type of input number other is not supported.
141 """
142 # check if other is of a supported type
143 if not isinstance(other, (*self._supported_scalars, Dual)):
144 raise TypeError(f"Unsupported type '{type(other)}'")
145 if isinstance(other, self._supported_scalars):
146 # scalar
147 return Dual(other * self.real, other * self.dual)
148 else:
149 # dual
150 real = self.real * other.real
151 dual = self.real * other.dual + self.dual * other.real
152 return Dual(real, dual)
154 def __rmul__(self, other):
155 """
156 Overload the multiplication function to compute the multiplication of two real numbers.
158 Parameters
159 ----------
160 other : Scalar
161 Input number which is multiplied by a real number.
163 Returns
164 -------
165 Dual
166 The method returns the value of multiplying two real numbers.
168 """
169 return self.__mul__(other)
171 def __truediv__(self, other):
172 """
173 Compute the division of 'self' dual number by 'other' dual number.
175 Parameters
176 ----------
177 other : Dual, Scalar
178 Input number which divides a dual number.
180 Returns
181 -------
182 Dual
183 The method returns the value of the division.
185 """
186 return self * other ** (-1)
188 def __rtruediv__(self, other):
189 """
190 Overload the division function to compute the divition of one real number by another real number.
192 Parameters
193 ----------
194 other : Scalar
195 Input number which divides a real number.
197 Returns
198 -------
199 Dual
200 The method returns the value of dividing one real number by another real number.
202 """
203 return other * self ** (-1)
205 def __neg__(self):
206 """
207 Compute the negation of one dual number.
209 Returns
210 -------
211 Dual
212 The method returns the value of the negation of one dual number.
214 """
215 return Dual(-self.real, -self.dual)
217 def __pow__(self, other):
218 """
219 Compute the exponentiation of raising one dual number to the power of another dual number or of one real number.
221 Parameters
222 ----------
223 other : Dual, Scalar
224 Input exponent to which the base dual number will be raised.
226 Returns
227 -------
228 Dual
229 The method returns the value of raising one dual number to the power of another dual number or of one real number.
231 Raises
232 ------
233 TypeError
234 This method raises a `TypeError` if the type of input exponent other is not supported.
236 """
237 # check if other is of supported type
238 if not isinstance(other, (*self._supported_scalars, Dual)):
239 raise TypeError(f"Unsupported type '{type(other)}'")
240 if isinstance(other, self._supported_scalars):
241 # scalar
242 real = self.real ** other
243 dual = self.dual * other * (self.real ** (other - 1))
244 return Dual(real, dual)
245 else:
246 # dual
247 real = self.real ** other.real
248 dual = other.real * self.dual * self.real ** (other.real - 1) + np.log(self.real) * other.dual * self.real ** other.real
249 return Dual(real, dual)
251 def __rpow__(self, other):
252 """
253 Overload the exponentiation function to compute the exponentiation of raising a real number to the power of another real
254 number.
256 Parameters
257 ----------
258 other : Scalar
259 Input exponent to which the base real number will be raised.
261 Returns
262 -------
263 Dual
264 The method returns the value of raising one real number to the power of another real number.
266 Raises
267 ------
268 TypeError
269 This method raises a `TypeError` if the type of input exponent other is not supported.
271 """
272 # check if other is of supported type
273 if not isinstance(other, self._supported_scalars):
274 raise TypeError(f"Unsupported type '{type(other)}'")
275 real = other ** self.real
276 dual = (other ** self.real) * np.log(other) * self.dual
277 return Dual(real, dual)
279 ### Square Root Function ###
280 def sqrt(self):
281 """
282 Compute the square root of one dual number.
284 Returns
285 -------
286 Dual
287 The method returns the square root of one dual number.
289 Raises
290 ------
291 ValueError
292 This method raises a `ValueError` if the value of input dual number is less than zero.
294 """
295 # check that the real component of the dual number is greater than or equal to 0.
296 if self.real < 0:
297 raise ValueError("Cannot square root: real part of dual number is lesser than 0.")
298 return Dual(self.real ** (1/2), self.dual * ((1/2) * (self.real ** (-1/2))))
300 ### Exponential Function ###
301 def exp(self):
302 """
303 Compute the exponentiation of raising the natural number to the power of one dual number.
305 Returns
306 -------
307 Dual
308 The method returns the value of raising the natural number to the power of one dual number.
310 """
311 return Dual(np.exp(self.real), np.exp(self.real) * self.dual)
313 ### Logarithmic Function ###
314 def log(self, base):
315 """
316 Compute the logarithm to find the power to which the input base must be raised to yield the given dual number.
318 Parameters
319 ----------
320 base : Dual, Scalar
321 Input base which is raised to yield a given dual number.
323 Returns
324 -------
325 Dual
326 The method returns the value of the exponent to which the input base must be raised to yield the given dual number.
328 Raises
329 ------
330 TypeError
331 This method raises a `TypeError` if the type of input base number is not supported.
333 ValueError
334 This method raises a `ValueError` if the real part of the dual number or the value of input base is less than zero.
336 """
337 # check if base is of supported type
338 if not isinstance(base, self._supported_scalars):
339 raise TypeError(f"Unsupported base type '{type(base)}'")
340 # check that the base is above 0.
341 if base <= 0:
342 raise ValueError("Cannot log: Base is lesser than or equal to 0.")
343 # check that the real component of the dual number is above 0.
344 if self.real <= 0:
345 raise ValueError("Cannot log: Real part of the dual number is lesser than or equal to 0.")
346 return Dual(np.log(self.real) / np.log(base), 1 / (np.log(base) * self.real) * self.dual)
348 ### Logistic Function ###
349 def standard_logistic(self):
350 """
351 Compute the value of the standard logistic function with the given dual number as input parameter.
353 Returns
354 -------
355 Dual
356 The method returns the value of the standard logistic function with the given dual number as input parameter.
358 """
359 return 1 / (1 + Dual.exp(-self))
361 ### Trigonometric Functions ###
362 def sin(self):
363 """
364 Compute the value of the sine function with the given dual number as input parameter.
366 Returns
367 -------
368 Dual
369 The method returns the value of the sine function with the given dual number as input parameter.
371 """
372 return Dual(np.sin(self.real), np.cos(self.real) * self.dual)
374 def cos(self):
375 """
376 Compute the value of the cosine function with the given dual number as input parameter.
378 Returns
379 -------
380 Dual
381 The method returns the value of the cosine function with the given dual number as input parameter.
383 """
384 return Dual(np.cos(self.real), np.sin(self.real) * -self.dual)
386 def tan(self):
387 """
388 Compute the value of the tangent function with the given dual number as input parameter.
390 Returns
391 -------
392 Dual
393 The method returns the value of the tangent function with the given dual number as input parameter.
395 """
396 return Dual(np.tan(self.real), self.dual / (np.cos(self.real) ** 2))
398 ### Inverse Trigonometric Functions ###
399 def arcsin(self):
400 """
401 Compute the value of the arcsine function with the given dual number as input parameter.
403 Returns
404 -------
405 Dual
406 The method returns the value of the arcsine function with the given dual number as input parameter.
408 Raises
409 ------
410 ValueError
411 This method raises a `ValueError` if the real part of the dual number is smaller than -1 or greater than 1.
413 """
414 # check that the real component of the dual number is between -1 and 1.
415 if self.real >= 1 or self.real <= -1:
416 raise ValueError("Cannot arcsin: Real part of dual number is not between -1 and 1.")
417 return Dual(np.arcsin(self.real), self.dual / np.sqrt(1 - self.real ** 2))
419 def arccos(self):
420 """
421 Compute the value of the arccosine function with the given dual number as input parameter.
423 Returns
424 -------
425 Dual
426 The method returns the value of the arccosine function with the given dual number as input parameter.
428 Raises
429 ------
430 ValueError
431 This method raises a `ValueError` if the real part of the dual number is smaller than -1 or greater than 1.
433 """
434 # check that the real component of the dual number is between -1 and 1.
435 if self.real >= 1 or self.real <= -1:
436 raise ValueError("Cannot arccos: Real part of dual number is not between -1 and 1.")
437 return Dual(np.arccos(self.real), - self.dual / np.sqrt(1 - self.real ** 2))
439 def arctan(self):
440 """
441 Compute the value of the arctangent function with the given dual number as input parameter.
443 Returns
444 -------
445 Dual
446 The method returns the value of the arctangent function with the given dual number as input parameter.
448 """
449 return Dual(np.arctan(self.real), self.dual / ((self.real ** 2) + 1))
451 ### Hyperbolic Functions ###
452 def sinh(self):
453 """
454 Compute the value of the hyperbolic sine function with the given dual number as input parameter.
456 Returns
457 -------
458 Dual
459 The method returns the value of the hyperbolic sine function with the given dual number as input parameter.
461 """
462 return (self.exp() - (Dual.exp(-self))) / 2
464 def cosh(self):
465 """
466 Compute the value of the hyperbolic cosine function with the given dual number as input parameter.
468 Returns
469 -------
470 Dual
471 The method returns the value of the hyperbolic cosine function with the given dual number as input parameter.
473 """
474 return (self.exp() + (Dual.exp(-self))) / 2
476 def tanh(self):
477 """
478 Compute the value of the hyperbolic tangent function with the given dual number as input parameter.
480 Returns
481 -------
482 Dual
483 The method returns the value of the hyperbolic tangent function with the given dual number as input parameter.
485 """
486 return self.sinh() / self.cosh()