rustedscithe 0.1.1__cp310-abi3-win_amd64.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.
@@ -0,0 +1,34 @@
1
+ """Public Python facade for the RustedSciThe extension.
2
+
3
+ The compiled module is kept private as ``rustedscithe._native``. Re-exporting
4
+ its classes here preserves the simple public import while allowing a small
5
+ Python-native convenience layer to live beside the extension in wheels.
6
+ """
7
+
8
+ from . import _native
9
+ from ._native import * # noqa: F401,F403 - stable extension surface
10
+ from .facade import (
11
+ capabilities,
12
+ create_bvp,
13
+ create_ivp,
14
+ create_fit,
15
+ create_nonlinear,
16
+ fit,
17
+ render_table,
18
+ run_task_batch,
19
+ run_task_batch_report,
20
+ solve_bvp,
21
+ solve_ivp,
22
+ solve_nonlinear,
23
+ task,
24
+ write_table,
25
+ )
26
+
27
+ __all__ = [
28
+ name for name in dir(_native) if not name.startswith("_")
29
+ ] + [
30
+ "capabilities", "create_bvp", "create_fit", "create_ivp",
31
+ "create_nonlinear", "fit", "render_table", "run_task_batch",
32
+ "run_task_batch_report", "solve_bvp",
33
+ "solve_ivp", "solve_nonlinear", "task", "write_table",
34
+ ]
@@ -0,0 +1,4 @@
1
+ [diffend] Oversized file quarantined before diffing.
2
+ name: rustedscithe/_native.pyd
3
+ size: 46345216 bytes
4
+ sha256: 1a018fe2487c25106bb5222850fadfbaaab8b10c305847e40bb3b5d411630df6
rustedscithe/facade.py ADDED
@@ -0,0 +1,374 @@
1
+ """Small Python-native convenience layer over the compiled extension."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from collections.abc import Iterable, Mapping
7
+ from typing import Any
8
+
9
+ from numpy.typing import ArrayLike
10
+
11
+ from . import _native
12
+
13
+ Expr = _native.Expr
14
+ SymbolicSystem = _native.SymbolicSystem
15
+ SolverOptions = _native.SolverOptions
16
+ TaskDocument = _native.TaskDocument
17
+ BvpSciSolver = _native.BvpSciSolver
18
+ BvpDampSolver = _native.BvpDampSolver
19
+
20
+
21
+ def _owned_sequence(value: Any) -> Any:
22
+ """Convert NumPy-like containers before crossing into the Rust API."""
23
+
24
+ tolist = getattr(value, "tolist", None)
25
+ return tolist() if callable(tolist) else value
26
+
27
+
28
+ def _method_name(method: str) -> str:
29
+ normalized = method.strip().lower().replace("-", "_").replace(" ", "_")
30
+ aliases = {
31
+ "bdf": "bdf", "radau": "radau5", "radau5": "radau5",
32
+ "be": "backward_euler", "backward_euler": "backward_euler",
33
+ "lsode": "lsode2", "lsode2": "lsode2",
34
+ }
35
+ if normalized not in aliases:
36
+ raise ValueError(
37
+ "unsupported IVP method; expected bdf, radau5, backward_euler or lsode2"
38
+ )
39
+ return aliases[normalized]
40
+
41
+
42
+ def create_ivp(
43
+ method: str,
44
+ expressions: list[Expr],
45
+ state_variables: list[str],
46
+ time_variable: str,
47
+ y0: ArrayLike,
48
+ t0: float,
49
+ t_end: float,
50
+ max_step: float,
51
+ *,
52
+ parameters: list[str] | None = None,
53
+ parameter_values: ArrayLike | None = None,
54
+ options: SolverOptions | None = None,
55
+ **solver_options: Any,
56
+ ) -> Any:
57
+ """Construct a native IVP solver with one common Python signature."""
58
+
59
+ family = _method_name(method)
60
+ if family == "bdf":
61
+ return _native.BdfSolver(
62
+ expressions, state_variables, time_variable, _owned_sequence(y0), t0, t_end, max_step,
63
+ options=options, parameters=parameters,
64
+ parameter_values=_owned_sequence(parameter_values), **solver_options,
65
+ )
66
+ if family == "radau5":
67
+ # Radau owns y0 at solve time; it is not part of symbolic preparation.
68
+ return _native.RadauSolver(
69
+ expressions, state_variables, time_variable, t0, t_end, max_step,
70
+ options=options, parameters=parameters, **solver_options,
71
+ )
72
+ if family == "backward_euler":
73
+ return _native.BackwardEulerSolver(
74
+ expressions, state_variables, time_variable, _owned_sequence(y0), t0, t_end,
75
+ h=max_step, options=options, parameters=parameters,
76
+ parameter_values=_owned_sequence(parameter_values), **solver_options,
77
+ )
78
+ lsode_method = solver_options.pop("method", "bdf")
79
+ return _native.Lsode2PythonSolver(
80
+ expressions, state_variables, time_variable, _owned_sequence(y0), t0, t_end, max_step,
81
+ options=options, method=lsode_method, parameters=parameters,
82
+ parameter_values=_owned_sequence(parameter_values), **solver_options,
83
+ )
84
+
85
+
86
+ def solve_ivp(
87
+ method: str,
88
+ expressions: list[Expr],
89
+ state_variables: list[str],
90
+ time_variable: str,
91
+ y0: ArrayLike,
92
+ t0: float,
93
+ t_end: float,
94
+ max_step: float,
95
+ *,
96
+ parameters: list[str] | None = None,
97
+ parameter_values: ArrayLike | None = None,
98
+ options: SolverOptions | None = None,
99
+ **solver_options: Any,
100
+ ) -> Any:
101
+ """Construct and execute one IVP through the native lifecycle."""
102
+
103
+ solver = create_ivp(
104
+ method, expressions, state_variables, time_variable, y0, t0, t_end,
105
+ max_step, parameters=parameters, parameter_values=parameter_values,
106
+ options=options, **solver_options,
107
+ )
108
+ if _method_name(method) == "radau5":
109
+ return solver.solve(_owned_sequence(y0), _owned_sequence(parameter_values))
110
+ return solver.solve()
111
+
112
+
113
+ def _bvp_method_name(method: str) -> str:
114
+ """Normalize BVP aliases without hiding solver-specific capabilities."""
115
+
116
+ normalized = method.strip().lower().replace("-", "_").replace(" ", "_")
117
+ aliases = {
118
+ "bvp_sci": "bvp_sci",
119
+ "scipy": "bvp_sci",
120
+ "bvp_damp": "bvp_damp",
121
+ "damped": "bvp_damp",
122
+ }
123
+ if normalized not in aliases:
124
+ raise ValueError("unsupported BVP method; expected bvp_sci or bvp_damp")
125
+ return aliases[normalized]
126
+
127
+
128
+ def create_bvp(
129
+ method: str,
130
+ expressions: list[Expr],
131
+ state_variables: list[str],
132
+ boundary_data: Any,
133
+ mesh: ArrayLike,
134
+ initial_state: Any,
135
+ *,
136
+ options: SolverOptions | None = None,
137
+ **solver_options: Any,
138
+ ) -> Any:
139
+ """Construct a BVP solver while preserving native solver options.
140
+
141
+ ``boundary_data`` is a list of boundary expressions for ``bvp_sci`` and a
142
+ ``{state: [(position, value), ...]}`` mapping for ``bvp_damp``. Remaining
143
+ keyword arguments are forwarded unchanged to the selected native adapter.
144
+ """
145
+
146
+ family = _bvp_method_name(method)
147
+ solver_type = BvpSciSolver if family == "bvp_sci" else BvpDampSolver
148
+ return solver_type(
149
+ expressions,
150
+ state_variables,
151
+ boundary_data,
152
+ _owned_sequence(mesh),
153
+ _owned_sequence(initial_state),
154
+ options=options,
155
+ **solver_options,
156
+ )
157
+
158
+
159
+ def solve_bvp(
160
+ method: str,
161
+ expressions: list[Expr],
162
+ state_variables: list[str],
163
+ boundary_data: Any,
164
+ mesh: ArrayLike,
165
+ initial_state: Any,
166
+ *,
167
+ options: SolverOptions | None = None,
168
+ **solver_options: Any,
169
+ ) -> Any:
170
+ """Construct and execute one BVP through its native lifecycle."""
171
+
172
+ solver = create_bvp(
173
+ method,
174
+ expressions,
175
+ state_variables,
176
+ boundary_data,
177
+ mesh,
178
+ initial_state,
179
+ options=options,
180
+ **solver_options,
181
+ )
182
+ return solver.solve()
183
+
184
+
185
+ def solve_nonlinear(
186
+ system: SymbolicSystem,
187
+ initial_guess: ArrayLike,
188
+ *,
189
+ parameters: ArrayLike | None = None,
190
+ **solver_options: Any,
191
+ ) -> Any:
192
+ """Create and solve a square nonlinear system natively."""
193
+
194
+ solver = create_nonlinear(system, **solver_options)
195
+ return solver.solve(_owned_sequence(initial_guess), _owned_sequence(parameters))
196
+
197
+
198
+ def create_nonlinear(system: SymbolicSystem, **solver_options: Any) -> Any:
199
+ """Construct a prepared-capable nonlinear solver without solving it."""
200
+
201
+ return _native.NonlinearSolver(system, **solver_options)
202
+
203
+
204
+ def fit(
205
+ equations: list[Expr],
206
+ initial_guess: ArrayLike,
207
+ *,
208
+ parameters: ArrayLike | None = None,
209
+ **solver_options: Any,
210
+ ) -> Any:
211
+ """Create and solve a symbolic least-squares problem natively."""
212
+
213
+ solver = create_fit(equations, _owned_sequence(initial_guess), **solver_options)
214
+ return solver.solve() if parameters is None else solver.solve_with_parameters(_owned_sequence(parameters))
215
+
216
+
217
+ def create_fit(
218
+ equations: list[Expr],
219
+ initial_guess: ArrayLike,
220
+ **solver_options: Any,
221
+ ) -> Any:
222
+ """Construct a symbolic fitting solver while retaining its native API."""
223
+
224
+ return _native.FittingSolver(
225
+ equations,
226
+ _owned_sequence(initial_guess),
227
+ **solver_options,
228
+ )
229
+
230
+
231
+ def task(source_or_path: str | Path) -> TaskDocument:
232
+ """Parse a task document from text or a filesystem path."""
233
+
234
+ raw = str(source_or_path)
235
+ if isinstance(source_or_path, Path) or ("\n" not in raw and "\r" not in raw):
236
+ candidate = Path(source_or_path)
237
+ if candidate.exists() and candidate.is_file():
238
+ return TaskDocument.from_file(str(candidate))
239
+ return TaskDocument.parse(raw)
240
+
241
+
242
+ def capabilities(solver: str) -> Any:
243
+ """Forward the canonical capability descriptor from the extension."""
244
+
245
+ return _native.capabilities(solver)
246
+
247
+
248
+ def render_table(rows: Iterable[Mapping[str, Any]], columns: Iterable[str] | None = None) -> str:
249
+ """Render compact deterministic ASCII tables without capturing process noise.
250
+
251
+ The function is intentionally dependency-free and accepts detached scalar
252
+ values, so it can be used in a long-running batch even when an optional
253
+ plotting or reporting package is unavailable.
254
+ """
255
+
256
+ materialized = [dict(row) for row in rows]
257
+ if not materialized:
258
+ return ""
259
+ names = list(columns) if columns is not None else list(materialized[0])
260
+ if not names:
261
+ return ""
262
+ values = [[str(row.get(name, "")) for name in names] for row in materialized]
263
+ widths = [max(len(name), *(len(row[index]) for row in values)) for index, name in enumerate(names)]
264
+ border = "+" + "+".join("-" * (width + 2) for width in widths) + "+"
265
+ line = lambda row: "| " + " | ".join(value.ljust(width) for value, width in zip(row, widths)) + " |"
266
+ return "\n".join([border, line(names), border, *[line(row) for row in values], border])
267
+
268
+
269
+ def write_table(path: str | Path, rows: Iterable[Mapping[str, Any]], columns: Iterable[str] | None = None) -> None:
270
+ """Write one compact table and nothing else to ``path``."""
271
+
272
+ Path(path).write_text(render_table(rows, columns), encoding="utf-8")
273
+
274
+
275
+ def run_task_batch(
276
+ sources: Iterable[str | Path],
277
+ *,
278
+ continue_on_error: bool = True,
279
+ ) -> list[dict[str, Any]]:
280
+ """Run task documents independently and return a compact typed summary.
281
+
282
+ A failed task never prevents later tasks when ``continue_on_error`` is
283
+ true. The returned row preserves the Python exception class so callers can
284
+ route parser, BVP and solver failures without matching message text.
285
+ """
286
+
287
+ rows: list[dict[str, Any]] = []
288
+ for index, source in enumerate(sources, start=1):
289
+ raw_source = str(source)
290
+ # A full inline document is useful to the parser but poisonous to a
291
+ # compact report column. Keep paths readable and give inline jobs a
292
+ # stable label; the caller still has the original source object.
293
+ source_label = (
294
+ f"<inline:{index}>"
295
+ if "\n" in raw_source or "\r" in raw_source
296
+ else raw_source
297
+ )
298
+ row: dict[str, Any] = {"source": source_label}
299
+ try:
300
+ document = task(source)
301
+ result = document.run_bvp() if document.kind == "bvp" else document.run()
302
+ route = document.route()
303
+ row.update(
304
+ {
305
+ "status": "ok",
306
+ "kind": document.kind,
307
+ "route": route,
308
+ "result": result,
309
+ }
310
+ )
311
+ except Exception as error: # keep the batch alive, but retain its type
312
+ row.update(
313
+ {
314
+ "status": "error",
315
+ "kind": "unknown",
316
+ "error_type": type(error).__name__,
317
+ "error": str(error),
318
+ }
319
+ )
320
+ if not continue_on_error:
321
+ rows.append(row)
322
+ raise
323
+ rows.append(row)
324
+ return rows
325
+
326
+
327
+ def run_task_batch_report(
328
+ sources: Iterable[str | Path],
329
+ *,
330
+ path: str | Path | None = None,
331
+ continue_on_error: bool = True,
332
+ ) -> str:
333
+ """Run a task batch and return a compact, scalar-only report.
334
+
335
+ Numerical result objects remain available in :func:`run_task_batch`, but
336
+ this report deliberately contains only stable routing/status fields. It
337
+ is therefore safe to write as a small Markdown/ASCII artifact without
338
+ serializing trajectories or capturing compiler output.
339
+ """
340
+
341
+ rows = run_task_batch(sources, continue_on_error=continue_on_error)
342
+ report_rows: list[dict[str, Any]] = []
343
+ columns = [
344
+ "source",
345
+ "status",
346
+ "kind",
347
+ "method",
348
+ "solver",
349
+ "dispatch",
350
+ "frontend",
351
+ "execution",
352
+ "matrix_layout",
353
+ "error_type",
354
+ ]
355
+ for row in rows:
356
+ route = row.get("route", {})
357
+ report_rows.append(
358
+ {
359
+ "source": row.get("source", ""),
360
+ "status": row.get("status", ""),
361
+ "kind": row.get("kind", ""),
362
+ "method": route.get("method", ""),
363
+ "solver": route.get("solver", ""),
364
+ "dispatch": route.get("dispatch", ""),
365
+ "frontend": route.get("frontend", ""),
366
+ "execution": route.get("execution", ""),
367
+ "matrix_layout": route.get("matrix_layout", ""),
368
+ "error_type": row.get("error_type", ""),
369
+ }
370
+ )
371
+ report = render_table(report_rows, columns=columns)
372
+ if path is not None:
373
+ write_table(path, report_rows, columns=columns)
374
+ return report
rustedscithe/py.typed ADDED
@@ -0,0 +1 @@
1
+ # Marker file for PEP 561 typed-package discovery.