rateslib 3.0.0.dev4__cp311-abi3-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. rateslib/__init__.py +258 -0
  2. rateslib/ad/__init__.py +376 -0
  3. rateslib/ad/utils.py +22 -0
  4. rateslib/curves/__init__.py +66 -0
  5. rateslib/curves/curve_metrics_trait.py +50 -0
  6. rateslib/curves/user_defined.py +100 -0
  7. rateslib/default/__init__.py +32 -0
  8. rateslib/display/__init__.py +18 -0
  9. rateslib/display/frame.py +163 -0
  10. rateslib/display/plot.py +43 -0
  11. rateslib/enums/__init__.py +66 -0
  12. rateslib/fixings/__init__.py +26 -0
  13. rateslib/fx/__init__.py +37 -0
  14. rateslib/fx/fx_rate_metrics_trait.py +50 -0
  15. rateslib/instruments/__init__.py +61 -0
  16. rateslib/instruments/ir_linear/__init__.py +39 -0
  17. rateslib/instruments/ir_multi_ccy/__init__.py +18 -0
  18. rateslib/instruments/metrics_trait.py +47 -0
  19. rateslib/instruments/pseudo_and_combination/__init__.py +18 -0
  20. rateslib/instruments/securities/__init__.py +42 -0
  21. rateslib/legs/__init__.py +30 -0
  22. rateslib/licence/__init__.py +14 -0
  23. rateslib/periods/__init__.py +32 -0
  24. rateslib/periods/parameters/__init__.py +34 -0
  25. rateslib/py.typed +0 -0
  26. rateslib/rs/__init__.pyi +236 -0
  27. rateslib/rs/ad.pyi +154 -0
  28. rateslib/rs/curves.pyi +69 -0
  29. rateslib/rs/defaults.pyi +183 -0
  30. rateslib/rs/enums.pyi +230 -0
  31. rateslib/rs/fixings.pyi +99 -0
  32. rateslib/rs/fx.pyi +82 -0
  33. rateslib/rs/instruments.pyi +26 -0
  34. rateslib/rs/legs.pyi +60 -0
  35. rateslib/rs/licence.pyi +20 -0
  36. rateslib/rs/periods.pyi +121 -0
  37. rateslib/rs/scheduling.pyi +293 -0
  38. rateslib/rs.pyd +0 -0
  39. rateslib/scheduling/__init__.py +46 -0
  40. rateslib/scheduling/imm.py +173 -0
  41. rateslib/serialization/__init__.py +14 -0
  42. rateslib/solver/__init__.py +14 -0
  43. rateslib/splines/__init__.py +83 -0
  44. rateslib/splines/evaluate.py +68 -0
  45. rateslib/trait_protocols/__init__.py +33 -0
  46. rateslib/trait_protocols/cashflows_context.py +75 -0
  47. rateslib/trait_protocols/curves/__init__.py +15 -0
  48. rateslib/trait_protocols/curves/curve_metrics.py +65 -0
  49. rateslib/trait_protocols/curves/curve_metrics_mut.py +31 -0
  50. rateslib/trait_protocols/fx/__init__.py +14 -0
  51. rateslib/trait_protocols/fx/fx_rate_metrics.py +51 -0
  52. rateslib/trait_protocols/instruments/__init__.py +26 -0
  53. rateslib/trait_protocols/instruments/cashflows.py +44 -0
  54. rateslib/trait_protocols/instruments/local_ccy.py +34 -0
  55. rateslib/trait_protocols/instruments/metrics.py +39 -0
  56. rateslib/trait_protocols/instruments/npv.py +62 -0
  57. rateslib/trait_protocols/instruments/pricing_contexts.py +31 -0
  58. rateslib/trait_protocols/instruments/rate.py +63 -0
  59. rateslib/trait_protocols/instruments/solver_sensitivities.py +56 -0
  60. rateslib/trait_protocols/npv_context.py +162 -0
  61. rateslib/trait_protocols/utils/__init__.py +17 -0
  62. rateslib/trait_protocols/utils/homomophic_hash.py +34 -0
  63. rateslib/trait_protocols/utils/json.py +42 -0
  64. rateslib/trait_protocols/utils/model_prerequisites.py +46 -0
  65. rateslib/trait_protocols/utils/name.py +27 -0
  66. rateslib/trait_protocols/utils/state.py +18 -0
  67. rateslib/typing_rateslib/__init__.py +34 -0
  68. rateslib-3.0.0.dev4.dist-info/METADATA +63 -0
  69. rateslib-3.0.0.dev4.dist-info/RECORD +79 -0
  70. rateslib-3.0.0.dev4.dist-info/WHEEL +4 -0
  71. rateslib-3.0.0.dev4.dist-info/licenses/COMMERCIAL_LICENCE +171 -0
  72. rateslib-3.0.0.dev4.dist-info/licenses/COMMERCIAL_LICENCE_ADDENDUM1 +77 -0
  73. rateslib-3.0.0.dev4.dist-info/licenses/LICENCE +140 -0
  74. rateslib-3.0.0.dev4.dist-info/sboms/bridge.cyclonedx.json +6765 -0
  75. rateslib_json/calendars.json +51683 -0
  76. rateslib_json/float_rate_series.json +266 -0
  77. rateslib_json/fx_index.json +202 -0
  78. rateslib_json/irs_series.json +340 -0
  79. rateslib_json/specs.json +1462 -0
