scientific-computing-system 1.6.2__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 (97) hide show
  1. cds/__init__.py +102 -0
  2. cds/__main__.py +8 -0
  3. cds/_version.py +10 -0
  4. cds/cli/__init__.py +104 -0
  5. cds/cli/__main__.py +8 -0
  6. cds/cli/_handlers.py +405 -0
  7. cds/cli/_parser.py +156 -0
  8. cds/cli/_style.py +117 -0
  9. cds/core/__init__.py +5 -0
  10. cds/core/_numeric.py +92 -0
  11. cds/core/models.py +166 -0
  12. cds/data_analysis/__init__.py +30 -0
  13. cds/data_analysis/dataset.py +122 -0
  14. cds/data_analysis/loader.py +75 -0
  15. cds/data_analysis/pandas_io.py +108 -0
  16. cds/data_analysis/transform.py +36 -0
  17. cds/data_analysis/viz.py +88 -0
  18. cds/diffeq/__init__.py +29 -0
  19. cds/diffeq/_implicit.py +349 -0
  20. cds/diffeq/solvers.py +312 -0
  21. cds/graph/__init__.py +31 -0
  22. cds/graph/algorithms.py +399 -0
  23. cds/hypothesis/__init__.py +48 -0
  24. cds/hypothesis/evaluator.py +369 -0
  25. cds/hypothesis/generator.py +275 -0
  26. cds/knowledge/__init__.py +41 -0
  27. cds/knowledge/graph.py +489 -0
  28. cds/knowledge/notes.py +231 -0
  29. cds/knowledge/retrieval.py +167 -0
  30. cds/math_utils/__init__.py +41 -0
  31. cds/math_utils/calculus.py +40 -0
  32. cds/math_utils/linalg.py +461 -0
  33. cds/math_utils/special.py +167 -0
  34. cds/ml/__init__.py +25 -0
  35. cds/ml/clustering.py +190 -0
  36. cds/ml/decomposition.py +172 -0
  37. cds/ml/linear_models.py +225 -0
  38. cds/ml/neighbors.py +162 -0
  39. cds/ml/neural.py +204 -0
  40. cds/ml/preprocessing.py +131 -0
  41. cds/ml/tree.py +202 -0
  42. cds/modeling/__init__.py +40 -0
  43. cds/modeling/_base.py +210 -0
  44. cds/modeling/_nodes.py +526 -0
  45. cds/modeling/expression.py +81 -0
  46. cds/modeling/model.py +134 -0
  47. cds/modeling/solver.py +176 -0
  48. cds/montecarlo/__init__.py +21 -0
  49. cds/montecarlo/methods.py +272 -0
  50. cds/nlp/__init__.py +170 -0
  51. cds/nlp/attention.py +285 -0
  52. cds/nlp/autograd/__init__.py +70 -0
  53. cds/nlp/autograd/_grad.py +120 -0
  54. cds/nlp/autograd/ops.py +133 -0
  55. cds/nlp/autograd/tensor.py +298 -0
  56. cds/nlp/bpe.py +449 -0
  57. cds/nlp/data.py +70 -0
  58. cds/nlp/embed.py +190 -0
  59. cds/nlp/layers.py +259 -0
  60. cds/nlp/model.py +456 -0
  61. cds/nlp/optim.py +163 -0
  62. cds/nlp/training.py +167 -0
  63. cds/nlp/viz.py +254 -0
  64. cds/numerical_integration/__init__.py +31 -0
  65. cds/numerical_integration/quadrature.py +480 -0
  66. cds/optimization/__init__.py +18 -0
  67. cds/optimization/_metaheuristics.py +287 -0
  68. cds/optimization/minimize.py +425 -0
  69. cds/plot/__init__.py +49 -0
  70. cds/plot/_backend.py +32 -0
  71. cds/plot/charts.py +462 -0
  72. cds/probability/__init__.py +55 -0
  73. cds/probability/_advanced.py +283 -0
  74. cds/probability/distributions.py +236 -0
  75. cds/py.typed +0 -0
  76. cds/quantum/__init__.py +51 -0
  77. cds/quantum/circuit.py +92 -0
  78. cds/quantum/multi_qubit.py +255 -0
  79. cds/quantum/simulator.py +38 -0
  80. cds/scientific/__init__.py +34 -0
  81. cds/scientific/constants.py +35 -0
  82. cds/scientific/formulas.py +116 -0
  83. cds/signals/__init__.py +43 -0
  84. cds/signals/filters.py +421 -0
  85. cds/signals/processing.py +224 -0
  86. cds/stats/__init__.py +83 -0
  87. cds/stats/_distributions.py +102 -0
  88. cds/stats/descriptive.py +172 -0
  89. cds/stats/hypothesis_tests.py +403 -0
  90. cds/stats/nonparametric.py +131 -0
  91. cds/stats/regression.py +51 -0
  92. cds/stats/time_series.py +408 -0
  93. scientific_computing_system-1.6.2.dist-info/METADATA +741 -0
  94. scientific_computing_system-1.6.2.dist-info/RECORD +97 -0
  95. scientific_computing_system-1.6.2.dist-info/WHEEL +4 -0
  96. scientific_computing_system-1.6.2.dist-info/entry_points.txt +2 -0
  97. scientific_computing_system-1.6.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,122 @@
