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/graph/algorithms.py
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"""Graph algorithms — standard implementations from Cormen et al. (CLRS).
|
|
2
|
+
|
|
3
|
+
References:
|
|
4
|
+
- Cormen, Leiserson, Rivest, Stein. Introduction to Algorithms (4th ed.)
|
|
5
|
+
- Dijkstra, E.W. (1959). A note on two problems in connexion with graphs.
|
|
6
|
+
- Kruskal, J.B. (1956). On the shortest spanning subtree of a graph.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import heapq
|
|
12
|
+
import math
|
|
13
|
+
from collections import deque
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Edge:
|
|
19
|
+
"""Weighted edge in a graph."""
|
|
20
|
+
|
|
21
|
+
src: int
|
|
22
|
+
dst: int
|
|
23
|
+
weight: float = 1.0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Graph:
|
|
28
|
+
"""Adjacency-list graph representation.
|
|
29
|
+
|
|
30
|
+
Supports both directed and undirected graphs with weighted edges.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
n_vertices: int
|
|
34
|
+
directed: bool = False
|
|
35
|
+
adj: dict[int, list[tuple[int, float]]] = field(default_factory=dict)
|
|
36
|
+
edges: list[Edge] = field(default_factory=list)
|
|
37
|
+
|
|
38
|
+
def add_edge(self, u: int, v: int, weight: float = 1.0) -> None:
|
|
39
|
+
"""Add an edge from u to v (and v to u if undirected)."""
|
|
40
|
+
self.adj.setdefault(u, []).append((v, weight))
|
|
41
|
+
if not self.directed:
|
|
42
|
+
self.adj.setdefault(v, []).append((u, weight))
|
|
43
|
+
self.edges.append(Edge(u, v, weight))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def bfs(graph: Graph, start: int) -> list[int]:
|
|
47
|
+
"""Breadth-first search traversal.
|
|
48
|
+
|
|
49
|
+
Returns vertices in BFS order starting from `start`.
|
|
50
|
+
Time complexity: O(V + E) [CLRS §22.2]
|
|
51
|
+
"""
|
|
52
|
+
visited: set[int] = set()
|
|
53
|
+
order: list[int] = []
|
|
54
|
+
queue: deque[int] = deque([start])
|
|
55
|
+
visited.add(start)
|
|
56
|
+
|
|
57
|
+
while queue:
|
|
58
|
+
node = queue.popleft()
|
|
59
|
+
order.append(node)
|
|
60
|
+
for neighbor, _ in graph.adj.get(node, []):
|
|
61
|
+
if neighbor not in visited:
|
|
62
|
+
visited.add(neighbor)
|
|
63
|
+
queue.append(neighbor)
|
|
64
|
+
return order
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def dfs(graph: Graph, start: int) -> list[int]:
|
|
68
|
+
"""Depth-first search traversal (iterative).
|
|
69
|
+
|
|
70
|
+
Returns vertices in DFS order starting from `start`.
|
|
71
|
+
Time complexity: O(V + E) [CLRS §22.3]
|
|
72
|
+
"""
|
|
73
|
+
visited: set[int] = set()
|
|
74
|
+
order: list[int] = []
|
|
75
|
+
stack: list[int] = [start]
|
|
76
|
+
|
|
77
|
+
while stack:
|
|
78
|
+
node = stack.pop()
|
|
79
|
+
if node in visited:
|
|
80
|
+
continue
|
|
81
|
+
visited.add(node)
|
|
82
|
+
order.append(node)
|
|
83
|
+
for neighbor, _ in reversed(graph.adj.get(node, [])):
|
|
84
|
+
if neighbor not in visited:
|
|
85
|
+
stack.append(neighbor)
|
|
86
|
+
return order
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def dijkstra(
|
|
90
|
+
graph: Graph,
|
|
91
|
+
start: int,
|
|
92
|
+
) -> tuple[dict[int, float], dict[int, int | None]]:
|
|
93
|
+
"""Dijkstra's shortest path algorithm.
|
|
94
|
+
|
|
95
|
+
Returns (distances, predecessors) from `start` to all reachable vertices.
|
|
96
|
+
Time complexity: O((V + E) log V) with binary heap [Dijkstra 1959]
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
graph: weighted graph (non-negative weights)
|
|
100
|
+
start: source vertex
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
distances: dict mapping vertex -> shortest distance from start
|
|
104
|
+
predecessors: dict mapping vertex -> previous vertex on shortest path
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
ValueError: if a negative weight is encountered
|
|
108
|
+
"""
|
|
109
|
+
dist: dict[int, float] = {start: 0.0}
|
|
110
|
+
prev: dict[int, int | None] = {start: None}
|
|
111
|
+
heap: list[tuple[float, int]] = [(0.0, start)]
|
|
112
|
+
visited: set[int] = set()
|
|
113
|
+
|
|
114
|
+
while heap:
|
|
115
|
+
d, u = heapq.heappop(heap)
|
|
116
|
+
if u in visited:
|
|
117
|
+
continue
|
|
118
|
+
visited.add(u)
|
|
119
|
+
|
|
120
|
+
for v, w in graph.adj.get(u, []):
|
|
121
|
+
if w < 0:
|
|
122
|
+
raise ValueError("negative edge weights not supported")
|
|
123
|
+
new_dist = d + w
|
|
124
|
+
if v not in dist or new_dist < dist[v]:
|
|
125
|
+
dist[v] = new_dist
|
|
126
|
+
prev[v] = u
|
|
127
|
+
heapq.heappush(heap, (new_dist, v))
|
|
128
|
+
|
|
129
|
+
return dist, prev
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _find(parent: dict[int, int], x: int) -> int:
|
|
133
|
+
"""Union-Find: find with path compression."""
|
|
134
|
+
while parent[x] != x:
|
|
135
|
+
parent[x] = parent[parent[x]]
|
|
136
|
+
x = parent[x]
|
|
137
|
+
return x
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _union(
|
|
141
|
+
parent: dict[int, int],
|
|
142
|
+
rank: dict[int, int],
|
|
143
|
+
a: int,
|
|
144
|
+
b: int,
|
|
145
|
+
) -> bool:
|
|
146
|
+
"""Union-Find: union by rank. Returns True if merged."""
|
|
147
|
+
ra, rb = _find(parent, a), _find(parent, b)
|
|
148
|
+
if ra == rb:
|
|
149
|
+
return False
|
|
150
|
+
if rank[ra] < rank[rb]:
|
|
151
|
+
ra, rb = rb, ra
|
|
152
|
+
parent[rb] = ra
|
|
153
|
+
if rank[ra] == rank[rb]:
|
|
154
|
+
rank[ra] += 1
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def kruskal_mst(graph: Graph) -> tuple[list[Edge], float]:
|
|
159
|
+
"""Kruskal's minimum spanning tree algorithm.
|
|
160
|
+
|
|
161
|
+
Time complexity: O(E log E) [Kruskal 1956]
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
graph: undirected weighted graph
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
mst_edges: list of edges in the MST
|
|
168
|
+
total_weight: sum of edge weights in the MST
|
|
169
|
+
"""
|
|
170
|
+
sorted_edges = sorted(graph.edges, key=lambda e: e.weight)
|
|
171
|
+
parent = {i: i for i in range(graph.n_vertices)}
|
|
172
|
+
rank = {i: 0 for i in range(graph.n_vertices)}
|
|
173
|
+
|
|
174
|
+
mst: list[Edge] = []
|
|
175
|
+
total = 0.0
|
|
176
|
+
|
|
177
|
+
for edge in sorted_edges:
|
|
178
|
+
if _union(parent, rank, edge.src, edge.dst):
|
|
179
|
+
mst.append(edge)
|
|
180
|
+
total += edge.weight
|
|
181
|
+
if len(mst) == graph.n_vertices - 1:
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
return mst, total
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def topological_sort(graph: Graph) -> list[int]:
|
|
188
|
+
"""Kahn's algorithm for topological sort of a DAG.
|
|
189
|
+
|
|
190
|
+
Time complexity: O(V + E) [CLRS §22.4]
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
graph: directed acyclic graph
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
list of vertices in topological order
|
|
197
|
+
|
|
198
|
+
Raises:
|
|
199
|
+
ValueError: if graph contains a cycle
|
|
200
|
+
"""
|
|
201
|
+
in_degree: dict[int, int] = {i: 0 for i in range(graph.n_vertices)}
|
|
202
|
+
for u in graph.adj:
|
|
203
|
+
for v, _ in graph.adj[u]:
|
|
204
|
+
in_degree[v] = in_degree.get(v, 0) + 1
|
|
205
|
+
|
|
206
|
+
queue: deque[int] = deque(v for v in range(graph.n_vertices) if in_degree.get(v, 0) == 0)
|
|
207
|
+
order: list[int] = []
|
|
208
|
+
|
|
209
|
+
while queue:
|
|
210
|
+
u = queue.popleft()
|
|
211
|
+
order.append(u)
|
|
212
|
+
for v, _ in graph.adj.get(u, []):
|
|
213
|
+
in_degree[v] -= 1
|
|
214
|
+
if in_degree[v] == 0:
|
|
215
|
+
queue.append(v)
|
|
216
|
+
|
|
217
|
+
if len(order) != graph.n_vertices:
|
|
218
|
+
raise ValueError("graph contains a cycle")
|
|
219
|
+
return order
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def has_cycle(graph: Graph) -> bool:
|
|
223
|
+
"""Detect if a directed graph has a cycle using DFS coloring.
|
|
224
|
+
|
|
225
|
+
Time complexity: O(V + E)
|
|
226
|
+
|
|
227
|
+
WHITE=0 (unvisited), GRAY=1 (in current DFS path), BLACK=2 (finished).
|
|
228
|
+
"""
|
|
229
|
+
WHITE, GRAY, BLACK = 0, 1, 2
|
|
230
|
+
color: dict[int, int] = {i: WHITE for i in range(graph.n_vertices)}
|
|
231
|
+
|
|
232
|
+
def _dfs(u: int) -> bool:
|
|
233
|
+
color[u] = GRAY
|
|
234
|
+
for v, _ in graph.adj.get(u, []):
|
|
235
|
+
if color[v] == GRAY:
|
|
236
|
+
return True
|
|
237
|
+
if color[v] == WHITE and _dfs(v):
|
|
238
|
+
return True
|
|
239
|
+
color[u] = BLACK
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
return any(_dfs(v) for v in range(graph.n_vertices) if color[v] == WHITE)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def degree(graph: Graph, v: int) -> int:
|
|
246
|
+
"""Degree of vertex ``v`` (out-degree if directed)."""
|
|
247
|
+
if v < 0 or v >= graph.n_vertices:
|
|
248
|
+
raise ValueError(f"vertex {v} out of range [0, {graph.n_vertices})")
|
|
249
|
+
return len(graph.adj.get(v, []))
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def connected_components(graph: Graph) -> list[list[int]]:
|
|
253
|
+
"""Connected components of an undirected graph (BFS forests).
|
|
254
|
+
|
|
255
|
+
For directed graphs, returns weakly connected components (ignores direction).
|
|
256
|
+
"""
|
|
257
|
+
# Build undirected adjacency for weak connectivity.
|
|
258
|
+
undirected: dict[int, set[int]] = {i: set() for i in range(graph.n_vertices)}
|
|
259
|
+
for u, nbrs in graph.adj.items():
|
|
260
|
+
for v, _ in nbrs:
|
|
261
|
+
undirected[u].add(v)
|
|
262
|
+
undirected[v].add(u)
|
|
263
|
+
|
|
264
|
+
remaining = set(range(graph.n_vertices))
|
|
265
|
+
components: list[list[int]] = []
|
|
266
|
+
while remaining:
|
|
267
|
+
start = min(remaining)
|
|
268
|
+
visited: set[int] = set()
|
|
269
|
+
queue: deque[int] = deque([start])
|
|
270
|
+
visited.add(start)
|
|
271
|
+
order: list[int] = []
|
|
272
|
+
while queue:
|
|
273
|
+
u = queue.popleft()
|
|
274
|
+
order.append(u)
|
|
275
|
+
for v in undirected[u]:
|
|
276
|
+
if v not in visited:
|
|
277
|
+
visited.add(v)
|
|
278
|
+
queue.append(v)
|
|
279
|
+
components.append(sorted(order))
|
|
280
|
+
remaining -= visited
|
|
281
|
+
return components
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def bellman_ford(
|
|
285
|
+
graph: Graph,
|
|
286
|
+
start: int,
|
|
287
|
+
) -> tuple[dict[int, float], dict[int, int | None]]:
|
|
288
|
+
"""Bellman–Ford single-source shortest paths (allows negative weights).
|
|
289
|
+
|
|
290
|
+
Returns ``(distances, predecessors)``. Unreachable vertices are omitted
|
|
291
|
+
from ``distances``. Raises ``ValueError`` if a negative cycle is reachable
|
|
292
|
+
from ``start``.
|
|
293
|
+
"""
|
|
294
|
+
if start < 0 or start >= graph.n_vertices:
|
|
295
|
+
raise ValueError(f"start vertex {start} out of range")
|
|
296
|
+
dist: dict[int, float] = {start: 0.0}
|
|
297
|
+
prev: dict[int, int | None] = {start: None}
|
|
298
|
+
|
|
299
|
+
for _ in range(graph.n_vertices - 1):
|
|
300
|
+
updated = False
|
|
301
|
+
for e in graph.edges:
|
|
302
|
+
if e.src not in dist:
|
|
303
|
+
continue
|
|
304
|
+
nd = dist[e.src] + e.weight
|
|
305
|
+
if e.dst not in dist or nd < dist[e.dst]:
|
|
306
|
+
dist[e.dst] = nd
|
|
307
|
+
prev[e.dst] = e.src
|
|
308
|
+
updated = True
|
|
309
|
+
if not graph.directed and e.dst in dist:
|
|
310
|
+
# undirected: edges list stores one direction; relax reverse too
|
|
311
|
+
nd_r = dist[e.dst] + e.weight
|
|
312
|
+
if e.src not in dist or nd_r < dist[e.src]:
|
|
313
|
+
dist[e.src] = nd_r
|
|
314
|
+
prev[e.src] = e.dst
|
|
315
|
+
updated = True
|
|
316
|
+
if not updated:
|
|
317
|
+
break
|
|
318
|
+
|
|
319
|
+
# Negative cycle detection
|
|
320
|
+
for e in graph.edges:
|
|
321
|
+
if e.src in dist and (e.dst not in dist or dist[e.src] + e.weight < dist[e.dst]):
|
|
322
|
+
raise ValueError("negative cycle detected")
|
|
323
|
+
# Undirected reverse check (usually already caught by the forward test).
|
|
324
|
+
if (
|
|
325
|
+
not graph.directed
|
|
326
|
+
and e.dst in dist
|
|
327
|
+
and (e.src not in dist or dist[e.dst] + e.weight < dist[e.src])
|
|
328
|
+
): # pragma: no cover
|
|
329
|
+
raise ValueError("negative cycle detected")
|
|
330
|
+
return dist, prev
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def floyd_warshall(graph: Graph) -> list[list[float]]:
|
|
334
|
+
"""All-pairs shortest paths (Floyd–Warshall).
|
|
335
|
+
|
|
336
|
+
Returns an ``n x n`` distance matrix. Unreachable pairs are ``math.inf``.
|
|
337
|
+
Raises ``ValueError`` if a negative cycle exists on the diagonal.
|
|
338
|
+
"""
|
|
339
|
+
n = graph.n_vertices
|
|
340
|
+
dist: list[list[float]] = [[math.inf] * n for _ in range(n)]
|
|
341
|
+
for i in range(n):
|
|
342
|
+
dist[i][i] = 0.0
|
|
343
|
+
for e in graph.edges:
|
|
344
|
+
if e.weight < dist[e.src][e.dst]:
|
|
345
|
+
dist[e.src][e.dst] = e.weight
|
|
346
|
+
if not graph.directed and e.weight < dist[e.dst][e.src]:
|
|
347
|
+
dist[e.dst][e.src] = e.weight
|
|
348
|
+
|
|
349
|
+
for k in range(n):
|
|
350
|
+
for i in range(n):
|
|
351
|
+
dik = dist[i][k]
|
|
352
|
+
if dik == math.inf:
|
|
353
|
+
continue
|
|
354
|
+
for j in range(n):
|
|
355
|
+
cand = dik + dist[k][j]
|
|
356
|
+
if cand < dist[i][j]:
|
|
357
|
+
dist[i][j] = cand
|
|
358
|
+
|
|
359
|
+
for i in range(n):
|
|
360
|
+
if dist[i][i] < 0:
|
|
361
|
+
raise ValueError("negative cycle detected")
|
|
362
|
+
return dist
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def prim_mst(graph: Graph, start: int = 0) -> tuple[list[Edge], float]:
|
|
366
|
+
"""Prim's MST algorithm (binary heap), undirected graphs.
|
|
367
|
+
|
|
368
|
+
Returns ``(mst_edges, total_weight)``.
|
|
369
|
+
"""
|
|
370
|
+
if graph.directed:
|
|
371
|
+
raise ValueError("prim_mst requires an undirected graph")
|
|
372
|
+
if graph.n_vertices == 0:
|
|
373
|
+
return [], 0.0
|
|
374
|
+
if start < 0 or start >= graph.n_vertices:
|
|
375
|
+
raise ValueError(f"start vertex {start} out of range")
|
|
376
|
+
|
|
377
|
+
in_mst = [False] * graph.n_vertices
|
|
378
|
+
# (weight, u, v) — edge u->v to add v
|
|
379
|
+
heap: list[tuple[float, int, int]] = [(0.0, start, start)]
|
|
380
|
+
mst: list[Edge] = []
|
|
381
|
+
total = 0.0
|
|
382
|
+
parent: dict[int, int | None] = {start: None}
|
|
383
|
+
best: dict[int, float] = {start: 0.0}
|
|
384
|
+
|
|
385
|
+
while heap and len(mst) < graph.n_vertices:
|
|
386
|
+
w, u, v = heapq.heappop(heap)
|
|
387
|
+
if in_mst[v]:
|
|
388
|
+
continue
|
|
389
|
+
in_mst[v] = True
|
|
390
|
+
if u != v:
|
|
391
|
+
mst.append(Edge(u, v, w))
|
|
392
|
+
total += w
|
|
393
|
+
for nbr, wt in graph.adj.get(v, []):
|
|
394
|
+
if not in_mst[nbr] and (nbr not in best or wt < best[nbr]):
|
|
395
|
+
best[nbr] = wt
|
|
396
|
+
parent[nbr] = v
|
|
397
|
+
heapq.heappush(heap, (wt, v, nbr))
|
|
398
|
+
|
|
399
|
+
return mst, total
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hypothesis generation and evaluation module for Cognitive Discovery.
|
|
3
|
+
|
|
4
|
+
Provides tools to generate structured scientific hypotheses from
|
|
5
|
+
research questions. Includes prompt templates and an offline
|
|
6
|
+
generator for immediate use, plus a clear Protocol for supplying
|
|
7
|
+
custom generator implementations for specialized research needs.
|
|
8
|
+
|
|
9
|
+
The focus is on making hypotheses falsifiable, with explicit
|
|
10
|
+
assumptions, predictions, and confidence estimates.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
from cds.hypothesis import generate_hypotheses
|
|
14
|
+
|
|
15
|
+
hypos = generate_hypotheses(
|
|
16
|
+
"Why do we observe the Hubble tension?",
|
|
17
|
+
domain="cosmology",
|
|
18
|
+
n=3
|
|
19
|
+
)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from cds.core.models import Domain, Hypothesis, HypothesisStatus
|
|
23
|
+
from cds.hypothesis.evaluator import (
|
|
24
|
+
ChiSquareGofPayload,
|
|
25
|
+
EvaluationData,
|
|
26
|
+
EvaluationResult,
|
|
27
|
+
HypothesisEvaluator,
|
|
28
|
+
)
|
|
29
|
+
from cds.hypothesis.generator import (
|
|
30
|
+
HypothesisGenerator,
|
|
31
|
+
PromptTemplate,
|
|
32
|
+
SimpleOfflineGenerator,
|
|
33
|
+
generate_hypotheses,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"Domain",
|
|
38
|
+
"Hypothesis",
|
|
39
|
+
"HypothesisStatus",
|
|
40
|
+
"HypothesisGenerator",
|
|
41
|
+
"PromptTemplate",
|
|
42
|
+
"SimpleOfflineGenerator",
|
|
43
|
+
"generate_hypotheses",
|
|
44
|
+
"HypothesisEvaluator",
|
|
45
|
+
"EvaluationData",
|
|
46
|
+
"ChiSquareGofPayload",
|
|
47
|
+
"EvaluationResult",
|
|
48
|
+
]
|