factory_taylor.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # factory_taylor.py: object factory for Taylor expansions
  2. # Copyright 2022 Romain Serra
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from typing import List, Optional, Union
  15. import numpy as np
  16. from swiftt.taylor.taylor_map import ComplexTaylorMap, RealTaylorMap
  17. from swiftt.taylor.real_univar_taylor import RealMultivarTaylor, RealUnivarTaylor
  18. from swiftt.taylor.complex_univar_taylor import ComplexMultivarTaylor, ComplexUnivarTaylor
  19. from swiftt.taylor.taylor_expans_abstract import default_unknown_name, TaylorExpansAbstract
  20. def const_expansion(n_var: int, order: int, const: Union[complex, float],
  21. var_names: Optional[List[str]] = None) -> Union[ComplexMultivarTaylor, RealMultivarTaylor]:
  22. """Method to create a Taylor expansion with a given number of variables and order with all polynomial coefficients
  23. at zero except the zeroth-order one if needed. In other words, it writes as const + remainder.
  24. Args:
  25. n_var (int): number of variables.
  26. order (int): order of expansion.
  27. const (Union[complex, float]): coefficient of constant part.
  28. var_names (List[str]): name of variables.
  29. Returns:
  30. Union[ComplexMultivarTaylor, RealMultivarTaylor]: Taylor expansions built from inputs.
  31. """
  32. if var_names is None:
  33. var_names = TaylorExpansAbstract.get_default_var_names(n_var)
  34. if isinstance(const, complex):
  35. if n_var == 1:
  36. output = ComplexUnivarTaylor(order, var_names)
  37. else:
  38. # multivariate case
  39. output = ComplexMultivarTaylor(n_var, order, var_names)
  40. coeff = np.zeros(output.dim_alg, dtype=np.complex128)
  41. else:
  42. # real variable(s) case
  43. if n_var == 1:
  44. output = RealUnivarTaylor(order, var_names)
  45. else:
  46. # multivariate case
  47. output = RealMultivarTaylor(n_var, order, var_names)
  48. coeff = np.zeros(output.dim_alg, dtype=np.float64)
  49. coeff[0] = const
  50. output.coeff = coeff
  51. return output
  52. def zero_expansion(n_var: int, order: int, var_names: Optional[List[str]] = None,
  53. dtype: type = np.float64) -> Union[ComplexMultivarTaylor, RealMultivarTaylor]:
  54. """Method to create a Taylor expansion with a given number of variables and order with a null polynomial part.
  55. Args:
  56. n_var (int): number of variables.
  57. order (int): order of expansion.
  58. var_names (List[str]): name of variables.
  59. dtype (type): assumed numpy.dtype for coefficients and variables (float or complex).
  60. Returns:
  61. Union[ComplexMultivarTaylor, RealMultivarTaylor]: Taylor expansions whose polynomial part is null.
  62. """
  63. const = 0. if dtype == np.float64 else complex(0., 0.)
  64. return const_expansion(n_var, order, const, var_names)
  65. def one_expansion(n_var: int, order: int, var_names: Optional[List[str]] = None,
  66. dtype: type = np.float64) -> Union[ComplexMultivarTaylor, RealMultivarTaylor]:
  67. """Method to create a Taylor expansion with a given number of variables and order with all polynomial coefficients
  68. at zero except the zeroth-order one that is set to one.
  69. Args:
  70. n_var (int): number of variables.
  71. order (int): order of expansion.
  72. var_names (List[str]): name of variables.
  73. dtype (tyoe): assumed numpy.dtype for coefficients and variables (float or complex).
  74. Returns:
  75. Union[ComplexMultivarTaylor, RealMultivarTaylor]: Taylor expansions whose polynomial part is constant
  76. and equal to one.
  77. """
  78. if dtype == np.float64:
  79. return const_expansion(n_var, order, 1., var_names)
  80. # complex variable(s) case
  81. return const_expansion(n_var, order, complex(1., 0.), var_names)
  82. def unknown_var(n_var: int, order: int, index_var: int, nominal: Union[complex, float], scaling: Union[complex, float],
  83. var_names: List[str]) -> Union[ComplexMultivarTaylor, RealMultivarTaylor]:
  84. """Method to create a Taylor expansion with a given number of variables and order whose polynomial part is affine
  85. (constant + linear) in a single variable and null for the others.
  86. Args:
  87. n_var (int): number of variables.
  88. order (int): order of expansion.
  89. index_var (int): index of variable with affine contribution.
  90. nominal (Union[complex, float]): value for constant part.
  91. scaling (Union[complex, float]): value for linear coefficient in variable of interest.
  92. var_names (List[str]): name of variables.
  93. Returns:
  94. Union[ComplexMultivarTaylor, RealMultivarTaylor]: Taylor expansion corresponding to inputs.
  95. """
  96. if index_var < 0 or index_var >= n_var:
  97. raise ValueError("Input index non-positive or larger than number of variables")
  98. if var_names is None:
  99. var_names = TaylorExpansAbstract.get_default_var_names(n_var)
  100. if not isinstance(nominal, complex) and not isinstance(scaling, complex):
  101. if n_var == 1:
  102. output = RealUnivarTaylor(order, var_names)
  103. else:
  104. # multivariable case
  105. output = RealMultivarTaylor(n_var, order, var_names)
  106. coeff = np.zeros(output.dim_alg, dtype=np.float64)
  107. else:
  108. # complex variable(s) case
  109. if n_var == 1:
  110. output = ComplexUnivarTaylor(order, var_names)
  111. else:
  112. # multivariable case
  113. output = ComplexMultivarTaylor(n_var, order, var_names)
  114. coeff = np.zeros(output.dim_alg, dtype=np.complex128)
  115. coeff[0], coeff[index_var + 1] = nominal, scaling
  116. output.coeff = coeff
  117. return output
  118. def build_affine_square_map(order: int, consts, lins,
  119. var_names: Optional[List[str]] = None) -> ComplexTaylorMap:
  120. """Method to create a square Taylor map with a given number of variables and order. Each element is affine
  121. (constant + linear) for a single variable.
  122. Args:
  123. order (int): order of expansion.
  124. consts (Iterable[complex, float]): values for constant part.
  125. lins (Iterable[complex, float]): value for linear coefficient. First one is for first element and first
  126. variable, second one for second element and second variable, etc.
  127. var_names (List[str]): name of variables.
  128. Returns:
  129. ComplexTaylorMap: Taylor map corresponding to inputs.
  130. """
  131. n_var = len(consts)
  132. if len(lins) != n_var:
  133. raise ValueError("Inconsistency between number of input constants and linear terms")
  134. is_floats = True
  135. for lin, const in zip(lins, consts):
  136. if isinstance(lin, complex) or isinstance(const, complex):
  137. is_floats = False
  138. break
  139. dtype = np.float64 if is_floats else np.complex128
  140. zero = zero_expansion(n_var, order, var_names, dtype)
  141. coeff = np.zeros(zero.dim_alg, dtype=dtype)
  142. expansion = zero.copy()
  143. coeff[0], coeff[1] = consts[0], lins[0]
  144. expansion.coeff = coeff
  145. expansions = [expansion]
  146. for i in range(1, len(consts)):
  147. coeff[0], coeff[i], coeff[i + 1] = consts[i], 0., lins[i]
  148. expansions.append(zero.create_expansion_with_coeff(coeff))
  149. return RealTaylorMap(expansions) if is_floats else ComplexTaylorMap(expansions)
  150. def create_unknown_map(order: int, consts, var_names: Optional[List[str]] = None) -> ComplexTaylorMap:
  151. """Method to create a square Taylor map with a given number of variables and order with a constant polynomial part.
  152. Includes the trivial case of order zero.
  153. Args:
  154. order (int): order of expansion.
  155. consts (Iterable[complex, float]): values for constant part.
  156. var_names (List[str]): name of variables.
  157. Returns:
  158. ComplexTaylorMap: Taylor map corresponding to inputs.
  159. """
  160. n_var = len(consts)
  161. is_floats = True
  162. for const in consts:
  163. if isinstance(const, complex):
  164. is_floats = False
  165. break
  166. if order == 0:
  167. zero = zero_expansion(n_var, order, var_names)
  168. expansions = [zero.create_const_expansion(const) for const in consts]
  169. return RealTaylorMap(expansions) if is_floats else ComplexTaylorMap(expansions)
  170. if order > 0:
  171. if is_floats:
  172. return build_affine_square_map(order, consts, np.ones(n_var, dtype=np.float64), var_names)
  173. # complex variable(s) case
  174. return build_affine_square_map(order, consts, np.ones(n_var, dtype=np.complex128), var_names)
  175. raise ValueError("The inputted expansion's order is non-positive.")
  176. def from_string(expr: str, order: Optional[int] = None, n_var: Optional[int] = None) -> RealMultivarTaylor:
  177. """Method to create a Taylor expansion from a string e.g. "x1**2 - x1 + x2 - 3". Uses the sympy library to
  178. interpret the polynomial part.
  179. Args:
  180. expr (str): string representing a polynomial to be converted into a Taylor expansion.
  181. order (int): order of the expansion (optional). If not present, taken as the degree of the polynomial.
  182. n_var (int): number of variables in the expansion (optional, if not present interpreted from input).
  183. Should not be less than the actual number of variables in the string.
  184. Returns:
  185. RealMultivarTaylor: Taylor expansions built from input(s).
  186. """
  187. try:
  188. from sympy import poly_from_expr # here to avoid having it as a mandatory dependency
  189. poly, opt = poly_from_expr(expr)
  190. if order is None:
  191. order = sum(poly.LM("grlex").exponents)
  192. var_names = [str(el) for el in opt["gens"]]
  193. if n_var is None:
  194. n_var = len(opt["gens"])
  195. elif n_var < len(opt["gens"]):
  196. raise ValueError("The required number of variables cannot be less than in the inputted string.")
  197. else:
  198. # additional variables need to be named
  199. index = len(var_names) + 1
  200. while len(var_names) < n_var:
  201. tentative_name = default_unknown_name + str(index)
  202. if tentative_name in var_names:
  203. index += 1
  204. else:
  205. var_names.append(tentative_name)
  206. if n_var > 1:
  207. output = RealMultivarTaylor(len(opt["gens"]), order, var_names)
  208. coeff = np.zeros(output.dim_alg)
  209. mapping = output.get_mapping_monom()
  210. # loop over all non-zero coefficients for monomials with a degree not more than the expansion's order
  211. for el, monom in zip(poly.coeffs(), poly.monoms()):
  212. if sum(monom) <= order:
  213. coeff[mapping[monom]] = el
  214. else:
  215. # univariate case
  216. output = RealUnivarTaylor(order, var_names)
  217. coeff = np.zeros(order + 1)
  218. inter = np.flip(poly.all_coeffs()) # sympy returns canonical coefficients in decreasing order
  219. index = min(len(coeff), len(inter))
  220. coeff[:index] = np.array(inter[:index])
  221. output.coeff = coeff
  222. return output
  223. except ImportError:
  224. raise ImportError("Sympy has not been found. Install it to be able to initialize Taylor expansions from "
  225. "strings.")