usel 1.0.4__py3-none-any.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 (53) hide show
  1. usel/__init__.py +22 -0
  2. usel/cli/__init__.py +7 -0
  3. usel/cli/main.py +151 -0
  4. usel/config/__init__.py +7 -0
  5. usel/config/loader.py +70 -0
  6. usel/exceptions/__init__.py +45 -0
  7. usel/io/__init__.py +17 -0
  8. usel/io/readers.py +55 -0
  9. usel/io/writers.py +47 -0
  10. usel/linalg/__init__.py +14 -0
  11. usel/linalg/decompositions.py +42 -0
  12. usel/logging/__init__.py +12 -0
  13. usel/logging/logger.py +74 -0
  14. usel/math/__init__.py +8 -0
  15. usel/math/matrix.py +212 -0
  16. usel/math/transforms.py +48 -0
  17. usel/random/__init__.py +19 -0
  18. usel/random/generators.py +44 -0
  19. usel/schrodinger/__init__.py +60 -0
  20. usel/schrodinger/boundary_conditions.py +75 -0
  21. usel/schrodinger/evolution.py +61 -0
  22. usel/schrodinger/grids.py +85 -0
  23. usel/schrodinger/hamiltonian.py +74 -0
  24. usel/schrodinger/normalization.py +65 -0
  25. usel/schrodinger/observables.py +86 -0
  26. usel/schrodinger/operators.py +111 -0
  27. usel/schrodinger/potentials.py +67 -0
  28. usel/schrodinger/propagators.py +25 -0
  29. usel/schrodinger/results.py +67 -0
  30. usel/schrodinger/solver.py +325 -0
  31. usel/schrodinger/validation.py +33 -0
  32. usel/schrodinger/visualization.py +54 -0
  33. usel/schrodinger/wavefunctions.py +96 -0
  34. usel/solvers/__init__.py +22 -0
  35. usel/solvers/crank_nicolson.py +33 -0
  36. usel/solvers/finite_difference.py +29 -0
  37. usel/solvers/imaginary_time.py +24 -0
  38. usel/solvers/integration.py +64 -0
  39. usel/solvers/ode.py +84 -0
  40. usel/solvers/rk4.py +31 -0
  41. usel/solvers/root_finding.py +132 -0
  42. usel/solvers/split_operator.py +39 -0
  43. usel/utils/__init__.py +26 -0
  44. usel/utils/precision.py +24 -0
  45. usel/utils/profiling.py +56 -0
  46. usel/utils/timing.py +68 -0
  47. usel/utils/validation.py +31 -0
  48. usel-1.0.4.dist-info/METADATA +157 -0
  49. usel-1.0.4.dist-info/RECORD +53 -0
  50. usel-1.0.4.dist-info/WHEEL +5 -0
  51. usel-1.0.4.dist-info/entry_points.txt +2 -0
  52. usel-1.0.4.dist-info/licenses/LICENSE +21 -0
  53. usel-1.0.4.dist-info/top_level.txt +1 -0