1
+ """Pure Python DataFrame-like structure for structured data analysis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from typing import TypeAlias
7
+
8
+ # A cell value in a DataSet row. Tabular data here is expected to be scalar
9
+ # primitives — numbers, strings, booleans, or null — rather than nested
10
+ # containers, which keeps column extraction and aggregation type-safe.
11
+ Scalar: TypeAlias = int | float | str | bool | None
12
+
13
+ # A single row maps column name -> cell value.
14
+ Row: TypeAlias = dict[str, Scalar]
15
+
16
+
17
+ class DataSet:
18
+ """A lightweight, pure Python 'DataFrame' for structured data.
19
+
20
+ Data is stored internally as a list of dictionaries where keys are column names.
21
+ """
22
+
23
+ def __init__(self, data: list[Row]):
24
+ """Store ``data`` and derive column names from the first row's keys."""
25
+ self.data = data
26
+ self._columns = list(data[0].keys()) if data else []
27
+
28
+ @property
29
+ def columns(self) -> list[str]:
30
+ """Return the list of column names."""
31
+ return self._columns
32
+
33
+ @property
34
+ def shape(self) -> tuple[int, int]:
35
+ """Return (rows, columns) tuple."""
36
+ return len(self.data), len(self._columns)
37
+
38
+ def __len__(self) -> int:
39
+ """Return the number of rows."""
40
+ return len(self.data)
41
+
42
+ def __getitem__(self, idx: int) -> Row:
43
+ """Return the row at integer index ``idx``."""
44
+ return self.data[idx]
45
+
46
+ def column(self, name: str) -> list[Scalar]:
47
+ """Extract a single column as a list."""
48
+ if name not in self._columns:
49
+ raise ValueError(f"Column '{name}' not found. Available: {self._columns}")
50
+ return [row[name] for row in self.data]
51
+
52
+ def filter(self, predicate: Callable[[Row], bool]) -> DataSet:
53
+ """Filter the dataset based on a predicate function."""
54
+ filtered_data = [row for row in self.data if predicate(row)]
55
+ return DataSet(filtered_data)
56
+
57
+ def head(self, n: int = 5) -> DataSet:
58
+ """Return the first n rows."""
59
+ return DataSet(self.data[:n])
60
+
61
+ def tail(self, n: int = 5) -> DataSet:
62
+ """Return the last n rows."""
63
+ return DataSet(self.data[-n:])
64
+
65
+ def select(self, *names: str) -> DataSet:
66
+ """Select a subset of columns."""
67
+ for name in names:
68
+ if name not in self._columns:
69
+ raise ValueError(f"Column '{name}' not found.")
70
+
71
+ new_data = [{name: row[name] for name in names} for row in self.data]
72
+ return DataSet(new_data)
73
+
74
+ def group_by(self, column_name: str) -> DataGroup:
75
+ """Group data by a specific column for aggregation."""
76
+ if column_name not in self._columns:
77
+ raise ValueError(f"Column '{column_name}' not found.")
78
+
79
+ groups: dict[Scalar, list[Row]] = {}
80
+ for row in self.data:
81
+ key = row[column_name]
82
+ if key not in groups:
83
+ groups[key] = []
84
+ groups[key].append(row)
85
+
86
+ return DataGroup(groups, column_name)
87
+
88
+ def to_list(self) -> list[Row]:
89
+ """Export data as a list of dictionaries."""
90
+ return [row.copy() for row in self.data]
91
+
92
+ def __repr__(self) -> str:
93
+ """Return a compact ``DataSet(rows=..., cols=...)`` summary."""
94
+ if not self.data:
95
+ return "DataSet(empty)"
96
+ return f"DataSet(rows={len(self.data)}, cols={len(self._columns)})"
97
+
98
+
99
+ class DataGroup:
100
+ """Helper class for grouped data aggregations."""
101
+
102
+ def __init__(self, groups: dict[Scalar, list[Row]], group_col: str):
103
+ """Store the grouped rows and the name of the grouping column."""
104
+ self.groups = groups
105
+ self.group_col = group_col
106
+
107
+ def mean(self, numeric_col: str) -> dict[Scalar, float]:
108
+ """Calculate the mean of a numeric column for each group."""
109
+ result: dict[Scalar, float] = {}
110
+ for key, rows in self.groups.items():
111
+ values: list[float] = [
112
+ float(v) for row in rows if isinstance((v := row.get(numeric_col)), (int, float))
113
+ ]
114
+ if values:
115
+ result[key] = sum(values) / len(values)
116
+ else:
117
+ result[key] = 0.0
118
+ return result
119
+
120
+ def count(self) -> dict[Scalar, int]:
121
+ """Count the number of rows in each group."""
122
+ return {key: len(rows) for key, rows in self.groups.items()}
@@ -0,0 +1,75 @@
1
+ """Simple CSV loader — no pandas dependency."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import csv
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+
9
+
10
+ @dataclass
11
+ class DataTable:
12
+ """In-memory tabular data: a header row plus a list of string rows."""
13
+
14
+ headers: list[str] = field(default_factory=list)
15
+ rows: list[list[str]] = field(default_factory=list)
16
+
17
+ @property
18
+ def n_rows(self) -> int:
19
+ """Number of data rows (excluding the header)."""
20
+ return len(self.rows)
21
+
22
+ @property
23
+ def n_cols(self) -> int:
24
+ """Number of columns (i.e. number of header entries)."""
25
+ return len(self.headers)
26
+
27
+ def column(self, name: str) -> list[str]:
28
+ """Return all values in the column identified by `name`."""
29
+ idx = self.headers.index(name)
30
+ return [row[idx] for row in self.rows]
31
+
32
+ def column_as_float(self, name: str) -> list[float]:
33
+ """Return a column as floats; raises ValueError if a cell is non-numeric."""
34
+ return [float(v) for v in self.column(name)]
35
+
36
+ def head(self, n: int = 5) -> list[list[str]]:
37
+ """Return the first `n` rows (default 5) for quick inspection."""
38
+ return self.rows[:n]
39
+
40
+ def describe(self) -> dict[str, dict[str, float]]:
41
+ """Quick summary stats for numeric columns."""
42
+ from cds.stats.descriptive import mean, median, stdev
43
+
44
+ result: dict[str, dict[str, float]] = {}
45
+ for h in self.headers:
46
+ try:
47
+ vals = self.column_as_float(h)
48
+ result[h] = {
49
+ "count": len(vals),
50
+ "mean": mean(vals),
51
+ "std": stdev(vals),
52
+ "min": min(vals),
53
+ "median": median(vals),
54
+ "max": max(vals),
55
+ }
56
+ except (ValueError, TypeError):
57
+ # Non-numeric column — skip aggregation, keep prior result.
58
+ pass
59
+ return result
60
+
61
+
62
+ def load_csv(path: str | Path) -> DataTable:
63
+ """Load a CSV file into a DataTable.
64
+
65
+ The first row is treated as headers; remaining rows are stored as strings.
66
+ Raises FileNotFoundError if `path` does not exist.
67
+ """
68
+ p = Path(path)
69
+ if not p.exists():
70
+ raise FileNotFoundError(f"no such file: {p}")
71
+ with open(p, newline="") as f:
72
+ reader = csv.reader(f)
73
+ headers = next(reader)
74
+ rows = list(reader)
75
+ return DataTable(headers=headers, rows=rows)
@@ -0,0 +1,108 @@
1
+ """Pandas interoperability bridge for :class:`cds.data_analysis.DataSet`.
2
+
3
+ This module is **optional**: pandas is imported lazily, so the core package
4
+ remains zero-dependency. Install the extra with ``pip install ".[pandas]"``
5
+ (or ``[all]``). Importing this module without pandas installed raises a clear
6
+ ``ImportError`` with install guidance.
7
+
8
+ The bridge converts between CDS's pure-Python :class:`DataSet` (a list of
9
+ row dicts) and a pandas ``DataFrame``, letting users who already live in the
10
+ pandas ecosystem move data in both directions without rewriting pipelines.
11
+
12
+ Design notes:
13
+ - Conversions are lossless for the scalar types :class:`DataSet` supports
14
+ (``int | float | str | bool | None``).
15
+ - ``None`` cells map to ``NaN`` in pandas and back, matching pandas convention.
16
+ - Column order is preserved from the source on both directions.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import TYPE_CHECKING, Any
22
+
23
+ from cds.data_analysis.dataset import DataSet, Row
24
+
25
+ if TYPE_CHECKING:
26
+ # Imported only under TYPE_CHECKING so the module imports cleanly when
27
+ # pandas is absent; the real import happens inside the functions.
28
+ import pandas as pd
29
+
30
+
31
+ _PANDAS_INSTALL_HINT = (
32
+ "pandas is an optional dependency. Install it with "
33
+ "`pip install scientific-computing-system[pandas]` "
34
+ "(or `pip install pandas`)."
35
+ )
36
+
37
+
38
+ def _require_pandas() -> Any:
39
+ """Import and return the ``pandas`` module, raising a helpful error if absent."""
40
+ try:
41
+ import pandas as pd
42
+ except ImportError as exc: # pragma: no cover - exercised only without pandas
43
+ raise ImportError(_PANDAS_INSTALL_HINT) from exc
44
+ return pd
45
+
46
+
47
+ def to_dataframe(ds: DataSet) -> pd.DataFrame:
48
+ """Convert a :class:`DataSet` to a pandas ``DataFrame``.
49
+
50
+ Each row dict becomes a DataFrame row; column order follows
51
+ ``ds.columns``. ``None`` values are preserved as-is (pandas renders them
52
+ as ``NaN`` in numeric columns).
53
+
54
+ Args:
55
+ ds: a CDS :class:`DataSet`.
56
+
57
+ Returns:
58
+ A pandas ``DataFrame`` with the same rows and columns.
59
+
60
+ Raises:
61
+ ImportError: if pandas is not installed.
62
+ """
63
+ pandas_module = _require_pandas()
64
+ rows = ds.to_list()
65
+ columns = ds.columns
66
+ # The DataFrame constructor is typed as returning Any in the stubs; assign
67
+ # to an annotated local so the return type is pinned without a cast.
68
+ df: pd.DataFrame = pandas_module.DataFrame(rows, columns=columns if columns else None)
69
+ return df
70
+
71
+
72
+ def from_dataframe(df: pd.DataFrame) -> DataSet:
73
+ """Convert a pandas ``DataFrame`` to a :class:`DataSet`.
74
+
75
+ Each DataFrame row becomes a row dict keyed by column name. pandas
76
+ ``NaN`` values are converted to ``None`` to match :class:`DataSet`'s
77
+ scalar type contract.
78
+
79
+ Args:
80
+ df: a pandas ``DataFrame``.
81
+
82
+ Returns:
83
+ A CDS :class:`DataSet` with the same data.
84
+
85
+ Raises:
86
+ ImportError: if pandas is not installed.
87
+ """
88
+ pandas_module = _require_pandas()
89
+ rows: list[Row] = []
90
+ # .to_dict(orient="records") yields plain dicts; NaN -> NaN (float).
91
+ # We normalize NaN to None to honor the DataSet scalar contract.
92
+ raw_records = df.to_dict(orient="records")
93
+ for record in raw_records:
94
+ row: Row = {}
95
+ for key, value in record.items():
96
+ # pd.isna handles both scalar NaN and None, but raises on arrays.
97
+ # Values here are scalars from a DataFrame cell.
98
+ try:
99
+ is_na = bool(pandas_module.isna(value))
100
+ except (TypeError, ValueError): # pragma: no cover - defensive
101
+ # pd.isna raises only on array-like inputs. DataSet's Row
102
+ # contract restricts cells to scalars (int|float|str|bool|None),
103
+ # so this branch is unreachable for conforming data; kept as a
104
+ # guard against malformed/foreign rows slipping through.
105
+ is_na = value is None
106
+ row[str(key)] = None if is_na else value
107
+ rows.append(row)
108
+ return DataSet(rows)
@@ -0,0 +1,36 @@
1
+ """Data transformations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from cds.stats.descriptive import mean, stdev
6
+
7
+
8
+ def normalize(data: list[float]) -> list[float]:
9
+ """Min-max normalization to [0, 1]."""
10
+ lo, hi = min(data), max(data)
11
+ rng = hi - lo
12
+ if rng == 0:
13
+ return [0.0] * len(data)
14
+ return [(x - lo) / rng for x in data]
15
+
16
+
17
+ def z_score(data: list[float]) -> list[float]:
18
+ """Standardize to mean=0, std=1."""
19
+ m = mean(data)
20
+ s = stdev(data)
21
+ if s == 0:
22
+ return [0.0] * len(data)
23
+ return [(x - m) / s for x in data]
24
+
25
+
26
+ def moving_average(data: list[float], window: int = 3) -> list[float]:
27
+ """Trailing moving average with the given window size (>= 1)."""
28
+
29
+ if window < 1:
30
+ raise ValueError("window must be >= 1")
31
+ result = []
32
+ for i in range(len(data)):
33
+ start = max(0, i - window + 1)
34
+ chunk = data[start : i + 1]
35
+ result.append(sum(chunk) / len(chunk))
36
+ return result
@@ -0,0 +1,88 @@
1
+ """Pure Python ASCII visualization tools for structured data."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ def plot_bar(data: dict[str, float], title: str = "Bar Chart", width: int = 50) -> str:
7
+ """Generate an ASCII bar chart from a dictionary.
8
+
9
+ Args:
10
+ data: Mapping from label to numeric value.
11
+ title: Chart title.
12
+ width: Maximum bar width in characters.
13
+ """
14
+ if not data:
15
+ return "No data to plot."
16
+
17
+ vals = list(data.values())
18
+ max_val = max(vals)
19
+ min_val = min(vals)
20
+
21
+ lines = [f"\n[bold]{title}[/]", "─" * len(title)]
22
+
23
+ # Simple normalization logic that respects 0 as a baseline
24
+ limit = max(abs(max_val), abs(min_val), 1e-10)
25
+
26
+ for label, val in data.items():
27
+ # Calculate bar length relative to the largest absolute value
28
+ bar_len = int((abs(val) / limit) * width)
29
+ if val >= 0:
30
+ bar = "█" * bar_len
31
+ suffix = f" (+{val:.2f})"
32
+ else:
33
+ # Represent negative values with a different character or notation
34
+ bar = "░" * bar_len
35
+ suffix = f" ({val:.2f})"
36
+
37
+ lines.append(f"{label:<15} | {bar}{suffix}")
38
+
39
+ return "\n".join(lines)
40
+
41
+
42
+ def plot_line(
43
+ y_values: list[float], title: str = "Line Plot", height: int = 10, width: int = 60
44
+ ) -> str:
45
+ """Generate a simple ASCII line plot.
46
+
47
+ Args:
48
+ y_values: List of numeric values.
49
+ title: Plot title.
50
+ height: Number of rows.
51
+ width: Number of columns (will sample data to fit).
52
+ """
53
+ if not y_values:
54
+ return "No data to plot."
55
+
56
+ # Guard: width must be at least 2; otherwise stride below divides by zero.
57
+ eff_width = max(2, width)
58
+
59
+ # Sample/Interpolate to fit width
60
+ if len(y_values) > eff_width:
61
+ indices = [int(i * (len(y_values) - 1) / (eff_width - 1)) for i in range(eff_width)]
62
+ sampled = [y_values[i] for i in indices]
63
+ else:
64
+ sampled = y_values
65
+ eff_width = len(y_values)
66
+
67
+ max_y = max(sampled)
68
+ min_y = min(sampled)
69
+ y_range = max_y - min_y if max_y != min_y else 1.0
70
+
71
+ # Create grid
72
+ grid = [[" " for _ in range(eff_width)] for _ in range(height)]
73
+
74
+ for x, y in enumerate(sampled):
75
+ # Calculate row (inverted because row 0 is top)
76
+ norm_y = (y - min_y) / y_range
77
+ row = height - 1 - int(norm_y * (height - 1))
78
+ grid[row][x] = "•"
79
+
80
+ lines = [f"\n[bold]{title}[/]", "─" * len(title)]
81
+ for grid_row in grid:
82
+ lines.append("".join(grid_row))
83
+
84
+ min_y_str = f"{min_y:.2f}"
85
+ max_y_str = f"{max_y:.2f}"
86
+
87
+ lines.append(f"min: {min_y_str:<{eff_width // 2}}max: {max_y_str:>{eff_width // 2}}")
88
+ return "\n".join(lines)
cds/diffeq/__init__.py ADDED
@@ -0,0 +1,29 @@
1
+ """Ordinary differential equation solvers — Euler, RK4, RK45, implicit methods."""
2
+
3
+ from cds.diffeq._implicit import (
4
+ backward_euler,
5
+ backward_euler_system,
6
+ trapezoid_method,
7
+ trapezoid_method_system,
8
+ )
9
+ from cds.diffeq.solvers import (
10
+ ODESolution,
11
+ euler_method,
12
+ midpoint_method,
13
+ rk4,
14
+ rk45,
15
+ solve_system,
16
+ )
17
+
18
+ __all__ = [
19
+ "ODESolution",
20
+ "backward_euler",
21
+ "backward_euler_system",
22
+ "euler_method",
23
+ "midpoint_method",
24
+ "rk4",
25
+ "rk45",
26
+ "solve_system",
27
+ "trapezoid_method",
28
+ "trapezoid_method_system",
29
+ ]