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.
- cds/__init__.py +102 -0
- cds/__main__.py +8 -0
- cds/_version.py +10 -0
- cds/cli/__init__.py +104 -0
- cds/cli/__main__.py +8 -0
- cds/cli/_handlers.py +405 -0
- cds/cli/_parser.py +156 -0
- cds/cli/_style.py +117 -0
- cds/core/__init__.py +5 -0
- cds/core/_numeric.py +92 -0
- cds/core/models.py +166 -0
- cds/data_analysis/__init__.py +30 -0
- cds/data_analysis/dataset.py +122 -0
- cds/data_analysis/loader.py +75 -0
- cds/data_analysis/pandas_io.py +108 -0
- cds/data_analysis/transform.py +36 -0
- cds/data_analysis/viz.py +88 -0
- cds/diffeq/__init__.py +29 -0
- cds/diffeq/_implicit.py +349 -0
- cds/diffeq/solvers.py +312 -0
- cds/graph/__init__.py +31 -0
- cds/graph/algorithms.py +399 -0
- cds/hypothesis/__init__.py +48 -0
- cds/hypothesis/evaluator.py +369 -0
- cds/hypothesis/generator.py +275 -0
- cds/knowledge/__init__.py +41 -0
- cds/knowledge/graph.py +489 -0
- cds/knowledge/notes.py +231 -0
- cds/knowledge/retrieval.py +167 -0
- cds/math_utils/__init__.py +41 -0
- cds/math_utils/calculus.py +40 -0
- cds/math_utils/linalg.py +461 -0
- cds/math_utils/special.py +167 -0
- cds/ml/__init__.py +25 -0
- cds/ml/clustering.py +190 -0
- cds/ml/decomposition.py +172 -0
- cds/ml/linear_models.py +225 -0
- cds/ml/neighbors.py +162 -0
- cds/ml/neural.py +204 -0
- cds/ml/preprocessing.py +131 -0
- cds/ml/tree.py +202 -0
- cds/modeling/__init__.py +40 -0
- cds/modeling/_base.py +210 -0
- cds/modeling/_nodes.py +526 -0
- cds/modeling/expression.py +81 -0
- cds/modeling/model.py +134 -0
- cds/modeling/solver.py +176 -0
- cds/montecarlo/__init__.py +21 -0
- cds/montecarlo/methods.py +272 -0
- cds/nlp/__init__.py +170 -0
- cds/nlp/attention.py +285 -0
- cds/nlp/autograd/__init__.py +70 -0
- cds/nlp/autograd/_grad.py +120 -0
- cds/nlp/autograd/ops.py +133 -0
- cds/nlp/autograd/tensor.py +298 -0
- cds/nlp/bpe.py +449 -0
- cds/nlp/data.py +70 -0
- cds/nlp/embed.py +190 -0
- cds/nlp/layers.py +259 -0
- cds/nlp/model.py +456 -0
- cds/nlp/optim.py +163 -0
- cds/nlp/training.py +167 -0
- cds/nlp/viz.py +254 -0
- cds/numerical_integration/__init__.py +31 -0
- cds/numerical_integration/quadrature.py +480 -0
- cds/optimization/__init__.py +18 -0
- cds/optimization/_metaheuristics.py +287 -0
- cds/optimization/minimize.py +425 -0
- cds/plot/__init__.py +49 -0
- cds/plot/_backend.py +32 -0
- cds/plot/charts.py +462 -0
- cds/probability/__init__.py +55 -0
- cds/probability/_advanced.py +283 -0
- cds/probability/distributions.py +236 -0
- cds/py.typed +0 -0
- cds/quantum/__init__.py +51 -0
- cds/quantum/circuit.py +92 -0
- cds/quantum/multi_qubit.py +255 -0
- cds/quantum/simulator.py +38 -0
- cds/scientific/__init__.py +34 -0
- cds/scientific/constants.py +35 -0
- cds/scientific/formulas.py +116 -0
- cds/signals/__init__.py +43 -0
- cds/signals/filters.py +421 -0
- cds/signals/processing.py +224 -0
- cds/stats/__init__.py +83 -0
- cds/stats/_distributions.py +102 -0
- cds/stats/descriptive.py +172 -0
- cds/stats/hypothesis_tests.py +403 -0
- cds/stats/nonparametric.py +131 -0
- cds/stats/regression.py +51 -0
- cds/stats/time_series.py +408 -0
- scientific_computing_system-1.6.2.dist-info/METADATA +741 -0
- scientific_computing_system-1.6.2.dist-info/RECORD +97 -0
- scientific_computing_system-1.6.2.dist-info/WHEEL +4 -0
- scientific_computing_system-1.6.2.dist-info/entry_points.txt +2 -0
- scientific_computing_system-1.6.2.dist-info/licenses/LICENSE +21 -0
cds/cli/_parser.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Argument-parser construction for the ``cds`` CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from cds.cli._handlers import (
|
|
8
|
+
_cmd_benchmark,
|
|
9
|
+
_cmd_calc,
|
|
10
|
+
_cmd_constants,
|
|
11
|
+
_cmd_dashboard,
|
|
12
|
+
_cmd_hypothesis,
|
|
13
|
+
_cmd_info,
|
|
14
|
+
_cmd_integrate,
|
|
15
|
+
_cmd_modules,
|
|
16
|
+
_cmd_plot,
|
|
17
|
+
_cmd_prompt,
|
|
18
|
+
_cmd_sample,
|
|
19
|
+
_cmd_stats,
|
|
20
|
+
_cmd_version,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
_DOMAIN_CHOICES = ["physics", "cosmology", "mathematics", "biology", "chemistry", "general_science"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
27
|
+
"""Construct the top-level ``cds`` argument parser and its subcommands."""
|
|
28
|
+
parser = argparse.ArgumentParser(
|
|
29
|
+
prog="cds",
|
|
30
|
+
description="Cognitive Discovery System — computational science platform.",
|
|
31
|
+
)
|
|
32
|
+
parser.add_argument("--version", "-v", action="store_true", help="Show System version and exit")
|
|
33
|
+
|
|
34
|
+
sub = parser.add_subparsers(dest="command")
|
|
35
|
+
|
|
36
|
+
p_version = sub.add_parser("version", help="Show System version.")
|
|
37
|
+
p_version.set_defaults(func=_cmd_version)
|
|
38
|
+
|
|
39
|
+
p_hyp = sub.add_parser(
|
|
40
|
+
"hypothesis", help="Generate scientific hypotheses for a research question."
|
|
41
|
+
)
|
|
42
|
+
p_hyp.add_argument("question", help="The core research question or problem")
|
|
43
|
+
p_hyp.add_argument(
|
|
44
|
+
"--domain",
|
|
45
|
+
"-d",
|
|
46
|
+
default="general_science",
|
|
47
|
+
choices=_DOMAIN_CHOICES,
|
|
48
|
+
help="Scientific domain focus",
|
|
49
|
+
)
|
|
50
|
+
p_hyp.add_argument("--num", "-n", type=int, default=3, help="Number of hypotheses to propose")
|
|
51
|
+
p_hyp.add_argument("--output", "-o", help="Save results as JSON")
|
|
52
|
+
p_hyp.add_argument("--show-prompt", action="store_true", help="Print the exact prompt template")
|
|
53
|
+
p_hyp.add_argument("--dry-run", action="store_true", help="Do not run generation logic")
|
|
54
|
+
p_hyp.set_defaults(func=_cmd_hypothesis)
|
|
55
|
+
|
|
56
|
+
p_prompt = sub.add_parser("prompt", help="Print a ready-to-use prompt for a custom generator.")
|
|
57
|
+
p_prompt.add_argument("question", help="Research question")
|
|
58
|
+
p_prompt.add_argument(
|
|
59
|
+
"--domain",
|
|
60
|
+
"-d",
|
|
61
|
+
default="general_science",
|
|
62
|
+
choices=_DOMAIN_CHOICES,
|
|
63
|
+
help="Scientific domain focus",
|
|
64
|
+
)
|
|
65
|
+
p_prompt.add_argument(
|
|
66
|
+
"--num", "-n", type=int, default=3, help="Number of hypotheses to propose"
|
|
67
|
+
)
|
|
68
|
+
p_prompt.set_defaults(func=_cmd_prompt)
|
|
69
|
+
|
|
70
|
+
p_info = sub.add_parser("info", help="Show System info, module status, and System health.")
|
|
71
|
+
p_info.set_defaults(func=_cmd_info)
|
|
72
|
+
|
|
73
|
+
sub.add_parser("dashboard", help="Launch the interactive System dashboard.").set_defaults(
|
|
74
|
+
func=_cmd_dashboard
|
|
75
|
+
)
|
|
76
|
+
sub.add_parser("benchmark", help="Run built-in benchmarks to verify performance.").set_defaults(
|
|
77
|
+
func=_cmd_benchmark
|
|
78
|
+
)
|
|
79
|
+
sub.add_parser("constants", help="List available physical constants.").set_defaults(
|
|
80
|
+
func=_cmd_constants
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
p_plot = sub.add_parser(
|
|
84
|
+
"plot",
|
|
85
|
+
help="Plot a series of numbers (ASCII terminal, or PNG with --file if cds[plot] installed).",
|
|
86
|
+
)
|
|
87
|
+
p_plot.add_argument("values", help="Comma-separated list of numbers (e.g. '1,5,3,8')")
|
|
88
|
+
p_plot.add_argument("--title", "-t", default="CLI Plot", help="Title of the plot")
|
|
89
|
+
p_plot.add_argument(
|
|
90
|
+
"--file",
|
|
91
|
+
"-f",
|
|
92
|
+
default=None,
|
|
93
|
+
help="Save a PNG via optional matplotlib (requires cds[plot]); omit for ASCII",
|
|
94
|
+
)
|
|
95
|
+
p_plot.add_argument(
|
|
96
|
+
"--kind",
|
|
97
|
+
"-k",
|
|
98
|
+
default="series",
|
|
99
|
+
choices=["series", "hist", "acf"],
|
|
100
|
+
help="Chart type when using --file (default: series)",
|
|
101
|
+
)
|
|
102
|
+
p_plot.set_defaults(func=_cmd_plot)
|
|
103
|
+
|
|
104
|
+
p_calc = sub.add_parser("calc", help="Quick physics calculations.")
|
|
105
|
+
p_calc.add_argument("formula", help="Formula: ke, gravity, wave, gas")
|
|
106
|
+
p_calc.set_defaults(func=_cmd_calc)
|
|
107
|
+
|
|
108
|
+
p_stats = sub.add_parser(
|
|
109
|
+
"stats", help="Descriptive statistics for a comma-separated number list."
|
|
110
|
+
)
|
|
111
|
+
p_stats.add_argument("values", help="Comma-separated numbers (e.g. '1,2,3,4')")
|
|
112
|
+
p_stats.set_defaults(func=_cmd_stats)
|
|
113
|
+
|
|
114
|
+
p_sample = sub.add_parser("sample", help="Draw samples from a probability distribution.")
|
|
115
|
+
p_sample.add_argument(
|
|
116
|
+
"dist",
|
|
117
|
+
choices=["uniform", "gaussian", "exponential", "poisson"],
|
|
118
|
+
help="Distribution name",
|
|
119
|
+
)
|
|
120
|
+
p_sample.add_argument("-n", type=int, default=5, help="Number of samples (default 5)")
|
|
121
|
+
p_sample.add_argument("--seed", type=int, default=None, help="RNG seed")
|
|
122
|
+
p_sample.add_argument("--a", type=float, default=0.0, help="uniform lower bound")
|
|
123
|
+
p_sample.add_argument("--b", type=float, default=1.0, help="uniform upper bound")
|
|
124
|
+
p_sample.add_argument("--mu", type=float, default=0.0, help="gaussian mean")
|
|
125
|
+
p_sample.add_argument("--sigma", type=float, default=1.0, help="gaussian stdev")
|
|
126
|
+
p_sample.add_argument("--lam", type=float, default=1.0, help="rate λ for exponential/poisson")
|
|
127
|
+
p_sample.set_defaults(func=_cmd_sample)
|
|
128
|
+
|
|
129
|
+
p_int = sub.add_parser(
|
|
130
|
+
"integrate", help="Numerically integrate a built-in function over [a, b]."
|
|
131
|
+
)
|
|
132
|
+
p_int.add_argument(
|
|
133
|
+
"integrand",
|
|
134
|
+
choices=["sin", "cos", "exp", "x2", "unit"],
|
|
135
|
+
help="Integrand name",
|
|
136
|
+
)
|
|
137
|
+
p_int.add_argument("--a", type=float, default=0.0, help="Lower limit (default 0)")
|
|
138
|
+
p_int.add_argument("--b", type=float, default=1.0, help="Upper limit (default 1)")
|
|
139
|
+
p_int.add_argument("-n", type=int, default=1000, help="Number of panels (default 1000)")
|
|
140
|
+
p_int.add_argument(
|
|
141
|
+
"--method",
|
|
142
|
+
choices=["simpson", "trap"],
|
|
143
|
+
default="simpson",
|
|
144
|
+
help="Quadrature rule (default simpson)",
|
|
145
|
+
)
|
|
146
|
+
p_int.set_defaults(func=_cmd_integrate)
|
|
147
|
+
|
|
148
|
+
sub.add_parser(
|
|
149
|
+
"modules", help="List all scientific modules available in the System."
|
|
150
|
+
).set_defaults(func=_cmd_modules)
|
|
151
|
+
|
|
152
|
+
return parser
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Historical private name kept as an alias for backwards compatibility.
|
|
156
|
+
_build_parser = build_parser
|
cds/cli/_style.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""ANSI colour helpers and ASCII table rendering for the CDS CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
_RESET = "\033[0m"
|
|
8
|
+
_STYLES: dict[str, str] = {
|
|
9
|
+
"bold": "\033[1m",
|
|
10
|
+
"dim": "\033[2m",
|
|
11
|
+
"italic": "\033[3m",
|
|
12
|
+
"red": "\033[31m",
|
|
13
|
+
"green": "\033[32m",
|
|
14
|
+
"yellow": "\033[33m",
|
|
15
|
+
"blue": "\033[34m",
|
|
16
|
+
"magenta": "\033[35m",
|
|
17
|
+
"cyan": "\033[36m",
|
|
18
|
+
"bold green": "\033[1;32m",
|
|
19
|
+
"bold blue": "\033[1;34m",
|
|
20
|
+
"bold cyan": "\033[1;36m",
|
|
21
|
+
"bold magenta": "\033[1;35m",
|
|
22
|
+
"bold red": "\033[1;31m",
|
|
23
|
+
"bold yellow": "\033[1;33m",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _supports_color() -> bool:
|
|
28
|
+
"""Return True when stdout looks like an interactive colour terminal."""
|
|
29
|
+
return sys.stdout.isatty()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _wrap(style: str, text: str) -> str:
|
|
33
|
+
"""Wrap ``text`` in the ANSI codes for ``style`` if stdout is a TTY."""
|
|
34
|
+
if not _supports_color():
|
|
35
|
+
return text
|
|
36
|
+
code = _STYLES.get(style, "")
|
|
37
|
+
if not code:
|
|
38
|
+
return text
|
|
39
|
+
return f"{code}{text}{_RESET}"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _print(*args: object) -> None:
|
|
43
|
+
"""Print helper routed through stdout (so tests can capture via capsys)."""
|
|
44
|
+
print(*args)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _render(markup: str) -> str:
|
|
48
|
+
"""Render rich-style ``[style]...[/]`` markup into ANSI-coloured text.
|
|
49
|
+
|
|
50
|
+
Only the tag shapes actually used by this CLI are supported:
|
|
51
|
+
``[bold]``, ``[italic]``, ``[dim]``, ``[red]``, ``[green]``, ``[yellow]``,
|
|
52
|
+
``[blue]``, ``[cyan]``, ``[magenta]`` and a few combined ``[bold green]``
|
|
53
|
+
variants. Unknown tags are stripped to their inner text. Nesting is not
|
|
54
|
+
supported (the CLI never nests them).
|
|
55
|
+
"""
|
|
56
|
+
out: list[str] = []
|
|
57
|
+
i = 0
|
|
58
|
+
n = len(markup)
|
|
59
|
+
while i < n:
|
|
60
|
+
open_idx = markup.find("[", i)
|
|
61
|
+
if open_idx == -1:
|
|
62
|
+
out.append(markup[i:])
|
|
63
|
+
break
|
|
64
|
+
out.append(markup[i:open_idx])
|
|
65
|
+
close_idx = markup.find("]", open_idx)
|
|
66
|
+
if close_idx == -1:
|
|
67
|
+
out.append(markup[open_idx:])
|
|
68
|
+
break
|
|
69
|
+
tag = markup[open_idx + 1 : close_idx]
|
|
70
|
+
# Closing tag [/] ends the current styled run.
|
|
71
|
+
if tag.startswith("/"):
|
|
72
|
+
out.append(_RESET if _supports_color() else "")
|
|
73
|
+
i = close_idx + 1
|
|
74
|
+
continue
|
|
75
|
+
# Combined forms like "bold green" are looked up directly.
|
|
76
|
+
code = _STYLES.get(tag)
|
|
77
|
+
if code is None and " " in tag:
|
|
78
|
+
# ``[bold green]`` style — already in the table; fall back to first word.
|
|
79
|
+
code = _STYLES.get(tag.split()[0], "")
|
|
80
|
+
out.append(code if (_supports_color() and code is not None) else "")
|
|
81
|
+
i = close_idx + 1
|
|
82
|
+
return "".join(out)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _format_table(title: str, headers: list[str], rows: list[list[str]]) -> str:
|
|
86
|
+
"""Render ``headers``/``rows`` as a bordered ASCII table with a title.
|
|
87
|
+
|
|
88
|
+
A tiny reimplementation of the ``rich.Table`` output the CLI used to
|
|
89
|
+
produce: top/bottom title rule, a header row underlined with ``-``, and
|
|
90
|
+
each data row on its own line. Columns are sized to the widest cell.
|
|
91
|
+
"""
|
|
92
|
+
cols = len(headers)
|
|
93
|
+
widths = [len(h) for h in headers]
|
|
94
|
+
for row in rows:
|
|
95
|
+
for c, cell in enumerate(row[:cols]):
|
|
96
|
+
widths[c] = max(widths[c], len(cell))
|
|
97
|
+
|
|
98
|
+
def _border(left: str, fill: str, right: str) -> str:
|
|
99
|
+
return left + fill + right
|
|
100
|
+
|
|
101
|
+
sep = _border("+", "+".join("-" * (w + 2) for w in widths), "+")
|
|
102
|
+
|
|
103
|
+
lines: list[str] = []
|
|
104
|
+
if title:
|
|
105
|
+
lines.append(_wrap("bold", title))
|
|
106
|
+
lines.append(sep)
|
|
107
|
+
header_cells = " | ".join(h.ljust(widths[c]) for c, h in enumerate(headers))
|
|
108
|
+
lines.append(f"| {header_cells} |")
|
|
109
|
+
lines.append(sep)
|
|
110
|
+
for row in rows:
|
|
111
|
+
cells = " | ".join(
|
|
112
|
+
str(row[c]).ljust(widths[c]) if c < len(row) else "".ljust(widths[c])
|
|
113
|
+
for c in range(cols)
|
|
114
|
+
)
|
|
115
|
+
lines.append(f"| {cells} |")
|
|
116
|
+
lines.append(sep)
|
|
117
|
+
return "\n".join(lines)
|
cds/core/__init__.py
ADDED
cds/core/_numeric.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Central numeric constants for CDS solvers and numerical kernels.
|
|
2
|
+
|
|
3
|
+
These replace scattered magic numbers (``1e-15`` near-zero guards, ``1e-7``
|
|
4
|
+
finite-difference steps, ``0.01`` learning rates, ``-1e9`` masking sentinels)
|
|
5
|
+
with named, documented constants. The values mirror the literals they replace
|
|
6
|
+
so behavior is unchanged; only readability and consistency improve.
|
|
7
|
+
|
|
8
|
+
Notes
|
|
9
|
+
-----
|
|
10
|
+
- ``EPSILON`` is derived from :func:`math.ulp` rather than hardcoded, so it
|
|
11
|
+
reflects the actual machine epsilon on the running interpreter.
|
|
12
|
+
- Module-local algorithm constants (e.g. the Lanczos ``cof`` coefficients and
|
|
13
|
+
``_MAXIT`` / ``_EPS`` / ``_FPMIN`` in ``stats.hypothesis_tests``) stay where
|
|
14
|
+
they are: they are intrinsic to a specific Numerical-Recipes routine, not
|
|
15
|
+
shared tuning knobs.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import math
|
|
21
|
+
|
|
22
|
+
# Machine epsilon of the running interpreter (~2.220446e-16 on CPython/IEEE-754).
|
|
23
|
+
# Use this instead of the legacy hardcoded ``1e-15`` singularity guards.
|
|
24
|
+
EPSILON: float = math.ulp(1.0)
|
|
25
|
+
|
|
26
|
+
# Near-zero threshold for singularity / divide-by-zero guards
|
|
27
|
+
# (pivot checks, zero-vector guards, variance floors). Kept at ``1e-15`` to
|
|
28
|
+
# preserve the exact behavior of the original guards.
|
|
29
|
+
NEAR_ZERO: float = 1e-15
|
|
30
|
+
|
|
31
|
+
# Default step size for central finite-difference gradient estimates.
|
|
32
|
+
DEFAULT_FD_STEP: float = 1e-7
|
|
33
|
+
|
|
34
|
+
# Generic convergence tolerance for iterative solvers (gradient descent,
|
|
35
|
+
# Adam, golden-section line search).
|
|
36
|
+
DEFAULT_TOLERANCE: float = 1e-8
|
|
37
|
+
|
|
38
|
+
# Newton-Raphson root-finding tolerance (``abs(f(x)) < tol``).
|
|
39
|
+
NEWTON_TOLERANCE: float = 1e-10
|
|
40
|
+
|
|
41
|
+
# Base step for the Newton-Raphson numerical derivative.
|
|
42
|
+
NEWTON_DERIVATIVE_STEP: float = 1e-5
|
|
43
|
+
|
|
44
|
+
# RK45 adaptive integrator defaults.
|
|
45
|
+
RK45_DEFAULT_ATOL: float = 1e-6
|
|
46
|
+
RK45_DEFAULT_RTOL: float = 1e-3
|
|
47
|
+
RK45_DEFAULT_DT: float = 0.01
|
|
48
|
+
|
|
49
|
+
# Adaptive-step shrink / grow factors for the RK45 controller.
|
|
50
|
+
RK45_STEP_SHRINK: float = 0.1
|
|
51
|
+
RK45_STEP_SAFETY: float = 0.9
|
|
52
|
+
RK45_STEP_GROW: float = 10.0
|
|
53
|
+
|
|
54
|
+
# Loop-termination epsilon for ODE time stepping (``while t < t_end - eps``).
|
|
55
|
+
LOOP_EPSILON: float = 1e-12
|
|
56
|
+
|
|
57
|
+
# Near-minimum positive float (Numerical Recipes ``FPMIN``), used to avoid
|
|
58
|
+
# underflow in continued-fraction / series evaluations.
|
|
59
|
+
NEAR_MIN_FLOAT: float = 1e-300
|
|
60
|
+
|
|
61
|
+
# Adam-family optimizer defaults (Kingma & Ba 2015). Unifies the defaults
|
|
62
|
+
# previously split between ``optimization.minimize`` (lr=0.01) and
|
|
63
|
+
# ``nlp.optim`` (lr=1e-3) — both now read ``ADAM_DEFAULT_LR``.
|
|
64
|
+
ADAM_DEFAULT_LR: float = 1e-3
|
|
65
|
+
ADAM_DEFAULT_BETAS: tuple[float, float] = (0.9, 0.999)
|
|
66
|
+
ADAM_DEFAULT_EPS: float = 1e-8
|
|
67
|
+
|
|
68
|
+
# SGD learning rate default.
|
|
69
|
+
SGD_DEFAULT_LR: float = 0.01
|
|
70
|
+
|
|
71
|
+
# MLP / plain gradient-descent learning rate default.
|
|
72
|
+
GD_DEFAULT_LR: float = 0.01
|
|
73
|
+
|
|
74
|
+
# Layer normalization variance-floor epsilon.
|
|
75
|
+
LAYERNORM_EPS: float = 1e-5
|
|
76
|
+
|
|
77
|
+
# Sampling temperature divide-by-zero guard.
|
|
78
|
+
SAMPLING_TEMPERATURE_EPS: float = 1e-8
|
|
79
|
+
|
|
80
|
+
# Softmax sampling divide-by-zero guard (legacy ``1e-8``).
|
|
81
|
+
SOFTMAX_TEMPERATURE_EPS: float = 1e-8
|
|
82
|
+
|
|
83
|
+
# Masked-fill sentinel replacing the legacy ``-1e9`` literal in NLP attention
|
|
84
|
+
# masks. ``-inf`` propagates to a true zero after softmax; ``-1e9`` only
|
|
85
|
+
# underflows to ~0, leaving a small residual probability.
|
|
86
|
+
NEG_INF_MASK: float = float("-inf")
|
|
87
|
+
|
|
88
|
+
# Entanglement (concurrence) detection threshold.
|
|
89
|
+
CONCURRENCE_THRESHOLD: float = 1e-9
|
|
90
|
+
|
|
91
|
+
# Plotting normalization floor (avoids division by zero on flat signals).
|
|
92
|
+
PLOT_NORMALIZATION_FLOOR: float = 1e-10
|
cds/core/models.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Core data models for CDS.
|
|
2
|
+
|
|
3
|
+
The data models here are plain :mod:`dataclasses` — no third-party validation
|
|
4
|
+
library. Field constraints that previously relied on ``pydantic`` (the
|
|
5
|
+
``[0.0, 1.0]`` confidence range, enum coercion of ``domain``) are enforced in
|
|
6
|
+
``Hypothesis.__post_init__``, which raises :class:`ValueError` on violations.
|
|
7
|
+
This keeps the whole ``cds`` package zero-dependency at runtime while
|
|
8
|
+
preserving the construction contract the test suite pins down.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Domain(str, Enum):
|
|
20
|
+
"""Broad scientific domains supported by CDS."""
|
|
21
|
+
|
|
22
|
+
PHYSICS = "physics"
|
|
23
|
+
COSMOLOGY = "cosmology"
|
|
24
|
+
MATHEMATICS = "mathematics"
|
|
25
|
+
BIOLOGY = "biology"
|
|
26
|
+
CHEMISTRY = "chemistry"
|
|
27
|
+
GENERAL_SCIENCE = "general_science"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class HypothesisStatus(str, Enum):
|
|
31
|
+
"""Lifecycle states for a Hypothesis."""
|
|
32
|
+
|
|
33
|
+
NEW = "new"
|
|
34
|
+
REFINED = "refined"
|
|
35
|
+
CRITIQUED = "critiqued"
|
|
36
|
+
TESTABLE = "testable"
|
|
37
|
+
VALIDATED = "validated"
|
|
38
|
+
REJECTED = "rejected"
|
|
39
|
+
ARCHIVED = "archived"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class Hypothesis:
|
|
44
|
+
"""A scientific hypothesis with metadata and traceability.
|
|
45
|
+
|
|
46
|
+
Attributes:
|
|
47
|
+
id: Unique identifier.
|
|
48
|
+
statement: The core hypothesis statement.
|
|
49
|
+
domain: Scientific domain; accepts a :class:`Domain` or its string value.
|
|
50
|
+
research_question: The research question this hypothesis addresses.
|
|
51
|
+
rationale: Optional reasoning behind the hypothesis.
|
|
52
|
+
assumptions: Preconditions assumed to hold.
|
|
53
|
+
predictions: Testable consequences of the hypothesis.
|
|
54
|
+
status: Current point in the hypothesis lifecycle.
|
|
55
|
+
confidence: Subjective belief in the hypothesis, in the closed
|
|
56
|
+
interval ``[0.0, 1.0]``.
|
|
57
|
+
created_at: Timezone-aware UTC timestamp of creation.
|
|
58
|
+
tags: Free-form categorical tags.
|
|
59
|
+
sources: References or retrieval sources backing the hypothesis.
|
|
60
|
+
metadata: Arbitrary string-keyed/string-valued extra information.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
id: str
|
|
64
|
+
statement: str
|
|
65
|
+
domain: Domain
|
|
66
|
+
research_question: str
|
|
67
|
+
rationale: str | None = None
|
|
68
|
+
assumptions: list[str] = field(default_factory=list)
|
|
69
|
+
predictions: list[str] = field(default_factory=list)
|
|
70
|
+
status: HypothesisStatus = HypothesisStatus.NEW
|
|
71
|
+
confidence: float = 0.5
|
|
72
|
+
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
73
|
+
tags: list[str] = field(default_factory=list)
|
|
74
|
+
sources: list[str] = field(default_factory=list)
|
|
75
|
+
metadata: dict[str, str] = field(default_factory=dict)
|
|
76
|
+
|
|
77
|
+
def __post_init__(self) -> None:
|
|
78
|
+
"""Coerce ``domain`` to its enum and validate ``confidence`` range.
|
|
79
|
+
|
|
80
|
+
Mirrors the coercion/validation pydantic performed previously: a
|
|
81
|
+
bare string like ``"physics"`` is accepted and converted to
|
|
82
|
+
:class:`Domain.PHYSICS`; ``confidence`` must lie in ``[0.0, 1.0]``.
|
|
83
|
+
Unknown values raise ``ValueError`` (matching the old
|
|
84
|
+
``ValidationError`` behaviour).
|
|
85
|
+
"""
|
|
86
|
+
# Coerce string values into the enum members so callers can pass either
|
|
87
|
+
# ``Domain.PHYSICS`` or the bare string ``"physics"``. This mirrors the
|
|
88
|
+
# coercion pydantic performed previously; an unknown value raises
|
|
89
|
+
# ValueError, matching the old ValidationError behaviour.
|
|
90
|
+
if not isinstance(self.domain, Domain):
|
|
91
|
+
try:
|
|
92
|
+
self.domain = Domain(self.domain)
|
|
93
|
+
except ValueError as exc:
|
|
94
|
+
raise ValueError(f"{self.domain!r} is not a valid Domain") from exc
|
|
95
|
+
|
|
96
|
+
# The closed interval [0.0, 1.0] is the only constraint on confidence.
|
|
97
|
+
# Out-of-range values raise ValueError, matching the former pydantic
|
|
98
|
+
# ``ge=0.0, le=1.0`` contract that the test suite pins down.
|
|
99
|
+
if not 0.0 <= self.confidence <= 1.0:
|
|
100
|
+
raise ValueError(f"confidence must be in [0.0, 1.0], got {self.confidence}")
|
|
101
|
+
|
|
102
|
+
def to_dict(self) -> dict[str, Any]:
|
|
103
|
+
"""Serialize this hypothesis to a JSON-friendly dict.
|
|
104
|
+
|
|
105
|
+
Mirrors the previous ``pydantic`` ``model_dump(mode="json")`` output:
|
|
106
|
+
enums become their string ``value`` and ``created_at`` is rendered as
|
|
107
|
+
an ISO-8601 string, so the result is directly :func:`json.dumps`-able.
|
|
108
|
+
"""
|
|
109
|
+
return {
|
|
110
|
+
"id": self.id,
|
|
111
|
+
"statement": self.statement,
|
|
112
|
+
"domain": self.domain.value,
|
|
113
|
+
"research_question": self.research_question,
|
|
114
|
+
"rationale": self.rationale,
|
|
115
|
+
"assumptions": list(self.assumptions),
|
|
116
|
+
"predictions": list(self.predictions),
|
|
117
|
+
"status": self.status.value,
|
|
118
|
+
"confidence": self.confidence,
|
|
119
|
+
"created_at": self.created_at.isoformat(),
|
|
120
|
+
"tags": list(self.tags),
|
|
121
|
+
"sources": list(self.sources),
|
|
122
|
+
"metadata": dict(self.metadata),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def from_dict(cls, data: dict[str, Any]) -> Hypothesis:
|
|
127
|
+
"""Rebuild a :class:`Hypothesis` from :meth:`to_dict` output.
|
|
128
|
+
|
|
129
|
+
Completes the JSON round trip: enum strings are coerced back into
|
|
130
|
+
their :class:`Domain` / :class:`HypothesisStatus` members and
|
|
131
|
+
``created_at`` is parsed from its ISO-8601 rendering. Missing
|
|
132
|
+
optional keys fall back to the same defaults as the constructor, so
|
|
133
|
+
partially-specified records load cleanly. Unknown ``status`` values
|
|
134
|
+
raise ``ValueError``.
|
|
135
|
+
"""
|
|
136
|
+
payload = dict(data) # copy so we never mutate the caller's dict
|
|
137
|
+
created_at = payload.get("created_at")
|
|
138
|
+
if isinstance(created_at, str):
|
|
139
|
+
payload["created_at"] = datetime.fromisoformat(created_at)
|
|
140
|
+
status = payload.get("status")
|
|
141
|
+
if status is not None and not isinstance(status, HypothesisStatus):
|
|
142
|
+
payload["status"] = HypothesisStatus(status)
|
|
143
|
+
return cls(**payload)
|
|
144
|
+
|
|
145
|
+
def to_markdown(self) -> str:
|
|
146
|
+
"""Render this hypothesis as a structured Markdown document."""
|
|
147
|
+
lines = [
|
|
148
|
+
f"# Hypothesis: {self.id}",
|
|
149
|
+
"",
|
|
150
|
+
f"**Statement**: {self.statement}",
|
|
151
|
+
"",
|
|
152
|
+
f"**Domain**: {self.domain.value}",
|
|
153
|
+
f"**Research Question**: {self.research_question}",
|
|
154
|
+
f"**Status**: {self.status.value} | **Confidence**: {self.confidence:.2f}",
|
|
155
|
+
"",
|
|
156
|
+
]
|
|
157
|
+
if self.rationale:
|
|
158
|
+
lines += ["## Rationale", self.rationale, ""]
|
|
159
|
+
if self.assumptions:
|
|
160
|
+
lines += ["## Assumptions"] + [f"- {a}" for a in self.assumptions] + [""]
|
|
161
|
+
if self.predictions:
|
|
162
|
+
preds = [f"- {p}" for p in self.predictions]
|
|
163
|
+
lines += ["## Predictions / Testable Consequences"] + preds + [""]
|
|
164
|
+
if self.tags:
|
|
165
|
+
lines += [f"**Tags**: {', '.join(self.tags)}"]
|
|
166
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Data loading, analysis and visualization helpers."""
|
|
2
|
+
|
|
3
|
+
from cds.data_analysis.dataset import DataSet
|
|
4
|
+
from cds.data_analysis.loader import DataTable, load_csv
|
|
5
|
+
from cds.data_analysis.transform import moving_average, normalize, z_score
|
|
6
|
+
from cds.data_analysis.viz import plot_bar, plot_line
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"load_csv",
|
|
10
|
+
"DataTable",
|
|
11
|
+
"DataSet",
|
|
12
|
+
"normalize",
|
|
13
|
+
"z_score",
|
|
14
|
+
"moving_average",
|
|
15
|
+
"plot_bar",
|
|
16
|
+
"plot_line",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def __getattr__(name: str): # type: ignore[no-untyped-def]
|
|
21
|
+
"""Lazily expose the optional pandas bridge.
|
|
22
|
+
|
|
23
|
+
``pandas_io`` imports pandas at call time, so it is only resolved on first
|
|
24
|
+
access — keeping the core import zero-dependency.
|
|
25
|
+
"""
|
|
26
|
+
if name in {"to_dataframe", "from_dataframe"}:
|
|
27
|
+
from cds.data_analysis import pandas_io
|
|
28
|
+
|
|
29
|
+
return getattr(pandas_io, name)
|
|
30
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|