usel/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ """usel: an open-source, CPU-only scientific computing framework.
2
+
3
+ This package provides the mathematical and computational foundation for
4
+ quantum mechanics, relativistic quantum mechanics, and computational
5
+ electromagnetics:
6
+
7
+ - Reusable matrix and linear algebra utilities
8
+ - Numerical solvers (root finding, ODEs, integration)
9
+ - FFT based frequency analysis
10
+ - Schrödinger equation solvers (bound states, wave-packet propagation)
11
+ - Dirac equation solvers (relativistic spinors, Pauli/Dirac matrices)
12
+ - Maxwell equation solvers (2D FDTD, electrostatics)
13
+ - Random number and matrix generation
14
+ - Configuration, IO, logging, and validation utilities
15
+ - A command line interface (``usel``)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ __version__ = "1.1.0"
21
+ __license__ = "MIT"
22
+ __all__ = ["__version__", "__license__"]
usel/cli/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Command line interface for usel."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from usel.cli.main import main
6
+
7
+ __all__ = ["main"]
usel/cli/main.py ADDED
@@ -0,0 +1,151 @@
1
+ """usel command line interface.
2
+
3
+ Commands
4
+ --------
5
+ usel version Print the installed usel version.
6
+ usel doctor Check environment health (Python version, dependencies).
7
+ usel benchmark Run a quick matrix-multiplication benchmark.
8
+ usel info Print platform and package information.
9
+ usel config Show or validate a configuration file.
10
+ usel test Run the test suite via pytest.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import importlib
17
+ import platform
18
+ import subprocess
19
+ import sys
20
+ import time
21
+ from collections.abc import Sequence
22
+
23
+ import usel
24
+ from usel.math.matrix import Matrix
25
+
26
+ _REQUIRED_PACKAGES = ("numpy", "scipy", "sympy")
27
+
28
+
29
+ def _cmd_version(_: argparse.Namespace) -> int:
30
+ print(f"usel version {usel.__version__}")
31
+ return 0
32
+
33
+
34
+ def _cmd_info(_: argparse.Namespace) -> int:
35
+ print("usel Info")
36
+ print("-" * 40)
37
+ print(f"Version: {usel.__version__}")
38
+ print(f"License: {usel.__license__}")
39
+ print(f"Python: {platform.python_version()}")
40
+ print(f"Platform: {platform.platform()}")
41
+ print(f"Processor: {platform.processor() or 'unknown'}")
42
+ return 0
43
+
44
+
45
+ def _cmd_doctor(_: argparse.Namespace) -> int:
46
+ print("Running usel environment check...")
47
+ ok = True
48
+
49
+ major, minor = sys.version_info[:2]
50
+ if (major, minor) >= (3, 11):
51
+ print(f"[OK] Python version {major}.{minor} >= 3.11")
52
+ else:
53
+ print(f"[FAIL] Python version {major}.{minor} < 3.11")
54
+ ok = False
55
+
56
+ for package in _REQUIRED_PACKAGES:
57
+ try:
58
+ importlib.import_module(package)
59
+ print(f"[OK] Dependency '{package}' is importable")
60
+ except ImportError:
61
+ print(f"[FAIL] Dependency '{package}' is missing")
62
+ ok = False
63
+
64
+ if ok:
65
+ print("\nAll checks passed. usel is ready to use.")
66
+ else:
67
+ print("\nSome checks failed. Please review the output above.")
68
+ return 0 if ok else 1
69
+
70
+
71
+ def _cmd_benchmark(args: argparse.Namespace) -> int:
72
+ size = args.size
73
+ print(f"Benchmarking {size}x{size} matrix multiplication...")
74
+ a = Matrix.random(size, size, seed=42)
75
+ b = Matrix.random(size, size, seed=43)
76
+
77
+ start = time.perf_counter()
78
+ _ = a @ b
79
+ elapsed = time.perf_counter() - start
80
+
81
+ print(f"Completed in {elapsed:.6f} seconds")
82
+ return 0
83
+
84
+
85
+ def _cmd_config(args: argparse.Namespace) -> int:
86
+ from usel.config.loader import load_config
87
+
88
+ try:
89
+ data = load_config(args.path)
90
+ except Exception as exc: # noqa: BLE001
91
+ print(f"[FAIL] Could not load configuration: {exc}")
92
+ return 1
93
+
94
+ print(f"[OK] Configuration file '{args.path}' loaded successfully")
95
+ print(f"Keys: {sorted(data.keys())}")
96
+ return 0
97
+
98
+
99
+ def _cmd_test(args: argparse.Namespace) -> int:
100
+ cmd = [sys.executable, "-m", "pytest"]
101
+ if args.path:
102
+ cmd.append(args.path)
103
+ result = subprocess.run(cmd, check=False) # noqa: S603
104
+ return result.returncode
105
+
106
+
107
+ def build_parser() -> argparse.ArgumentParser:
108
+ """Build and return the usel argument parser."""
109
+ parser = argparse.ArgumentParser(
110
+ prog="usel", description="usel: CPU-only scientific computing framework"
111
+ )
112
+ subparsers = parser.add_subparsers(dest="command", required=True)
113
+
114
+ subparsers.add_parser("version", help="Print the installed usel version").set_defaults(
115
+ func=_cmd_version
116
+ )
117
+ subparsers.add_parser("info", help="Print platform and package information").set_defaults(
118
+ func=_cmd_info
119
+ )
120
+ subparsers.add_parser(
121
+ "doctor", help="Check environment health (Python version, dependencies)"
122
+ ).set_defaults(func=_cmd_doctor)
123
+
124
+ benchmark_parser = subparsers.add_parser(
125
+ "benchmark", help="Run a quick matrix-multiplication benchmark"
126
+ )
127
+ benchmark_parser.add_argument(
128
+ "--size", type=int, default=200, help="Matrix dimension for the benchmark (default: 200)"
129
+ )
130
+ benchmark_parser.set_defaults(func=_cmd_benchmark)
131
+
132
+ config_parser = subparsers.add_parser("config", help="Load and validate a configuration file")
133
+ config_parser.add_argument("path", help="Path to a .yaml, .json, or .toml configuration file")
134
+ config_parser.set_defaults(func=_cmd_config)
135
+
136
+ test_parser = subparsers.add_parser("test", help="Run the test suite via pytest")
137
+ test_parser.add_argument("path", nargs="?", default=None, help="Optional test path filter")
138
+ test_parser.set_defaults(func=_cmd_test)
139
+
140
+ return parser
141
+
142
+
143
+ def main(argv: Sequence[str] | None = None) -> int:
144
+ """usel CLI entry point."""
145
+ parser = build_parser()
146
+ args = parser.parse_args(argv)
147
+ return int(args.func(args))
148
+
149
+
150
+ if __name__ == "__main__":
151
+ raise SystemExit(main())
@@ -0,0 +1,7 @@
1
+ """Configuration loading and saving for YAML, JSON, and TOML formats."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from usel.config.loader import load_config, save_config
6
+
7
+ __all__ = ["load_config", "save_config"]
usel/config/loader.py ADDED
@@ -0,0 +1,70 @@
1
+ """Load and save configuration files in YAML, JSON, or TOML format.
2
+
3
+ The format is inferred from the file extension: ``.yaml``/``.yml``,
4
+ ``.json``, or ``.toml``.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from usel.exceptions import ConfigError
14
+
15
+ _SUPPORTED_SUFFIXES = {".yaml", ".yml", ".json", ".toml"}
16
+
17
+
18
+ def load_config(filepath: str | Path) -> dict[str, Any]:
19
+ """Load a configuration file, inferring the format from its extension."""
20
+ path = Path(filepath)
21
+ if not path.exists():
22
+ raise ConfigError(f"Configuration file not found: {path}")
23
+ if path.suffix not in _SUPPORTED_SUFFIXES:
24
+ raise ConfigError(
25
+ f"Unsupported configuration format '{path.suffix}'. "
26
+ f"Supported formats: {sorted(_SUPPORTED_SUFFIXES)}"
27
+ )
28
+
29
+ try:
30
+ text = path.read_text(encoding="utf-8")
31
+ if path.suffix in (".yaml", ".yml"):
32
+ import yaml
33
+
34
+ data = yaml.safe_load(text) or {}
35
+ elif path.suffix == ".json":
36
+ data = json.loads(text) if text.strip() else {}
37
+ else: # .toml
38
+ import toml
39
+
40
+ data = toml.loads(text)
41
+ except Exception as exc: # noqa: BLE001 - re-raised as ConfigError
42
+ raise ConfigError(f"Failed to parse configuration file {path}: {exc}") from exc
43
+
44
+ if not isinstance(data, dict):
45
+ raise ConfigError(f"Configuration root must be a mapping/object, got {type(data).__name__}")
46
+ return data
47
+
48
+
49
+ def save_config(data: dict[str, Any], filepath: str | Path) -> None:
50
+ """Save a configuration dictionary, inferring the format from its extension."""
51
+ path = Path(filepath)
52
+ if path.suffix not in _SUPPORTED_SUFFIXES:
53
+ raise ConfigError(
54
+ f"Unsupported configuration format '{path.suffix}'. "
55
+ f"Supported formats: {sorted(_SUPPORTED_SUFFIXES)}"
56
+ )
57
+
58
+ try:
59
+ if path.suffix in (".yaml", ".yml"):
60
+ import yaml
61
+
62
+ path.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8")
63
+ elif path.suffix == ".json":
64
+ path.write_text(json.dumps(data, indent=2), encoding="utf-8")
65
+ else: # .toml
66
+ import toml
67
+
68
+ path.write_text(toml.dumps(data), encoding="utf-8")
69
+ except Exception as exc: # noqa: BLE001 - re-raised as ConfigError
70
+ raise ConfigError(f"Failed to write configuration file {path}: {exc}") from exc
@@ -0,0 +1,45 @@
1
+ """Custom exception hierarchy for usel."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class uselError(Exception):
7
+ """Base exception for all usel errors."""
8
+
9
+
10
+ class MatrixError(uselError):
11
+ """Raised for invalid matrix operations (shape mismatch, singularity, etc.)."""
12
+
13
+
14
+ class SolverError(uselError):
15
+ """Raised when a numerical solver fails to converge or receives bad input."""
16
+
17
+
18
+ class ConfigError(uselError):
19
+ """Raised for configuration loading, parsing, or validation failures."""
20
+
21
+
22
+ class ValidationError(uselError):
23
+ """Raised when input validation fails."""
24
+
25
+
26
+ class IOError_(uselError):
27
+ """Raised for file input/output failures.
28
+
29
+ Named ``IOError_`` to avoid shadowing the Python builtin ``IOError``.
30
+ """
31
+
32
+
33
+ class PhysicsError(uselError):
34
+ """Raised when a physics simulation encounters invalid parameters or diverges."""
35
+
36
+
37
+ __all__ = [
38
+ "uselError",
39
+ "MatrixError",
40
+ "SolverError",
41
+ "ConfigError",
42
+ "ValidationError",
43
+ "IOError_",
44
+ "PhysicsError",
45
+ ]
usel/io/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+ """File input/output utilities: CSV, JSON, NumPy binary, and plain text."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from usel.io.readers import read_csv, read_json, read_npy, read_text
6
+ from usel.io.writers import write_csv, write_json, write_npy, write_text
7
+
8
+ __all__ = [
9
+ "read_csv",
10
+ "read_json",
11
+ "read_npy",
12
+ "read_text",
13
+ "write_csv",
14
+ "write_json",
15
+ "write_npy",
16
+ "write_text",
17
+ ]
usel/io/readers.py ADDED
@@ -0,0 +1,55 @@
1
+ """Readers for CSV, JSON, NumPy binary (.npy/.npz), and plain text files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import numpy as np
10
+
11
+ from usel.exceptions import IOError_
12
+
13
+
14
+ def read_csv(filepath: str | Path, delimiter: str = ",", skip_header: int = 0) -> np.ndarray:
15
+ """Read a CSV file into a NumPy array."""
16
+ path = Path(filepath)
17
+ if not path.exists():
18
+ raise IOError_(f"CSV file not found: {path}")
19
+ try:
20
+ return np.genfromtxt(path, delimiter=delimiter, skip_header=skip_header, dtype=float)
21
+ except Exception as exc: # noqa: BLE001
22
+ raise IOError_(f"Failed to read CSV file {path}: {exc}") from exc
23
+
24
+
25
+ def read_json(filepath: str | Path) -> Any:
26
+ """Read a JSON file and return the parsed object."""
27
+ path = Path(filepath)
28
+ if not path.exists():
29
+ raise IOError_(f"JSON file not found: {path}")
30
+ try:
31
+ return json.loads(path.read_text(encoding="utf-8"))
32
+ except Exception as exc: # noqa: BLE001
33
+ raise IOError_(f"Failed to read JSON file {path}: {exc}") from exc
34
+
35
+
36
+ def read_npy(filepath: str | Path) -> np.ndarray:
37
+ """Read a NumPy binary (.npy) file into an array."""
38
+ path = Path(filepath)
39
+ if not path.exists():
40
+ raise IOError_(f"NumPy binary file not found: {path}")
41
+ try:
42
+ return np.load(path, allow_pickle=False)
43
+ except Exception as exc: # noqa: BLE001
44
+ raise IOError_(f"Failed to read .npy file {path}: {exc}") from exc
45
+
46
+
47
+ def read_text(filepath: str | Path, encoding: str = "utf-8") -> str:
48
+ """Read a plain text file and return its contents as a string."""
49
+ path = Path(filepath)
50
+ if not path.exists():
51
+ raise IOError_(f"Text file not found: {path}")
52
+ try:
53
+ return path.read_text(encoding=encoding)
54
+ except Exception as exc: # noqa: BLE001
55
+ raise IOError_(f"Failed to read text file {path}: {exc}") from exc
usel/io/writers.py ADDED
@@ -0,0 +1,47 @@
1
+ """Writers for CSV, JSON, NumPy binary (.npy), and plain text files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import numpy as np
10
+
11
+ from usel.exceptions import IOError_
12
+
13
+
14
+ def write_csv(data: np.ndarray, filepath: str | Path, delimiter: str = ",") -> None:
15
+ """Write a NumPy array to a CSV file."""
16
+ path = Path(filepath)
17
+ try:
18
+ np.savetxt(path, np.asarray(data), delimiter=delimiter)
19
+ except Exception as exc: # noqa: BLE001
20
+ raise IOError_(f"Failed to write CSV file {path}: {exc}") from exc
21
+
22
+
23
+ def write_json(data: Any, filepath: str | Path, indent: int = 2) -> None:
24
+ """Write a JSON-serializable object to a file."""
25
+ path = Path(filepath)
26
+ try:
27
+ path.write_text(json.dumps(data, indent=indent), encoding="utf-8")
28
+ except Exception as exc: # noqa: BLE001
29
+ raise IOError_(f"Failed to write JSON file {path}: {exc}") from exc
30
+
31
+
32
+ def write_npy(data: np.ndarray, filepath: str | Path) -> None:
33
+ """Write a NumPy array to a .npy binary file."""
34
+ path = Path(filepath)
35
+ try:
36
+ np.save(path, np.asarray(data))
37
+ except Exception as exc: # noqa: BLE001
38
+ raise IOError_(f"Failed to write .npy file {path}: {exc}") from exc
39
+
40
+
41
+ def write_text(content: str, filepath: str | Path, encoding: str = "utf-8") -> None:
42
+ """Write a string to a plain text file."""
43
+ path = Path(filepath)
44
+ try:
45
+ path.write_text(content, encoding=encoding)
46
+ except Exception as exc: # noqa: BLE001
47
+ raise IOError_(f"Failed to write text file {path}: {exc}") from exc
@@ -0,0 +1,14 @@
1
+ """Linear algebra utilities: eigen decompositions, SVD, QR, LU, Cholesky."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from usel.linalg.decompositions import (
6
+ cholesky,
7
+ eigenvalues,
8
+ eigenvectors,
9
+ lu,
10
+ qr,
11
+ svd,
12
+ )
13
+
14
+ __all__ = ["eigenvalues", "eigenvectors", "svd", "qr", "lu", "cholesky"]
@@ -0,0 +1,42 @@
1
+ """Functional wrappers around common matrix decompositions.
2
+
3
+ These functions accept and return :class:`usel.math.Matrix` instances
4
+ (or raw NumPy arrays where noted), providing a functional API alongside the
5
+ object-oriented methods already available on ``Matrix``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import numpy as np
11
+
12
+ from usel.math.matrix import Matrix
13
+
14
+
15
+ def eigenvalues(matrix: Matrix) -> np.ndarray:
16
+ """Return the eigenvalues of a square matrix."""
17
+ return matrix.eigenvalues()
18
+
19
+
20
+ def eigenvectors(matrix: Matrix) -> tuple[np.ndarray, Matrix]:
21
+ """Return (eigenvalues, eigenvectors) of a square matrix."""
22
+ return matrix.eigenvectors()
23
+
24
+
25
+ def svd(matrix: Matrix) -> tuple[Matrix, np.ndarray, Matrix]:
26
+ """Singular value decomposition. Returns (U, singular_values, Vt)."""
27
+ return matrix.decompose_svd()
28
+
29
+
30
+ def qr(matrix: Matrix) -> tuple[Matrix, Matrix]:
31
+ """QR decomposition. Returns (Q, R)."""
32
+ return matrix.decompose_qr()
33
+
34
+
35
+ def lu(matrix: Matrix) -> tuple[Matrix, Matrix, Matrix]:
36
+ """LU decomposition with partial pivoting. Returns (P, L, U)."""
37
+ return matrix.decompose_lu()
38
+
39
+
40
+ def cholesky(matrix: Matrix) -> Matrix:
41
+ """Cholesky decomposition for symmetric positive-definite matrices."""
42
+ return matrix.decompose_cholesky()
@@ -0,0 +1,12 @@
1
+ """Logging utilities: console logger, file logger, debug mode, performance logger."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from usel.logging.logger import (
6
+ PerformanceLogger,
7
+ get_console_logger,
8
+ get_file_logger,
9
+ set_debug_mode,
10
+ )
11
+
12
+ __all__ = ["get_console_logger", "get_file_logger", "set_debug_mode", "PerformanceLogger"]
usel/logging/logger.py ADDED
@@ -0,0 +1,74 @@
1
+ """Console and file loggers, debug mode toggling, and a performance logger."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import time
7
+ from pathlib import Path
8
+
9
+ _DEFAULT_FORMAT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
10
+
11
+
12
+ def get_console_logger(name: str = "usel", level: int = logging.INFO) -> logging.Logger:
13
+ """Return a logger configured to write formatted messages to the console."""
14
+ logger = logging.getLogger(name)
15
+ logger.setLevel(level)
16
+ if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
17
+ handler = logging.StreamHandler()
18
+ handler.setFormatter(logging.Formatter(_DEFAULT_FORMAT))
19
+ logger.addHandler(handler)
20
+ return logger
21
+
22
+
23
+ def get_file_logger(
24
+ name: str = "usel", filepath: str | Path = "usel.log", level: int = logging.INFO
25
+ ) -> logging.Logger:
26
+ """Return a logger configured to write formatted messages to a file."""
27
+ logger = logging.getLogger(name)
28
+ logger.setLevel(level)
29
+ path = Path(filepath)
30
+ if not any(
31
+ isinstance(h, logging.FileHandler) and h.baseFilename == str(path.resolve())
32
+ for h in logger.handlers
33
+ ):
34
+ handler = logging.FileHandler(path)
35
+ handler.setFormatter(logging.Formatter(_DEFAULT_FORMAT))
36
+ logger.addHandler(handler)
37
+ return logger
38
+
39
+
40
+ def set_debug_mode(logger: logging.Logger, enabled: bool = True) -> None:
41
+ """Toggle debug-level verbosity on the given logger."""
42
+ logger.setLevel(logging.DEBUG if enabled else logging.INFO)
43
+
44
+
45
+ class PerformanceLogger:
46
+ """A simple named-checkpoint performance logger.
47
+
48
+ Examples
49
+ --------
50
+ >>> perf = PerformanceLogger()
51
+ >>> perf.checkpoint("start")
52
+ >>> perf.checkpoint("end")
53
+ >>> perf.report()['start'] >= 0
54
+ True
55
+ """
56
+
57
+ def __init__(self) -> None:
58
+ self._checkpoints: dict[str, float] = {}
59
+ self._start = time.perf_counter()
60
+
61
+ def checkpoint(self, label: str) -> float:
62
+ """Record a checkpoint and return elapsed seconds since construction."""
63
+ elapsed = time.perf_counter() - self._start
64
+ self._checkpoints[label] = elapsed
65
+ return elapsed
66
+
67
+ def report(self) -> dict[str, float]:
68
+ """Return a mapping of checkpoint labels to elapsed seconds."""
69
+ return dict(self._checkpoints)
70
+
71
+ def reset(self) -> None:
72
+ """Reset the performance logger's start time and checkpoints."""
73
+ self._checkpoints.clear()
74
+ self._start = time.perf_counter()
usel/math/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Reusable numerical utilities: matrix creation, algebra, norms, transforms."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from usel.math.matrix import Matrix
6
+ from usel.math.transforms import fft, frequency_analysis, ifft
7
+
8
+ __all__ = ["Matrix", "fft", "ifft", "frequency_analysis"]