rateslib/__init__.py ADDED
@@ -0,0 +1,258 @@
1
+ # SPDX-License-Identifier: LicenseRef-Rateslib-Dual
2
+ #
3
+ # Copyright (c) 2026 Siffrorna Technology Limited
4
+ #
5
+ # Dual-licensed: Free Educational Licence or Paid Commercial Licence (commercial/professional use)
6
+ # Source-available, not open source.
7
+ #
8
+ # See LICENSE and https://rateslib.com/py/en/latest/i_licence.html for details,
9
+ # and/or contact info (at) rateslib (dot) com
10
+ ####################################################################################################
11
+
12
+ __docformat__ = "restructuredtext"
13
+ __version__ = "3.0.0.dev4"
14
+
15
+ # Let users know if they're missing any of our hard dependencies
16
+ _hard_dependencies = ("pandas", "matplotlib", "numpy")
17
+ _missing_dependencies: list[str] = []
18
+
19
+ for _dependency in _hard_dependencies:
20
+ try:
21
+ __import__(_dependency)
22
+ except ImportError as _e: # pragma: no cover
23
+ raise ImportError(f"`rateslib` requires installation of {_dependency}: {_e}")
24
+
25
+ ## User warnings convert from Rust::log
26
+ import logging
27
+ import warnings
28
+ import rateslib.rs
29
+
30
+
31
+ class RustLogToUserWarningHandler(logging.Handler):
32
+ def emit(self, record):
33
+ if record.levelno >= logging.WARNING:
34
+ warnings.warn(
35
+ f"[{record.levelname}] {record.getMessage()}",
36
+ category=UserWarning,
37
+ stacklevel=3, # Adjusts the stack trace so it points to the Python user's line
38
+ )
39
+
40
+
41
+ logger = logging.getLogger("rateslib")
42
+ logger.addHandler(RustLogToUserWarningHandler())
43
+ logger.setLevel(logging.WARNING)
44
+ logger.propagate = False
45
+ ##
46
+
47
+ from datetime import datetime as dt
48
+
49
+ from rateslib.default import *
50
+
51
+ defaults = DefaultManager()
52
+ calendars = CalendarManager()
53
+ specs = SpecManager()
54
+ indexes = IndexManager()
55
+
56
+ from rateslib.licence import *
57
+
58
+ licence = LicenceManager()
59
+
60
+ from rateslib.fixings import *
61
+
62
+ fixings = FixingsManager()
63
+
64
+ from rateslib.ad import *
65
+ from rateslib.enums import *
66
+ from rateslib.display import *
67
+ from rateslib.curves import *
68
+ from rateslib.scheduling import *
69
+ from rateslib.periods import *
70
+ from rateslib.periods.parameters import *
71
+ from rateslib.fx import *
72
+ from rateslib.solver import *
73
+ from rateslib.legs import *
74
+ from rateslib.instruments import *
75
+ from rateslib.trait_protocols import *
76
+
77
+ # fixings.set_data_directory(os.path.dirname(os.path.abspath(__file__)) + "/historical")
78
+
79
+ from contextlib import ContextDecorator
80
+
81
+
82
+ class default_context(ContextDecorator):
83
+ """
84
+ Context manager to temporarily set options in the `with` statement context.
85
+
86
+ You need to invoke as ``option_context(key, val, key2, val2, ...)``.
87
+
88
+ Examples
89
+ --------
90
+ >>> with default_context('convention', "act360", 'frequency', 'S'):
91
+ ... pass
92
+ """
93
+
94
+ def __init__(self, *args) -> None: # type: ignore[no-untyped-def]
95
+ if len(args) % 2 != 0 or len(args) < 2:
96
+ raise ValueError("Need to invoke as default_context(key, val, key2, val2, ...).")
97
+
98
+ self.ops = list(zip(args[::2], args[1::2], strict=False))
99
+
100
+ def __enter__(self) -> None:
101
+ self.undo = [(key, defaults.get(key)) for key, _ in self.ops]
102
+
103
+ for key, val in self.ops:
104
+ defaults.set(key, val)
105
+
106
+ def __exit__(self, *args) -> None: # type: ignore[no-untyped-def]
107
+ if self.undo:
108
+ for key, val in self.undo:
109
+ defaults.set(key, val)
110
+
111
+
112
+ __all__ = [
113
+ # globals
114
+ "dt",
115
+ "defaults",
116
+ "calendars",
117
+ "specs",
118
+ "indexes",
119
+ "fixings",
120
+ "licence",
121
+ # ad
122
+ "Dual",
123
+ "Dual2",
124
+ "Variable",
125
+ "ADOrder",
126
+ "gradient",
127
+ # curves
128
+ "CurveMetricsTrait",
129
+ "Curve",
130
+ "CurvesContext",
131
+ "CompositeCurve",
132
+ "CreditImpliedCurve",
133
+ "ProxyCurve",
134
+ "MultiCsaCurve",
135
+ "RolledCurve",
136
+ "TranslatedCurve",
137
+ "TenorsCurve",
138
+ "CurveMeta",
139
+ "CurvesContext",
140
+ "index_left",
141
+ "SmithWilsonCurve",
142
+ "NelsonSiegelCurve",
143
+ "NelsonSiegelSvenssonCurve",
144
+ "UserCurve",
145
+ # display
146
+ "frame",
147
+ "plot",
148
+ # scheduling
149
+ "Schedule",
150
+ "Tenor",
151
+ "Frequency",
152
+ "RollDay",
153
+ "Adjuster",
154
+ "Cal",
155
+ "UnionCal",
156
+ "NamedCal",
157
+ "Imm",
158
+ "Convention",
159
+ "StubInference",
160
+ "next_imm",
161
+ "get_imm",
162
+ "add_tenor",
163
+ "dcf",
164
+ # fx
165
+ "Ccy",
166
+ "FXPair",
167
+ "FXRate",
168
+ "FXRates",
169
+ "FXForwards",
170
+ "FXAggregator",
171
+ # solver
172
+ "Solver",
173
+ # periods
174
+ "Cashflow",
175
+ "MtmCashflow",
176
+ "FixedPeriod",
177
+ "FloatPeriod",
178
+ "ZeroFixedPeriod",
179
+ "ZeroFloatPeriod",
180
+ "CreditPremiumPeriod",
181
+ "CreditProtectionPeriod",
182
+ # periods.parameters
183
+ "SettlementParams",
184
+ "ScheduleParams",
185
+ "IndexParams",
186
+ "NonDeliverableParams",
187
+ "FixedRateParams",
188
+ "CreditParams",
189
+ "FloatRateParams",
190
+ "FixedRateParams",
191
+ "MtmParams",
192
+ # legs
193
+ "FixedLeg",
194
+ "FloatLeg",
195
+ "ZeroFloatLeg",
196
+ "ZeroFixedLeg",
197
+ "CreditPremiumLeg",
198
+ "CreditProtectionLeg",
199
+ "CustomLeg",
200
+ # instruments
201
+ "MetricsTrait",
202
+ "Bill",
203
+ "BondFuture",
204
+ "FixedRateLoan",
205
+ "FixedRateBond",
206
+ "FloatRateLoan",
207
+ "FloatRateNote",
208
+ "Fee",
209
+ "FXForward",
210
+ "FXSwap",
211
+ "NDF",
212
+ "FRA",
213
+ "IRS",
214
+ "SBS",
215
+ "STIRFuture",
216
+ "IIRS",
217
+ "YoYIS",
218
+ "XCS",
219
+ "XCSFixed",
220
+ "XCSFixedFixed",
221
+ "ZCIS",
222
+ "ZCS",
223
+ "CDS",
224
+ "Value",
225
+ "Portfolio",
226
+ "Spread",
227
+ "Fly",
228
+ # user defined object traits
229
+ "CurveMetrics",
230
+ "CurveMetricsMut",
231
+ "Metrics",
232
+ # enums
233
+ "AccrualMethod",
234
+ "Amortization",
235
+ "BillConvention",
236
+ "ConversionFactorConvention",
237
+ "CurveType",
238
+ "FloatFixingMethod",
239
+ "FXDeltaMethod",
240
+ "IndexMethod",
241
+ "IROptionMetric",
242
+ "LegIndexBase",
243
+ "LegMtm",
244
+ "LocalInterpolation",
245
+ "PPSplineEndpoint",
246
+ "RateMetric",
247
+ "SecurityMetric",
248
+ "SolverAlgo",
249
+ "SolverStatus",
250
+ "SpreadCompoundMethod",
251
+ "SwaptionSettlementMethod",
252
+ "Transform",
253
+ "YtmConvention",
254
+ "YtmC",
255
+ "YtmV1",
256
+ "YtmV2",
257
+ "YtmV3",
258
+ ]
@@ -0,0 +1,376 @@
1
+ # SPDX-License-Identifier: LicenseRef-Rateslib-Dual
2
+ #
3
+ # Copyright (c) 2026 Siffrorna Technology Limited
4
+ #
5
+ # Dual-licensed: Free Educational Licence or Paid Commercial Licence (commercial/professional use)
6
+ # Source-available, not open source.
7
+ #
8
+ # See LICENSE and https://rateslib.com/py/en/latest/i_licence.html for details,
9
+ # and/or contact info (at) rateslib (dot) com
10
+ ####################################################################################################
11
+
12
+ from __future__ import annotations
13
+
14
+ import math
15
+ from functools import partial
16
+ from statistics import NormalDist
17
+ from typing import TYPE_CHECKING, TypeAlias, Union
18
+ import numpy as np
19
+
20
+ from rateslib.rs import (
21
+ Dual,
22
+ Dual2,
23
+ Variable,
24
+ ADOrder,
25
+ _dsolve1,
26
+ _dsolve2,
27
+ _fdsolve1,
28
+ _fdsolve2,
29
+ set_order,
30
+ ift_1dim,
31
+ newton_1dim,
32
+ newton_ndim,
33
+ )
34
+
35
+ PRECISION = 1e-14
36
+ FLOATS = float | np.float16 | np.float32 | np.float64 | np.longdouble
37
+ INTS = int | np.int8 | np.int16 | np.int32 | np.int32 | np.int64
38
+
39
+ Number: TypeAlias = Union[float, Dual, Dual2, Variable]
40
+
41
+ if TYPE_CHECKING:
42
+ from rateslib.typing_rateslib import (
43
+ # Any,
44
+ Arr1dF64,
45
+ Arr1dObj,
46
+ Arr2dF64,
47
+ Arr2dObj,
48
+ # DualTypes,
49
+ # Number,
50
+ # Result,
51
+ Sequence,
52
+ )
53
+
54
+
55
+ def set_order_convert(
56
+ val: Number, order: int, tag: list[str] | None, vars_from: Dual | Dual2 | None = None
57
+ ) -> Number:
58
+ """
59
+ Convert a float, :class:`Dual` or :class:`Dual2` type to a specified alternate type with
60
+ tagged variables.
61
+
62
+ Parameters
63
+ ----------
64
+ val : float, Dual, Dual2, Variable
65
+ The value to convert.
66
+ order : int
67
+ The AD order to convert the value to if necessary.
68
+ tag : list of str, optional
69
+ The variable name(s) if upcasting a float to a Dual or Dual2
70
+ vars_from : optional, Dual or Dual2
71
+ A pre-existing Dual of correct order from which the Vars are extracted. Improves efficiency
72
+ when given.
73
+
74
+ Returns
75
+ -------
76
+ float, Dual, Dual2
77
+
78
+ Notes
79
+ -----
80
+ This function is used for AD variable management.
81
+
82
+ ``tag`` and ``vars_from`` are only used when floats are upcast and the variables need to be
83
+ specifically define.
84
+
85
+ """
86
+ if isinstance(val, FLOATS | INTS):
87
+ _ = [] if tag is None else tag
88
+ if order == 0:
89
+ return float(val)
90
+ elif order == 1:
91
+ if vars_from is None:
92
+ return Dual(val, _, [])
93
+ elif isinstance(vars_from, Dual):
94
+ return Dual.vars_from(vars_from, val, _, [])
95
+ else:
96
+ raise TypeError("`vars_from` must be a Dual when converting to ADOrder:1.")
97
+ elif order == 2:
98
+ if vars_from is None:
99
+ return Dual2(val, _, [], [])
100
+ elif isinstance(vars_from, Dual2):
101
+ return Dual2.vars_from(vars_from, val, _, [], [])
102
+ else:
103
+ raise TypeError("`vars_from` must be a Dual2 when converting to ADOrder:2.")
104
+ # else val is Dual or Dual2 so convert directly
105
+ return set_order(val, order)
106
+
107
+
108
+ def gradient(
109
+ dual: Number,
110
+ vars: Sequence[str] | None = None, # noqa: A002
111
+ order: int = 1,
112
+ keep_manifold: bool = False,
113
+ ) -> Arr1dF64 | Arr2dF64:
114
+ """
115
+ Return derivatives of a dual number.
116
+
117
+ Parameters
118
+ ----------
119
+ dual : Dual, Dual2, Variable, float
120
+ The dual variable from which to derive derivatives.
121
+ vars : str, tuple, list optional
122
+ Name of the variables which to return gradients for. If not given
123
+ defaults to all vars attributed to the instance.
124
+ order : {1, 2}
125
+ Whether to return the first or second derivative of the dual number.
126
+ Second order will raise if applied to a ``Dual`` and not ``Dual2`` instance.
127
+ keep_manifold : bool
128
+ If ``order`` is 1 and the type is ``Dual2`` one can return a ``Dual2``
129
+ where the ``dual2`` values are converted to ``dual`` values to represent
130
+ a first order manifold of the first derivative (and the ``dual2`` values
131
+ set to zero). Useful for propagation in iterations.
132
+
133
+ Returns
134
+ -------
135
+ float, ndarray, Dual2
136
+ """
137
+ _validate_keep_manifold(keep_manifold, order, dual)
138
+
139
+ if order == 1:
140
+ if not isinstance(dual, Dual | Dual2 | Variable):
141
+ if isinstance(dual, float | int):
142
+ return np.zeros(shape=(len(vars) if vars is not None else 0,))
143
+ else:
144
+ raise TypeError("Can call `gradient` only on dual-type variables.")
145
+ if isinstance(dual, Variable):
146
+ dual = Dual(dual.real, vars=dual.vars, dual=dual.dual)
147
+ if vars is None and not keep_manifold:
148
+ return dual.dual
149
+ elif vars is not None and not keep_manifold:
150
+ return dual.grad1(vars)
151
+ _ = dual.grad1_manifold(dual.vars if vars is None else vars) # type: ignore[union-attr]
152
+ return np.asarray(_)
153
+
154
+ elif order == 2:
155
+ if not isinstance(dual, Dual | Dual2 | Variable):
156
+ if isinstance(dual, float | int):
157
+ n = len(vars) if vars is not None else 0
158
+ return np.zeros(shape=(n, n))
159
+ else:
160
+ raise TypeError("Can call `gradient` only on dual-type variables.")
161
+ if isinstance(dual, Variable):
162
+ dual = Dual2(dual.real, vars=dual.vars, dual=dual.dual, dual2=[])
163
+ elif isinstance(dual, Dual):
164
+ raise TypeError("Dual type cannot derive second order automatic derivatives.")
165
+
166
+ if vars is None:
167
+ return 2.0 * dual.dual2
168
+ else:
169
+ return dual.grad2(vars)
170
+ else:
171
+ raise ValueError("`order` must be in {1, 2} for gradient calculation.")
172
+
173
+
174
+ def _validate_keep_manifold(keep_manifold: bool, order: int, dual: Number) -> None:
175
+ """Validate the keep_manifold argument for gradient."""
176
+ if keep_manifold and not isinstance(dual, Dual2):
177
+ if isinstance(dual, Dual):
178
+ raise TypeError("Dual type cannot perform `keep_manifold`.")
179
+ elif isinstance(dual, Variable):
180
+ raise TypeError("Variable type cannot perform `keep_manifold`.")
181
+ else:
182
+ raise TypeError("Float type cannot perform `keep_manifold`.")
183
+
184
+
185
+ def dual_exp(x: Number) -> Number:
186
+ """
187
+ Calculate the exponential value of a regular int or float or a dual number.
188
+
189
+ Parameters
190
+ ----------
191
+ x : int, float, Dual, Dual2, Variable
192
+ Value to calculate exponent of.
193
+
194
+ Returns
195
+ -------
196
+ float, Dual, Dual2
197
+ """
198
+ if isinstance(x, Dual | Dual2 | Variable | Variable):
199
+ return x.__exp__()
200
+ return math.exp(x)
201
+
202
+
203
+ def dual_log(x: Number, base: int | None = None) -> Number:
204
+ """
205
+ Calculate the logarithm of a regular int or float or a dual number.
206
+
207
+ Parameters
208
+ ----------
209
+ x : int, float, Dual, Dual2, Variable
210
+ Value to calculate exponent of.
211
+ base : int, float, optional
212
+ Base of the logarithm. Defaults to e to compute natural logarithm
213
+
214
+ Returns
215
+ -------
216
+ float, Dual, Dual2
217
+ """
218
+ if isinstance(x, Dual | Dual2 | Variable | Variable):
219
+ val = x.__log__()
220
+ if base is None:
221
+ return val
222
+ else:
223
+ return val * (1 / math.log(base))
224
+ elif base is None:
225
+ return math.log(x)
226
+ else:
227
+ return math.log(x, base)
228
+
229
+
230
+ def dual_norm_pdf(x: Number) -> Number:
231
+ """
232
+ Return the standard normal probability density function.
233
+
234
+ Parameters
235
+ ----------
236
+ x : float, Dual, Dual2, Variable
237
+
238
+ Returns
239
+ -------
240
+ float, Dual, Dual2
241
+ """
242
+ return dual_exp(-0.5 * x**2) / math.sqrt(2.0 * math.pi)
243
+
244
+
245
+ def dual_norm_cdf(x: Number) -> Number:
246
+ """
247
+ Return the cumulative standard normal distribution for given value.
248
+
249
+ Parameters
250
+ ----------
251
+ x : float, Dual, Dual2, Variable
252
+
253
+ Returns
254
+ -------
255
+ float, Dual, Dual2
256
+ """
257
+ if isinstance(x, Dual | Dual2 | Variable):
258
+ return x.__norm_cdf__()
259
+ else:
260
+ return NormalDist().cdf(x)
261
+
262
+
263
+ def dual_inv_norm_cdf(x: Number) -> Number:
264
+ """
265
+ Return the inverse cumulative standard normal distribution for given value.
266
+
267
+ Parameters
268
+ ----------
269
+ x : float, Dual, Dual2, Variable
270
+
271
+ Returns
272
+ -------
273
+ float, Dual, Dual2
274
+ """
275
+ if isinstance(x, Dual | Dual2 | Variable):
276
+ return x.__norm_inv_cdf__()
277
+ else:
278
+ return NormalDist().inv_cdf(x)
279
+
280
+
281
+ def dual_solve(
282
+ A: Arr2dObj | Arr2dF64,
283
+ b: Arr1dObj | Arr1dF64,
284
+ allow_lsq: bool = False,
285
+ types: tuple[type[float] | type[Dual] | type[Dual2], type[float] | type[Dual] | type[Dual2]] = (
286
+ Dual,
287
+ Dual,
288
+ ),
289
+ ) -> Arr1dObj | Arr1dF64:
290
+ """
291
+ Solve a linear system of equations involving dual number data types.
292
+
293
+ The `x` value is found for the equation :math:`Ax=b`.
294
+
295
+ .. warning::
296
+
297
+ This method has not yet implemented :class:`~rateslib.dual.Variable` types.
298
+
299
+ Parameters
300
+ ----------
301
+ A: 2-d array
302
+ Left side matrix of values.
303
+ b: 1-d array
304
+ Right side vector of values.
305
+ allow_lsq: bool
306
+ Whether to allow solutions for non-square `A`, i.e. when `len(b) > len(x)`.
307
+ types: tuple
308
+ Defining the input data type elements of `A` and `b`, e.g. (float, float) or (Dual, Dual).
309
+
310
+ Returns
311
+ -------
312
+ 1-d array
313
+ """
314
+ if types == (float, float):
315
+ # Use basic Numpy LinAlg
316
+ if allow_lsq:
317
+ return np.linalg.lstsq(A, b, rcond=None)[0] # type: ignore[arg-type]
318
+ else:
319
+ return np.linalg.solve(A, b) # type: ignore[arg-type]
320
+
321
+ # Move to Rust implementation
322
+ if types in [(Dual, float), (Dual2, float)]:
323
+ raise TypeError(
324
+ "Not implemented for type crossing. Use (Dual, Dual) or (Dual2, Dual2). It is no less"
325
+ "efficient to preconvert `b` to dual types and then solve.",
326
+ )
327
+
328
+ def order_from_type(t: float | Dual | Dual2):
329
+ if t is float:
330
+ return 0
331
+ elif t is Dual:
332
+ return 1
333
+ else:
334
+ return 2
335
+
336
+ A_ = np.vectorize(
337
+ partial(set_order_convert, tag=[], order=order_from_type(types[0]), vars_from=None)
338
+ )(A)
339
+ b_ = np.vectorize(
340
+ partial(set_order_convert, tag=[], order=order_from_type(types[1]), vars_from=None)
341
+ )(b)
342
+
343
+ a_ = [item for sublist in A_.tolist() for item in sublist] # 1D array of A_
344
+ b_ = b_[:, 0].tolist()
345
+
346
+ if types == (Dual, Dual):
347
+ return np.array(_dsolve1(a_, b_, allow_lsq))[:, None]
348
+ elif types == (Dual2, Dual2):
349
+ return np.array(_dsolve2(a_, b_, allow_lsq))[:, None]
350
+ elif types == (float, Dual):
351
+ return np.array(_fdsolve1(A_, b_, allow_lsq))[:, None]
352
+ elif types == (float, Dual2):
353
+ return np.array(_fdsolve2(A_, b_, allow_lsq))[:, None]
354
+ else:
355
+ raise TypeError(
356
+ "Provided `types` argument are not permitted. Must be a 2-tuple with "
357
+ "elements from {float, Dual, Dual2}"
358
+ )
359
+
360
+
361
+ __all__ = [
362
+ "Dual",
363
+ "Dual2",
364
+ "Variable",
365
+ "ADOrder",
366
+ "dual_exp",
367
+ "dual_log",
368
+ "dual_solve",
369
+ "dual_norm_cdf",
370
+ "dual_inv_norm_cdf",
371
+ "gradient",
372
+ "set_order",
373
+ "ift_1dim",
374
+ "newton_1dim",
375
+ "newton_ndim",
376
+ ]
rateslib/ad/utils.py ADDED
@@ -0,0 +1,22 @@
1
+ # SPDX-License-Identifier: LicenseRef-Rateslib-Dual
2
+ #
3
+ # Copyright (c) 2026 Siffrorna Technology Limited
4
+ #
5
+ # Dual-licensed: Free Educational Licence or Paid Commercial Licence (commercial/professional use)
6
+ # Source-available, not open source.
7
+ #
8
+ # See LICENSE and https://rateslib.com/py/en/latest/i_licence.html for details,
9
+ # and/or contact info (at) rateslib (dot) com
10
+ ####################################################################################################
11
+
12
+ from __future__ import annotations
13
+
14
+ from rateslib.rs import Dual, Dual2, Variable
15
+
16
+
17
+ def _abs_float(val: float | Dual | Dual2 | Variable) -> float:
18
+ """Overload the abs() builtin to return the abs of the real component only"""
19
+ if isinstance(val, Dual | Dual2 | Variable):
20
+ return abs(val.real)
21
+ else:
22
+ return abs(val)