123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942 |
- # taylor_expans_abstract.py: abstract class for Taylor expansions
- # Copyright 2022 Romain Serra
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from functools import lru_cache
- from abc import ABCMeta, abstractmethod
- from typing import List, Union, Iterable, Tuple, Callable, Optional
- import numpy as np
- from swiftt.algebraic_abstract import AlgebraicAbstract
- from swiftt.taylor.tables import algebra_dim
- TaylorOrScalar = Union["TaylorExpansAbstract", complex, float]
- Scalar1OrND = Union[complex, float, Iterable[complex], Iterable[float]]
- default_unknown_name = "x"
- default_inverse_name = "y"
- landau_symbol = "o"
- class TaylorExpansAbstract(AlgebraicAbstract, metaclass=ABCMeta):
- """Abstract class for all objects of a Taylor differential algebra.
- Attributes:
- _n_var (int): number of variable(s) in algebra.
- _order (int): order of algebra.
- _dim_alg (int): dimension of algebra i.e. number of monomials in the polynomial part of an expansion.
- _var_type (type): type of variable(s).
- _var_names (List[str]): name of variable(s). They are here only for printing purposes: their consistency is
- *not* checked when performing operations on Taylor expansions, for example "x+o(x)"+"y+o(y)"="2x+o(x)".
- """
- # pre-declared intrinsic functions for scalars
- _exp_cst: Callable
- _log_cst: Callable
- _sqrt_cst: Callable
- _cos_cst: Callable
- _sin_cst: Callable
- _tan_cst: Callable
- _cosh_cst: Callable
- _sinh_cst: Callable
- _tanh_cst: Callable
- def __init__(self, n_var: int, order: int, var_type: type, var_names: List[str]) -> None:
- """Constructor for TaylorExpansAbstract class.
- Args:
- n_var (int): number of variable(s) in algebra.
- order (int): order of algebra.
- var_type (type): type of variable(s).
- var_names (List[str]): name of variable(s).
- """
- self._n_var = n_var
- self._order = order
- self._var_names = var_names
- self._var_type = var_type
- self._dim_alg = algebra_dim(n_var, order)
- self._coeff: np.ndarray = None
- @property
- def dim_alg(self) -> int:
- """Getter for dimension of algebra.
- Returns:
- int: dimension of algebra.
- """
- return self._dim_alg
- @property
- def order(self) -> int:
- """Getter for order of algebra.
- Returns:
- int: order of algebra.
- """
- return self._order
- @property
- def n_var(self) -> int:
- """Getter for number of variables in algebra.
- Returns:
- int: number of variables.
- """
- return self._n_var
- @property
- def var_type(self) -> type:
- """
- Getter for the type of variables.
- Returns:
- type: type of variables
- """
- return self._var_type
- @property
- def var_names(self) -> List[str]:
- """Getter for names of variables in algebra. They are implemented in a lazy fashion so if not defined before,
- the names are arbitrary.
- Returns:
- List[str]: names of variables.
- """
- if self._var_names is None:
- # if not provided by user upon initialization, call for default names
- self._var_names = TaylorExpansAbstract.get_default_var_names(self._n_var)
- if len(self._var_names) != self._n_var:
- raise ValueError("The stored names of variables do not match the number of variables.")
- if len(set(self._var_names)) != self._n_var:
- raise ValueError("At least two variables have exactly the same name.")
- return list(self._var_names)
- @var_names.setter
- def var_names(self, names: List[str]) -> None:
- """Setter for names of variables in algebra. It checks the number of variables so is not used in the constructor
- in order to preserve computational performance.
- Args:
- (List[str]): names of variables.
- """
- if len(names) != self._n_var:
- raise ValueError("The number of names does not match the number of variables.")
- if len(set(names)) != self._n_var:
- raise ValueError("The given names of the variables are not unique.")
- self._var_names = list(names)
- @property
- @abstractmethod
- def remainder_term(self) -> str:
- """Abstract getter for the remainder (for printing purposes).
- Returns:
- str: symbolic expression for the remainder.
- """
- raise NotImplementedError
- @property
- @abstractmethod
- def coeff(self) -> np.ndarray:
- """Getter for coefficients of polynomial part.
- Returns:
- np.ndarray: coefficients of Taylor expansion.
- """
- raise NotImplementedError
- @coeff.setter
- @abstractmethod
- def coeff(self, coefficients: np.ndarray) -> None:
- """Setter for coefficients of polynomial part.
- Args:
- coefficients (np.ndarray): new coefficients of Taylor expansion.
- """
- raise NotImplementedError
- @property
- @abstractmethod
- def const(self):
- """
- Getter for the so-called constant coefficient(s) i.e. associated to the zeroth-order contribution and to be
- implemented by all inheritors.
- """
- raise NotImplementedError
- @const.setter
- @abstractmethod
- def const(self, cst) -> None:
- """
- Setter for the so-called constant coefficient(s) i.e. associated to the zeroth-order contribution and to be
- implemented by all inheritors.
- """
- raise NotImplementedError
- def is_in_same_algebra(self, other: "TaylorExpansAbstract") -> bool:
- """Checks if input has same order and number of variables.
- Args:
- other (TaylorExpansAbstract): Taylor expansion used to compare algebras.
- Returns:
- bool: True if and only if the two objects have the same order and number of variables.
- """
- return self._order == other.order and self._n_var == other.n_var
- def is_trivial(self) -> bool:
- """Function to call to know if expansion is in an algebra of order zero.
- Returns:
- bool: true if expansion is at order zero, false otherwise.
- """
- return self._order == 0
- def get_const_part(self) -> "TaylorExpansAbstract":
- """Abstract method returning the constant part (zeroth-order) and to be implemented by all inheritors.
- Returns:
- TaylorExpansAbstract: constant part of Taylor expansion.
- """
- return self.create_const_expansion(self.const)
- @abstractmethod
- def get_linear_part(self) -> "TaylorExpansAbstract":
- """Abstract method returning the linear part and to be implemented by all inheritors.
- Returns:
- TaylorExpansAbstract: linear part of Taylor expansion.
- """
- raise NotImplementedError
- def get_affine_part(self) -> "TaylorExpansAbstract":
- """Method returning the affine part.
- Returns:
- TaylorExpansAbstract: affine part of Taylor expansion.
- """
- try:
- return self.get_low_order_part(1)
- except ValueError:
- raise ValueError("There is no affine part in a zeroth-order expansion.")
- def get_nonlin_part(self) -> "TaylorExpansAbstract":
- """Method returning the non-linear part.
- Returns:
- TaylorExpansAbstract: non-linear part of Taylor expansion.
- """
- try:
- return self.get_high_order_part(2)
- except ValueError:
- raise ValueError("There is no non-linear part in an expansion below order two.")
- @abstractmethod
- def get_nilpo_part(self) -> "TaylorExpansAbstract":
- """Abstract method returning the nilpotent part and to be implemented by all inheritors. The nilpotent part (of
- the polynomial part in a Taylor expansion) is equal to the original expansion, except for the constant term that
- is set to zero.
- Returns:
- TaylorExpansAbstract: nilpotent part of Taylor expansion i.e. without the zeroth-order contribution.
- """
- raise NotImplementedError
- @abstractmethod
- def create_const_expansion(self, const: Scalar1OrND) -> "TaylorExpansAbstract":
- raise NotImplementedError
- @abstractmethod
- def is_nilpotent(self) -> bool:
- """Abstract method returning true if the Taylor expansions is nilpotent and to be implemented by all inheritors.
- A nilpotent expansion has a vanishing polynomial part if raised to a power greater than the order.
- Returns:
- bool: true if constant coefficient(s) is (are) zero.
- """
- raise NotImplementedError
- def __pow__(self, alpha: Union[int, float], modulo: Optional[float] = None) -> "TaylorExpansAbstract":
- """Method raising Taylor expansion objects to power. For integer, it wraps with the multiplication and squaring.
- For non-integer exponent, it is seen as a composition with x -> x ** alpha
- Args:
- alpha (Union[int, float]): power exponent.
- modulo (None): not used for Taylor expansions.
- Returns:
- TaylorExpansAbstract: Taylor expansion object to input power.
- """
- if self.is_trivial():
- return self.create_const_expansion(self.const ** alpha)
- # order of at least one
- is_int = isinstance(alpha, int)
- if is_int and alpha > 1:
- return self._pown(alpha)
- if isinstance(alpha, float) or (is_int and alpha < 0):
- # compose with x -> x ** alpha
- const = self.const
- nilpo = self.get_nilpo_part() * (1. / const)
- terms = np.ones(self._order + 1)
- terms[1:] = np.cumprod([(alpha - i) / (i + 1) for i in range(0, self._order)])
- powered = terms[-1] * nilpo
- powered.const = terms[-2]
- for el in terms[-3::-1]:
- powered *= nilpo
- powered.const = el
- return powered * (const**alpha)
- if is_int and alpha == 0:
- return self.create_const_expansion(1.)
- if is_int and alpha == 1:
- return self.copy()
- raise ValueError("Wrong exponent")
- def reciprocal(self) -> "TaylorExpansAbstract":
- """Method defining the reciprocal a.k.a. multiplicative inverse (within the algebra) of a Taylor expansion.
- It is computed as the composition from the left by x -> 1 / x
- Returns:
- TaylorExpansAbstract: multiplicative inverse of Taylor expansion object.
- """
- if self.is_trivial():
- return self.create_const_expansion(1. / self.const)
- # order of at least one
- const_inv = 1. / self.const
- nilpo = self.get_nilpo_part() * (-const_inv)
- inverted = nilpo + 1.
- for __ in range(1, self._order):
- inverted = nilpo * inverted + 1.
- return inverted * const_inv
- @abstractmethod
- def rigorous_integ_once_wrt_var(self, index_var: int) -> "TaylorExpansAbstract":
- """Method performing integration with respect to a given unknown variable. The integration constant is zero.
- This transformation is rigorous as the order of the expansion is increased by one. In other words, the output
- lives in another algebra, of higher dimension.
- Args:
- index_var (int): variable number w.r.t. which integration needs to be performed.
- Returns:
- TaylorExpansAbstract: integrated Taylor expansion object w.r.t. input variable number.
- """
- raise NotImplementedError
- def integ_once_wrt_var(self, index_var: int) -> "TaylorExpansAbstract":
- """Method performing integration with respect to a given unknown variable while remaining in the same
- algebra and to be implemented by all inheritors. This transformation is not rigorous in the sense that the order
- of the expansion should be increased by one.
- Args:
- index_var (int): variable number w.r.t. which integration needs to be performed.
- Returns:
- TaylorExpansAbstract: integrated Taylor expansion w.r.t. input variable number.
- """
- return self.rigorous_integ_once_wrt_var(index_var).truncated(self._order)
- def integ_wrt_var(self, index_var: int, integ_order: int) -> "TaylorExpansAbstract":
- """Method performing integration an arbitrary number of times with respect to a given unknown variable while
- remaining in the same algebra. This transformation is not rigorous in the sense that the order of the expansion
- should be increased at each integration.
- Args:
- index_var (int): variable number w.r.t. which integration needs to be performed.
- integ_order (int): number of time integration needs to be performed.
- Returns:
- TaylorExpansAbstract: integrated Taylor expansion w.r.t. input variable number.
- """
- if integ_order == 1:
- return self.integ_once_wrt_var(index_var)
- # recursive call with lowered integration order
- return self.integ_wrt_var(index_var, integ_order - 1).integ_once_wrt_var(index_var)
- @abstractmethod
- def deriv_once_wrt_var(self, index_var: int) -> "TaylorExpansAbstract":
- """Abstract method performing differentiation with respect to a given unknown variable while remaining in the
- same algebra and to be implemented by all inheritors. This transformation is not rigorous in the sense that the
- order of the expansion should be decreased by one.
- Args:
- index_var (int): variable number w.r.t. which differentiation needs to be performed.
- Returns:
- TaylorExpansAbstract: differentiated Taylor expansion w.r.t. input variable number.
- """
- raise NotImplementedError
- def deriv_wrt_var(self, index_var: int, diff_order: int) -> "TaylorExpansAbstract":
- """Method performing differentiation an arbitrary number of times with respect to a given unknown variable while
- remaining in the same algebra. This transformation is not rigorous in the sense that the order of the expansion
- should be decreased at each integration.
- Args:
- index_var (int): variable number w.r.t. which differentiation needs to be performed.
- diff_order (int): number of time differentiation needs to be performed.
- Returns:
- TaylorExpansAbstract: differentiated Taylor expansion w.r.t. input variable number.
- """
- if diff_order == 1:
- return self.deriv_once_wrt_var(index_var)
- # recursive call with lowered differentiation order
- return self.deriv_wrt_var(index_var, diff_order - 1).deriv_once_wrt_var(index_var)
- @abstractmethod
- def compose(self, other) -> "TaylorExpansAbstract":
- """Abstract method to be implemented by all inheritors. Performs composition with inputted Taylor expansion
- (must have the same order). The result has the same variables than the input.
- Args:
- other (TaylorExpansAbstract): argument to be composed on the right-hand side.
- Returns:
- TaylorExpansAbstract: composed expansion.
- """
- raise NotImplementedError
- @abstractmethod
- def pointwise_eval(self, x):
- """Abstract method for the evaluation of the Taylor expansion and to be implemented by all inheritors.
- Args:
- x (Union[complex, float, numpy.ndarray]): point of evaluation.
- Returns:
- Union[complex, float, numpy.ndarray]: Taylor expansion evaluated at given point.
- """
- raise NotImplementedError
- @abstractmethod
- def get_low_order_part(self, order: int) -> "TaylorExpansAbstract":
- """Abstract method to be implemented by all inheritors. It removes the high order terms of the expansion whilst
- leaving the order unchanged. Hence it is not a rigorous operation.
- Args:
- order (int): order (included) above which all contributions are removed.
- Returns:
- TaylorExpansAbstract: low order part of the Taylor expansion.
- """
- raise NotImplementedError
- @abstractmethod
- def truncated(self, new_order: int) -> "TaylorExpansAbstract":
- """Abstract method for the truncation at a given order and to be implemented by all inheritors. Output lives
- in another algebra, of lower dimension.
- Args:
- new_order (int): order of algebra in which to truncate the Taylor expansion.
- Returns:
- TaylorExpansAbstract: Taylor expansion truncated at input order.
- """
- raise NotImplementedError
- @abstractmethod
- def get_high_order_part(self, order: int) -> "TaylorExpansAbstract":
- """Abstract method to be implemented by all inheritors. It removes the low order terms of the expansion whilst
- leaving the order unchanged. Hence it is not a rigorous operation.
- Args:
- order (int): order (included) below which all contributions are removed.
- Returns:
- TaylorExpansAbstract: high order part of the Taylor expansion.
- """
- raise NotImplementedError
- @abstractmethod
- def prolong(self, new_order: int) -> "TaylorExpansAbstract":
- """Abstract method for the prolongation (opposite of truncation) at a given order and to be implemented by all
- inheritors. Output lives in another algebra, of higher dimension. It is not a rigorous operation as the possible
- contributions within the former remainder are ignored.
- Args:
- new_order (int): order of algebra in which to extend the Taylor expansion.
- Returns:
- TaylorExpansAbstract: Taylor expansion prolonged at input order.
- """
- raise NotImplementedError
- def prolong_one_order(self) -> "TaylorExpansAbstract":
- """Method for the prolongation (opposite of truncation) of exactly one order. Output lives in another algebra,
- of higher dimension. It is not a rigorous operation as the possible contributions within the former remainder
- are ignored.
- Returns:
- TaylorExpansAbstract: Taylor expansion prolonged of one order.
- """
- return self.prolong(self._order + 1)
- @abstractmethod
- def var_inserted(self, index_new_var: int, unknown_name: Optional[str] = None) -> "TaylorExpansAbstract":
- """Abstract method for the addition of a new variable and to be implemented by all inheritors. Output lives in
- another algebra, of higher dimension. All its terms associated with the new variable are zero and the other ones
- are identical to original expansion.
- Args:
- index_new_var (int): index of new variable to be added.
- unknown_name (str): name of new variable.
- Returns:
- TaylorExpansAbstract: Taylor expansion with an additional variable.
- """
- raise NotImplementedError
- def var_appended(self, unknown_name: Optional[str] = None) -> "TaylorExpansAbstract":
- """Wrapper to add a new variable at the end (stack).
- Args:
- unknown_name (str): name of new variable.
- Returns:
- TaylorExpansAbstract: Taylor expansion with an additional variable.
- """
- return self.var_inserted(self.n_var, unknown_name)
- @abstractmethod
- def var_removed(self, index_var: int) -> "TaylorExpansAbstract":
- """Abstract method for the removal of a variable. Output lives in another algebra, of smaller dimension. All its
- terms associated with the old variables only are identical to original expansion.
- Args:
- index_var (int): index of variable to be removed.
- Returns:
- TaylorExpansAbstract: Taylor expansion object with a variable removed.
- """
- raise NotImplementedError
- def last_var_removed(self) -> "TaylorExpansAbstract":
- """Wrapper to remove the last variable (unstack).
- Returns:
- TaylorExpansAbstract: Taylor expansion object with the last variable removed.
- """
- return self.var_removed(self.n_var - 1)
- @abstractmethod
- def var_eval(self, index_var: int, value: Union[complex, float]) -> "TaylorExpansAbstract":
- """Abstract method returning a Taylor expansion object where a variable has been replaced by a fixed scalar
- value. In other words, it is a partial evaluation of the polynomial part. It is not rigorous as terms of higher
- order hidden in the remainder would need to be considered in this operation.
- Args:
- index_var (int): index of variable to be evaluated.
- value (Union[complex, float]): value to replace given variable.
- Returns:
- TaylorExpansAbstract: Taylor expansion object with removed dependency.
- """
- raise NotImplementedError
- def last_var_eval(self, value: Union[complex, float]) -> "TaylorExpansAbstract":
- """Method returning a Taylor expansion object where the last variable has been replaced by a fixed scalar value.
- It wraps the general method for any variable.
- Args:
- value (Union[complex, float]): value to replace last variable with.
- Returns:
- TaylorExpansAbstract: Taylor expansion object with removed dependency.
- """
- return self.var_eval(self._n_var - 1, value)
- def contrib_removed(self, indices_var: List[int]) -> "TaylorExpansAbstract":
- """Method returning a Taylor expansion object where all coefficients associated to input variables' indices are
- set to zero.
- Args:
- indices_var (List[int]): indices of variables whose contribution is to be removed.
- Returns:
- TaylorExpansAbstract: Taylor expansion object with removed contributions.
- """
- output = self.copy()
- for index_var in indices_var:
- output = output.var_eval(index_var, 0.)
- return output
- def last_contrib_removed(self) -> "TaylorExpansAbstract":
- """Method returning a Taylor expansion object where all coefficients associated to last variable are set to
- zero.
- Returns:
- TaylorExpansAbstract: Taylor expansion object with contributions from last variable removed.
- """
- return self.contrib_removed([self._n_var - 1])
- @abstractmethod
- def divided_by_var(self, index_var: int) -> "TaylorExpansAbstract":
- """Abstract method returning the Taylor expansion divided by the input variable. This is not a rigorous
- operation as the order should be decreased by one.
- Args:
- index_var (int): index of variable to divide with.
- Returns:
- TaylorExpansAbstract: Taylor expansion divided by variable.
- """
- raise NotImplementedError
- def linearly_combine_with_another(self, alpha: Union[complex, float], expansion: "TaylorExpansAbstract",
- beta: Union[complex, float]) -> "TaylorExpansAbstract":
- """Method multiplying with a scalar and then adding with a another expansion also multiplied by some scalar.
- Args:
- alpha (Union[complex, float]): multiplier for self.
- expansion (TaylorExpansAbstract): expansion to be linearly combined with.
- beta (Union[complex, float]): multiplier for other expansion.
- Returns:
- TaylorExpansAbstract: linear combination of self and arguments.
- """
- return alpha * self + beta * expansion
- @staticmethod
- def landau_univar(order: int, var_names: List[str]) -> str:
- """Static method returning a string representing with a Landau notation the remainder of the expansion in the
- univariate case.
- Args:
- order (int): order of the expansion.
- var_names (List[str]): name of the variable.
- Returns:
- str: negligible part of the Taylor expansion w.r.t. input order.
- """
- if order == 0:
- return landau_symbol + "(1)"
- if order == 1:
- return landau_symbol + "(" + var_names[0] + ")"
- # order of at least two
- return landau_symbol + "(" + var_names[0] + "**" + str(order) + ")"
- @staticmethod
- def landau_multivar(order: int, var_names: List[str]) -> str:
- """Static method returning a string representing with a Landau notation the remainder of the expansion in the
- multivariate case.
- Args:
- order (int): order of the expansion.
- var_names (List[str]): name of variables.
- Returns:
- str: negligible part of the Taylor expansion w.r.t. input order.
- """
- if order == 0:
- return landau_symbol + "(1)"
- if len(var_names) == 2:
- norm = "|(" + var_names[0] + ", " + var_names[1] + ")|"
- else:
- # at least three variables
- norm = "|(" + var_names[0] + ",...," + var_names[-1] + ")|"
- if order == 1:
- return landau_symbol + "(" + norm + ")"
- # order of at least two
- return landau_symbol + "(" + norm + "**" + str(order) + ")"
- @staticmethod
- def get_default_var_names(n_var: int, unknown_name: str = default_unknown_name) -> List[str]:
- """Static method returning the default name for the variables of a Taylor expansion.
- Args:
- n_var (int): number of variables.
- unknown_name (str): character referring to variable of expansion.
- Returns:
- List[str]: name of variables.
- """
- return [unknown_name] if n_var == 1 else [unknown_name + str(i) for i in range(1, n_var + 1)]
- def exp(self) -> "TaylorExpansAbstract":
- """Exponential of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: exponential of Taylor expansion object.
- """
- if self.is_trivial():
- return self.create_const_expansion(self._exp_cst(self.const))
- nilpo = self.get_nilpo_part()
- order_plus_one = self.order + 1
- seq = np.ones(order_plus_one)
- seq[1:] /= np.cumprod(np.arange(1, order_plus_one))
- expon = nilpo * seq[-1]
- expon.const = seq[-2]
- for el in seq[-3::-1]:
- expon *= nilpo
- expon.const = el
- return expon * self._exp_cst(self.const)
- def log(self) -> "TaylorExpansAbstract":
- """Natural logarithm of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: natural logarithm of Taylor expansion object.
- """
- if self.is_trivial():
- return self.create_const_expansion(self._log_cst(self.const))
- order = self.order
- const = self.const
- nilpo = self.get_nilpo_part() * (1. / const)
- seq = np.append([0.], 1. / np.arange(1., order + 1))
- seq[2:] *= np.cumprod(-np.ones(order - 1))
- logar = nilpo * seq[-1]
- for el in seq[-2:0:-1]:
- logar.const = el
- logar *= nilpo
- logar.const = self._log_cst(const)
- return logar
- def sqrt(self) -> "TaylorExpansAbstract":
- """Square root of Taylor expansion.
- Returns:
- TaylorExpansAbstract: square root of Taylor expansion.
- """
- if self.is_trivial():
- return self.create_const_expansion(self._sqrt_cst(self.const))
- const = self.const
- nilpo = self.get_nilpo_part() * (1. / const)
- order = self.order
- terms = np.append([1.], (1. / 2. + np.arange(0., -order, -1.)) / np.arange(1., order + 1))
- terms = np.cumprod(terms)
- powered = nilpo * terms[-1]
- powered.const = terms[-2]
- for el in terms[-3::-1]:
- powered *= nilpo
- powered.const = el
- return powered * self._sqrt_cst(const)
- @staticmethod
- def seq_c_s_zero(order: int, eps: float) -> Tuple[np.ndarray, np.ndarray]:
- order_plus_one = order + 1
- c_0, s_0 = np.zeros(order_plus_one), np.zeros(order_plus_one)
- c_0[0] = s_0[1] = 1.
- integers = np.arange(1, order_plus_one)
- factors = eps / (integers[:-1] * integers[1:])
- for i in range(1, order - 1, 2):
- s_0[i + 2] = s_0[i] * factors[i]
- for i in range(0, order - 1, 2):
- c_0[i + 2] = c_0[i] * factors[i]
- return c_0, s_0
- @staticmethod
- @lru_cache(maxsize=1)
- def seq_cos_sin_zero(order: int) -> Tuple[np.ndarray, np.ndarray]:
- """Computes the truncated Maclaurin series of the trigonometric cosine and sine functions.
- Args:
- order (int): order of truncation.
- Returns:
- np.ndarray: truncated Maclaurin series of cosine.
- np.ndarray: truncated Maclaurin series of sine.
- """
- return TaylorExpansAbstract.seq_c_s_zero(order, -1.)
- @staticmethod
- @lru_cache(maxsize=1)
- def seq_cosh_sinh_zero(order: int) -> Tuple[np.ndarray, np.ndarray]:
- """Computes the truncated Maclaurin series of the hyperbolic cosine and sine functions.
- Args:
- order (int): order of truncation.
- Returns:
- numpy.ndarray: truncated Maclaurin series of hyperbolic cosine.
- numpy.ndarray: truncated Maclaurin series of hyperbolic sine.
- """
- return TaylorExpansAbstract.seq_c_s_zero(order, 1.)
- @staticmethod
- def sinusoids(p: "TaylorExpansAbstract", eps: float) -> Tuple["TaylorExpansAbstract", "TaylorExpansAbstract"]:
- order = p.order
- nilpo = p.get_nilpo_part()
- seq_cos, seq_sin = TaylorExpansAbstract.seq_c_s_zero(order, eps)
- cosinus = nilpo * seq_cos[-1]
- cosinus.const = seq_cos[-2]
- sinus = nilpo * seq_sin[-1]
- sinus.const = seq_sin[-2]
- for el1, el2 in zip(seq_cos[-3::-1], seq_sin[-3::-1]):
- cosinus *= nilpo
- cosinus.const = el1
- sinus *= nilpo
- sinus.const = el2
- return cosinus, sinus
- @staticmethod
- def _c_s(p: "TaylorExpansAbstract", eps: float,
- a: Union[complex, float], b: Union[complex, float]) -> "TaylorExpansAbstract":
- cosine_sine = TaylorExpansAbstract.sinusoids(p, eps)
- cosine, sine = cosine_sine[0], cosine_sine[1]
- return a * cosine + b * sine
- def cos(self) -> "TaylorExpansAbstract":
- """Cosine of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: cosine of Taylor expansion object.
- """
- const = self.const
- c, s = self._cos_cst(const), self._sin_cst(const)
- return self.create_const_expansion(c) if self.is_trivial() else self._c_s(self, -1., c, -s)
- def sin(self) -> "TaylorExpansAbstract":
- """Sine of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: sine of Taylor expansion object.
- """
- const = self.const
- c, s = self._cos_cst(const), self._sin_cst(const)
- return self.create_const_expansion(s) if self.is_trivial() else self._c_s(self, -1., s, c)
- def cosh(self) -> "TaylorExpansAbstract":
- """Hyperbolic cosine of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: hyperbolic cosine of Taylor expansion object.
- """
- const = self.const
- ch, sh = self._cosh_cst(const), self._sinh_cst(const)
- return self.create_const_expansion(ch) if self.is_trivial() else self._c_s(self, 1., ch, sh)
- def sinh(self) -> "TaylorExpansAbstract":
- """Hyperbolic sine of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: hyperbolic sine of Taylor expansion object.
- """
- const = self.const
- ch, sh = self._cosh_cst(const), self._sinh_cst(const)
- return self.create_const_expansion(ch) if self.is_trivial() else self._c_s(self, 1., sh, ch)
- @staticmethod
- def seq_tan_tanh(c: float, eps: float, order: int, tan_cst: Callable, tanh_cst: Callable) -> np.ndarray:
- order_plus_one = order + 1
- seq = np.empty(order_plus_one)
- seq[0] = tanh_cst(c) if eps == -1. else tan_cst(c)
- seq[1] = 1. + eps * seq[0] * seq[0]
- for i in range(2, order_plus_one):
- summed = seq[:i].dot(seq[i - 1::-1])
- seq[i] = summed * eps / i
- return seq
- def tan(self) -> "TaylorExpansAbstract":
- """Tangent of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: tangent of Taylor expansion object.
- """
- if self.is_trivial():
- return self.create_const_expansion(self._tan_cst(self.const))
- nilpo = self.get_nilpo_part()
- seq = TaylorExpansAbstract.seq_tan_tanh(self.const, 1., self.order,
- self._tan_cst, self._tanh_cst)
- tangent = nilpo * seq[-1]
- tangent.const = seq[-2]
- for el in seq[-3::-1]:
- tangent *= nilpo
- tangent.const = el
- return tangent
- def tanh(self) -> "TaylorExpansAbstract":
- """Hyperbolic tangent of Taylor expansion object.
- Returns:
- TaylorExpansAbstract: hyperbolic tangent of Taylor expansion object.
- """
- if self.is_trivial():
- return self.create_const_expansion(self._tanh_cst(self.const))
- nilpo = self.get_nilpo_part()
- seq = self.seq_tan_tanh(self.const, -1., self.order, self._tan_cst, self._tanh_cst)
- tangenth = nilpo * seq[-1]
- tangenth.const = seq[-2]
- for el in seq[-3::-1]:
- tangenth *= nilpo
- tangenth.const = el
- return tangenth
|