hypergraphz 0.1.5__tar.gz
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.
- hypergraphz-0.1.5/.gitignore +13 -0
- hypergraphz-0.1.5/LICENSE +19 -0
- hypergraphz-0.1.5/PKG-INFO +163 -0
- hypergraphz-0.1.5/README.md +154 -0
- hypergraphz-0.1.5/build.zig +134 -0
- hypergraphz-0.1.5/build.zig.zon +14 -0
- hypergraphz-0.1.5/hatch_build.py +45 -0
- hypergraphz-0.1.5/pyproject.toml +69 -0
- hypergraphz-0.1.5/python/hypergraphz/__init__.py +28 -0
- hypergraphz-0.1.5/python/hypergraphz/_binding.py +321 -0
- hypergraphz-0.1.5/python/hypergraphz/_errors.py +55 -0
- hypergraphz-0.1.5/python/hypergraphz/_graph.py +787 -0
- hypergraphz-0.1.5/python/hypergraphz/_query.py +120 -0
- hypergraphz-0.1.5/src/hgz_c_api.zig +1056 -0
- hypergraphz-0.1.5/src/hypergraphz.zig +4090 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) Davy Duperron
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hypergraphz
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Python bindings for the HypergraphZ hypergraph library
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# HypergraphZ - A Hypergraph Implementation in Zig
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
HypergraphZ is a directed hypergraph implementation in Zig (https://en.wikipedia.org/wiki/Hypergraph):
|
|
15
|
+
|
|
16
|
+
- Each hyperedge can contain zero, one (unary) or multiple vertices.
|
|
17
|
+
- Each hyperedge can contain vertices directed to themselves one or more times.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## For Python users
|
|
22
|
+
|
|
23
|
+
### Installation
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pip install hypergraphz
|
|
27
|
+
# or with uv
|
|
28
|
+
uv add hypergraphz
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Pre-built wheels are available for Linux (x86\_64, aarch64), macOS (x86\_64, arm64), and Windows (x86\_64).
|
|
32
|
+
|
|
33
|
+
### Quick start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from hypergraphz import Hypergraph
|
|
37
|
+
|
|
38
|
+
g = Hypergraph()
|
|
39
|
+
|
|
40
|
+
# Add vertices and hyperedges
|
|
41
|
+
alice = g.add_vertex({"name": "alice", "age": 30})
|
|
42
|
+
bob = g.add_vertex({"name": "bob", "age": 25})
|
|
43
|
+
carol = g.add_vertex({"name": "carol", "age": 35})
|
|
44
|
+
|
|
45
|
+
collab = g.add_hyperedge({"type": "project"})
|
|
46
|
+
g.connect(collab, [alice, bob, carol])
|
|
47
|
+
|
|
48
|
+
# Build the reverse index before querying
|
|
49
|
+
g.build()
|
|
50
|
+
|
|
51
|
+
# Shortest path
|
|
52
|
+
path = g.shortest_path(alice, carol)
|
|
53
|
+
|
|
54
|
+
# Fluent query builders
|
|
55
|
+
active = (
|
|
56
|
+
g.vertices()
|
|
57
|
+
.where(lambda d: d["age"] > 28)
|
|
58
|
+
.data()
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Structural algorithms
|
|
62
|
+
scores, _, _ = g.page_rank()
|
|
63
|
+
centrality = g.centrality()
|
|
64
|
+
components = g.connected_components()
|
|
65
|
+
|
|
66
|
+
# Sub-graph operations return independent Hypergraph objects
|
|
67
|
+
sub = g.vertex_induced_subgraph([alice, bob])
|
|
68
|
+
dual = g.dual()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### API summary
|
|
72
|
+
|
|
73
|
+
| Category | Methods |
|
|
74
|
+
|---|---|
|
|
75
|
+
| Lifecycle | `build()`, `clear()`, `save()`, `load()`, `clone()` |
|
|
76
|
+
| Vertices | `add_vertex()`, `get_vertex()`, `update_vertex()`, `delete_vertex()`, `vertex_count()`, `all_vertex_ids()` |
|
|
77
|
+
| Hyperedges | `add_hyperedge()`, `get_hyperedge()`, `update_hyperedge()`, `delete_hyperedge()`, `hyperedge_count()`, `all_hyperedge_ids()` |
|
|
78
|
+
| Relations | `connect()`, `prepend()`, `insert_vertex()`, `insert_many()`, `disconnect()`, `disconnect_at()` |
|
|
79
|
+
| Degree | `vertex_indegree()`, `vertex_outdegree()` |
|
|
80
|
+
| Queries | `hyperedge_vertices()`, `vertex_hyperedges()`, `neighborhood()`, `intersections()`, `hyperedges_connecting()`, `endpoints()`, `orphan_vertices()`, `orphan_hyperedges()` |
|
|
81
|
+
| Boolean | `is_connected()`, `is_reachable()`, `has_cycle()`, `is_k_uniform()` |
|
|
82
|
+
| Traversal | `shortest_path()`, `all_paths()`, `bfs()`, `dfs()`, `random_walk()` |
|
|
83
|
+
| Algorithms | `connected_components()`, `topological_sort()`, `cut_vertices()`, `centrality()`, `page_rank()`, `inclusions()`, `nestedness_profile()` |
|
|
84
|
+
| Matrices | `incidence_matrix()`, `incidence_matrix_coo()`, `laplacian()` |
|
|
85
|
+
| Sub-graphs | `dual()`, `k_skeleton()`, `vertex_induced_subgraph()`, `edge_induced_subgraph()`, `core()`, `expand_to_graph()`, `expand_to_star()`, `line_graph()`, `transitive_closure()` |
|
|
86
|
+
| Fluent builders | `vertices()` → `VertexQuery`, `hyperedges()` → `HyperedgeQuery` |
|
|
87
|
+
|
|
88
|
+
### Exceptions
|
|
89
|
+
|
|
90
|
+
All errors map to typed exceptions from `hypergraphz`:
|
|
91
|
+
|
|
92
|
+
| Exception | When raised |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `NotBuiltError` | Query requires `build()` first |
|
|
95
|
+
| `VertexNotFoundError` | Vertex ID does not exist |
|
|
96
|
+
| `HyperedgeNotFoundError` | Hyperedge ID does not exist |
|
|
97
|
+
| `CycleDetectedError` | Operation requires a DAG |
|
|
98
|
+
| `NoPathError` | No directed path exists |
|
|
99
|
+
| `IndexOutOfBoundsError` | Insertion index out of range |
|
|
100
|
+
| `NotEnoughVerticesError` | Operation needs more vertices |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## For Zig users
|
|
105
|
+
|
|
106
|
+
### Zig version
|
|
107
|
+
|
|
108
|
+
HypergraphZ currently requires **Zig 0.17.0-dev.242+5d55999d2**.
|
|
109
|
+
|
|
110
|
+
### Usage
|
|
111
|
+
|
|
112
|
+
Add `hypergraphz` as a dependency to your `build.zig.zon`:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
zig fetch --save https://github.com/yamafaktory/hypergraphz/archive/v0.1.0.tar.gz
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Add `hypergraphz` as a dependency to your `build.zig`:
|
|
119
|
+
|
|
120
|
+
```zig
|
|
121
|
+
const hypergraphz = b.dependency("hypergraphz", .{
|
|
122
|
+
.target = target,
|
|
123
|
+
.optimize = optimize,
|
|
124
|
+
});
|
|
125
|
+
exe.root_module.addImport("hypergraphz", hypergraphz.module("hypergraphz"));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Example
|
|
129
|
+
|
|
130
|
+
`examples/coauthorship.zig` models a small research community as a directed hypergraph
|
|
131
|
+
where vertices are researchers and hyperedges are papers. The first listed author is
|
|
132
|
+
treated as the corresponding author, giving each hyperedge a natural direction.
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
zig build example
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
It walks through seventeen features of the library in sequence:
|
|
139
|
+
|
|
140
|
+
| Section | API | What it shows |
|
|
141
|
+
| ------------------------ | ------------------------ | ------------------------------------------------------------ |
|
|
142
|
+
| Papers per researcher | `getVertexHyperedges` | reverse-index query |
|
|
143
|
+
| Research communities | `getConnectedComponents` | two isolated groups with no cross-group papers |
|
|
144
|
+
| Betweenness centrality | `computeCentrality` | which researchers bridge the most collaboration paths |
|
|
145
|
+
| Degrees of separation | `findShortestPath` | 1–3 hops within a community; unreachable across communities |
|
|
146
|
+
| Dual: paper-centric view | `getDual` | swap vertices ↔ hyperedges to see each researcher's papers |
|
|
147
|
+
| Shared authorship | `getIntersections` | researchers present on every paper in a given set |
|
|
148
|
+
| Seniority ordering | `topologicalSort` | hierarchy implied by the authorship direction |
|
|
149
|
+
| Pairwise expansion | `expandToGraph` | 6 papers encode 11 directed pairs in a regular graph |
|
|
150
|
+
| Spectral view | `toLaplacian` | normalized Laplacian; block-diagonal under disjoint groups |
|
|
151
|
+
| PageRank | `computePageRank` | citation-weighted stationary distribution; sums to 1 |
|
|
152
|
+
| Random walk | `randomWalk` | seeded weighted walk; stays inside the start's component |
|
|
153
|
+
| Star expansion | `expandToStar` | bipartite (researcher ↔ paper) projection; 2-uniform |
|
|
154
|
+
| Line graph | `getLineGraph` | papers as vertices, linked when they share an author |
|
|
155
|
+
| K-core decomposition | `getCore` | (s, t)-core peeling; trims the lone-paper researcher |
|
|
156
|
+
| Nestedness | `getInclusions` | strict-subset pairs across hyperedges; sized profile |
|
|
157
|
+
| Co-author neighborhoods | `getVertexNeighborhood` | undirected co-occurrence neighbors across all hyperedges |
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Documentation
|
|
162
|
+
|
|
163
|
+
The latest online documentation can be found [here](https://yamafaktory.github.io/hypergraphz/).
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# HypergraphZ - A Hypergraph Implementation in Zig
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
HypergraphZ is a directed hypergraph implementation in Zig (https://en.wikipedia.org/wiki/Hypergraph):
|
|
6
|
+
|
|
7
|
+
- Each hyperedge can contain zero, one (unary) or multiple vertices.
|
|
8
|
+
- Each hyperedge can contain vertices directed to themselves one or more times.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## For Python users
|
|
13
|
+
|
|
14
|
+
### Installation
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pip install hypergraphz
|
|
18
|
+
# or with uv
|
|
19
|
+
uv add hypergraphz
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Pre-built wheels are available for Linux (x86\_64, aarch64), macOS (x86\_64, arm64), and Windows (x86\_64).
|
|
23
|
+
|
|
24
|
+
### Quick start
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from hypergraphz import Hypergraph
|
|
28
|
+
|
|
29
|
+
g = Hypergraph()
|
|
30
|
+
|
|
31
|
+
# Add vertices and hyperedges
|
|
32
|
+
alice = g.add_vertex({"name": "alice", "age": 30})
|
|
33
|
+
bob = g.add_vertex({"name": "bob", "age": 25})
|
|
34
|
+
carol = g.add_vertex({"name": "carol", "age": 35})
|
|
35
|
+
|
|
36
|
+
collab = g.add_hyperedge({"type": "project"})
|
|
37
|
+
g.connect(collab, [alice, bob, carol])
|
|
38
|
+
|
|
39
|
+
# Build the reverse index before querying
|
|
40
|
+
g.build()
|
|
41
|
+
|
|
42
|
+
# Shortest path
|
|
43
|
+
path = g.shortest_path(alice, carol)
|
|
44
|
+
|
|
45
|
+
# Fluent query builders
|
|
46
|
+
active = (
|
|
47
|
+
g.vertices()
|
|
48
|
+
.where(lambda d: d["age"] > 28)
|
|
49
|
+
.data()
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Structural algorithms
|
|
53
|
+
scores, _, _ = g.page_rank()
|
|
54
|
+
centrality = g.centrality()
|
|
55
|
+
components = g.connected_components()
|
|
56
|
+
|
|
57
|
+
# Sub-graph operations return independent Hypergraph objects
|
|
58
|
+
sub = g.vertex_induced_subgraph([alice, bob])
|
|
59
|
+
dual = g.dual()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### API summary
|
|
63
|
+
|
|
64
|
+
| Category | Methods |
|
|
65
|
+
|---|---|
|
|
66
|
+
| Lifecycle | `build()`, `clear()`, `save()`, `load()`, `clone()` |
|
|
67
|
+
| Vertices | `add_vertex()`, `get_vertex()`, `update_vertex()`, `delete_vertex()`, `vertex_count()`, `all_vertex_ids()` |
|
|
68
|
+
| Hyperedges | `add_hyperedge()`, `get_hyperedge()`, `update_hyperedge()`, `delete_hyperedge()`, `hyperedge_count()`, `all_hyperedge_ids()` |
|
|
69
|
+
| Relations | `connect()`, `prepend()`, `insert_vertex()`, `insert_many()`, `disconnect()`, `disconnect_at()` |
|
|
70
|
+
| Degree | `vertex_indegree()`, `vertex_outdegree()` |
|
|
71
|
+
| Queries | `hyperedge_vertices()`, `vertex_hyperedges()`, `neighborhood()`, `intersections()`, `hyperedges_connecting()`, `endpoints()`, `orphan_vertices()`, `orphan_hyperedges()` |
|
|
72
|
+
| Boolean | `is_connected()`, `is_reachable()`, `has_cycle()`, `is_k_uniform()` |
|
|
73
|
+
| Traversal | `shortest_path()`, `all_paths()`, `bfs()`, `dfs()`, `random_walk()` |
|
|
74
|
+
| Algorithms | `connected_components()`, `topological_sort()`, `cut_vertices()`, `centrality()`, `page_rank()`, `inclusions()`, `nestedness_profile()` |
|
|
75
|
+
| Matrices | `incidence_matrix()`, `incidence_matrix_coo()`, `laplacian()` |
|
|
76
|
+
| Sub-graphs | `dual()`, `k_skeleton()`, `vertex_induced_subgraph()`, `edge_induced_subgraph()`, `core()`, `expand_to_graph()`, `expand_to_star()`, `line_graph()`, `transitive_closure()` |
|
|
77
|
+
| Fluent builders | `vertices()` → `VertexQuery`, `hyperedges()` → `HyperedgeQuery` |
|
|
78
|
+
|
|
79
|
+
### Exceptions
|
|
80
|
+
|
|
81
|
+
All errors map to typed exceptions from `hypergraphz`:
|
|
82
|
+
|
|
83
|
+
| Exception | When raised |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `NotBuiltError` | Query requires `build()` first |
|
|
86
|
+
| `VertexNotFoundError` | Vertex ID does not exist |
|
|
87
|
+
| `HyperedgeNotFoundError` | Hyperedge ID does not exist |
|
|
88
|
+
| `CycleDetectedError` | Operation requires a DAG |
|
|
89
|
+
| `NoPathError` | No directed path exists |
|
|
90
|
+
| `IndexOutOfBoundsError` | Insertion index out of range |
|
|
91
|
+
| `NotEnoughVerticesError` | Operation needs more vertices |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## For Zig users
|
|
96
|
+
|
|
97
|
+
### Zig version
|
|
98
|
+
|
|
99
|
+
HypergraphZ currently requires **Zig 0.17.0-dev.242+5d55999d2**.
|
|
100
|
+
|
|
101
|
+
### Usage
|
|
102
|
+
|
|
103
|
+
Add `hypergraphz` as a dependency to your `build.zig.zon`:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
zig fetch --save https://github.com/yamafaktory/hypergraphz/archive/v0.1.0.tar.gz
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Add `hypergraphz` as a dependency to your `build.zig`:
|
|
110
|
+
|
|
111
|
+
```zig
|
|
112
|
+
const hypergraphz = b.dependency("hypergraphz", .{
|
|
113
|
+
.target = target,
|
|
114
|
+
.optimize = optimize,
|
|
115
|
+
});
|
|
116
|
+
exe.root_module.addImport("hypergraphz", hypergraphz.module("hypergraphz"));
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Example
|
|
120
|
+
|
|
121
|
+
`examples/coauthorship.zig` models a small research community as a directed hypergraph
|
|
122
|
+
where vertices are researchers and hyperedges are papers. The first listed author is
|
|
123
|
+
treated as the corresponding author, giving each hyperedge a natural direction.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
zig build example
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
It walks through seventeen features of the library in sequence:
|
|
130
|
+
|
|
131
|
+
| Section | API | What it shows |
|
|
132
|
+
| ------------------------ | ------------------------ | ------------------------------------------------------------ |
|
|
133
|
+
| Papers per researcher | `getVertexHyperedges` | reverse-index query |
|
|
134
|
+
| Research communities | `getConnectedComponents` | two isolated groups with no cross-group papers |
|
|
135
|
+
| Betweenness centrality | `computeCentrality` | which researchers bridge the most collaboration paths |
|
|
136
|
+
| Degrees of separation | `findShortestPath` | 1–3 hops within a community; unreachable across communities |
|
|
137
|
+
| Dual: paper-centric view | `getDual` | swap vertices ↔ hyperedges to see each researcher's papers |
|
|
138
|
+
| Shared authorship | `getIntersections` | researchers present on every paper in a given set |
|
|
139
|
+
| Seniority ordering | `topologicalSort` | hierarchy implied by the authorship direction |
|
|
140
|
+
| Pairwise expansion | `expandToGraph` | 6 papers encode 11 directed pairs in a regular graph |
|
|
141
|
+
| Spectral view | `toLaplacian` | normalized Laplacian; block-diagonal under disjoint groups |
|
|
142
|
+
| PageRank | `computePageRank` | citation-weighted stationary distribution; sums to 1 |
|
|
143
|
+
| Random walk | `randomWalk` | seeded weighted walk; stays inside the start's component |
|
|
144
|
+
| Star expansion | `expandToStar` | bipartite (researcher ↔ paper) projection; 2-uniform |
|
|
145
|
+
| Line graph | `getLineGraph` | papers as vertices, linked when they share an author |
|
|
146
|
+
| K-core decomposition | `getCore` | (s, t)-core peeling; trims the lone-paper researcher |
|
|
147
|
+
| Nestedness | `getInclusions` | strict-subset pairs across hyperedges; sized profile |
|
|
148
|
+
| Co-author neighborhoods | `getVertexNeighborhood` | undirected co-occurrence neighbors across all hyperedges |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Documentation
|
|
153
|
+
|
|
154
|
+
The latest online documentation can be found [here](https://yamafaktory.github.io/hypergraphz/).
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const HypergraphZPath = "src/hypergraphz.zig";
|
|
3
|
+
const BenchPath = "bench/main.zig";
|
|
4
|
+
const TestRootPath = "tests/root.zig";
|
|
5
|
+
const CAbiPath = "src/hgz_c_api.zig";
|
|
6
|
+
|
|
7
|
+
pub fn build(b: *std.Build) void {
|
|
8
|
+
const target = b.standardTargetOptions(.{});
|
|
9
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
10
|
+
const root_source_file = b.path(HypergraphZPath);
|
|
11
|
+
|
|
12
|
+
const root_module = b.addModule("hypergraphz", .{
|
|
13
|
+
.root_source_file = root_source_file,
|
|
14
|
+
.target = target,
|
|
15
|
+
.optimize = optimize,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const test_module = b.createModule(.{
|
|
19
|
+
.root_source_file = b.path(TestRootPath),
|
|
20
|
+
.imports = &.{.{ .name = "hypergraphz", .module = root_module }},
|
|
21
|
+
.target = target,
|
|
22
|
+
.optimize = optimize,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Creates a step for unit testing. This only builds the test executable
|
|
26
|
+
// but does not run it.
|
|
27
|
+
const unit_tests = b.addTest(.{
|
|
28
|
+
.root_module = test_module,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const run_lib_unit_tests = b.addRunArtifact(unit_tests);
|
|
32
|
+
|
|
33
|
+
// Disable cache for unit tests to get logs.
|
|
34
|
+
// https://ziggit.dev/t/how-to-enable-more-logging-and-disable-caching-with-zig-build-test/2654/11
|
|
35
|
+
run_lib_unit_tests.has_side_effects = true;
|
|
36
|
+
|
|
37
|
+
// Similar to creating the run step earlier, this exposes a `test` step to
|
|
38
|
+
// the `zig build --help` menu, providing a way for the user to request
|
|
39
|
+
// running the unit tests.
|
|
40
|
+
// Notes:
|
|
41
|
+
// - `zig build test -Doptimize=ReleaseFast` to run the unit tests in release-fast mode.
|
|
42
|
+
const test_step = b.step("test", "Run unit tests");
|
|
43
|
+
test_step.dependOn(&run_lib_unit_tests.step);
|
|
44
|
+
|
|
45
|
+
// Generate docs step.
|
|
46
|
+
const docs_step = b.step("docs", "Build the HypergraphZ docs");
|
|
47
|
+
const docs_obj = b.addObject(.{
|
|
48
|
+
.name = "HypergraphZ",
|
|
49
|
+
.root_module = b.createModule(.{
|
|
50
|
+
.root_source_file = root_source_file,
|
|
51
|
+
.target = target,
|
|
52
|
+
.optimize = optimize,
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
const docs = docs_obj.getEmittedDocs();
|
|
56
|
+
docs_step.dependOn(&b.addInstallDirectory(.{
|
|
57
|
+
.source_dir = docs,
|
|
58
|
+
.install_dir = .prefix,
|
|
59
|
+
.install_subdir = "docs",
|
|
60
|
+
}).step);
|
|
61
|
+
|
|
62
|
+
// Format step.
|
|
63
|
+
const fmt_step = b.step("fmt", "Format all source files");
|
|
64
|
+
const fmt = b.addFmt(.{
|
|
65
|
+
.paths = &.{ HypergraphZPath, "bench", "build.zig", "tests", "examples" },
|
|
66
|
+
});
|
|
67
|
+
fmt_step.dependOn(&fmt.step);
|
|
68
|
+
|
|
69
|
+
// Check step used by the zls configuration.
|
|
70
|
+
const check_step = b.step("check", "Check if HypergraphZ compiles");
|
|
71
|
+
const check = b.addTest(.{
|
|
72
|
+
.root_module = test_module,
|
|
73
|
+
});
|
|
74
|
+
check_step.dependOn(&check.step);
|
|
75
|
+
const bench_check = b.addExecutable(.{
|
|
76
|
+
.name = "bench",
|
|
77
|
+
.root_module = b.createModule(.{
|
|
78
|
+
.root_source_file = b.path(BenchPath),
|
|
79
|
+
.target = target,
|
|
80
|
+
.optimize = optimize,
|
|
81
|
+
.imports = &.{.{ .name = "hypergraphz", .module = root_module }},
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
check_step.dependOn(&bench_check.step);
|
|
85
|
+
|
|
86
|
+
// Example step.
|
|
87
|
+
const example_step = b.step("example", "Run the co-authorship network example");
|
|
88
|
+
const example_exe = b.addExecutable(.{
|
|
89
|
+
.name = "coauthorship",
|
|
90
|
+
.root_module = b.createModule(.{
|
|
91
|
+
.root_source_file = b.path("examples/coauthorship.zig"),
|
|
92
|
+
.imports = &.{.{ .name = "hypergraphz", .module = root_module }},
|
|
93
|
+
.target = target,
|
|
94
|
+
.optimize = optimize,
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
example_step.dependOn(&b.addRunArtifact(example_exe).step);
|
|
98
|
+
check_step.dependOn(&example_exe.step);
|
|
99
|
+
|
|
100
|
+
// Shared library step (C ABI for Python bindings).
|
|
101
|
+
const lib_step = b.step("lib", "Build the HypergraphZ shared library (C ABI)");
|
|
102
|
+
const lib = b.addLibrary(.{
|
|
103
|
+
.name = "hypergraphz",
|
|
104
|
+
.linkage = .dynamic,
|
|
105
|
+
.root_module = b.createModule(.{
|
|
106
|
+
.root_source_file = b.path(CAbiPath),
|
|
107
|
+
.target = target,
|
|
108
|
+
.optimize = optimize,
|
|
109
|
+
.link_libc = true,
|
|
110
|
+
.imports = &.{.{ .name = "hypergraphz", .module = root_module }},
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
lib_step.dependOn(&b.addInstallArtifact(lib, .{}).step);
|
|
114
|
+
|
|
115
|
+
// Bench step.
|
|
116
|
+
const bench_source_file = b.path(BenchPath);
|
|
117
|
+
const bench_module = b.addModule("bench", .{
|
|
118
|
+
.root_source_file = bench_source_file,
|
|
119
|
+
.target = target,
|
|
120
|
+
.optimize = .ReleaseFast,
|
|
121
|
+
.imports = &.{.{ .name = "hypergraphz", .module = root_module }},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const bench_step = b.step("bench", "Run benchmarks");
|
|
125
|
+
const bench_exe = b.addExecutable(.{
|
|
126
|
+
.name = "bench",
|
|
127
|
+
.root_module = bench_module,
|
|
128
|
+
});
|
|
129
|
+
const bench_run = b.addRunArtifact(bench_exe);
|
|
130
|
+
if (b.args) |args| {
|
|
131
|
+
bench_run.addArgs(args);
|
|
132
|
+
}
|
|
133
|
+
bench_step.dependOn(&bench_run.step);
|
|
134
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Custom hatch build hook: compiles the Zig shared library during wheel packaging."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import sysconfig
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
10
|
+
|
|
11
|
+
ROOT = Path(__file__).parent
|
|
12
|
+
PKG_DIR = ROOT / "python" / "hypergraphz"
|
|
13
|
+
PATTERNS = ("libhypergraphz.so", "libhypergraphz.dylib", "hypergraphz.dll")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _wheel_tag() -> str:
|
|
17
|
+
python_tag = f"cp{sys.version_info.major}{sys.version_info.minor}"
|
|
18
|
+
plat = sysconfig.get_platform().replace("-", "_").replace(".", "_")
|
|
19
|
+
return f"{python_tag}-{python_tag}-{plat}"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CustomBuildHook(BuildHookInterface):
|
|
23
|
+
def initialize(self, version, build_data):
|
|
24
|
+
if self.target_name == "sdist":
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
# CI pre-copies the library before invoking cibuildwheel — nothing to build.
|
|
28
|
+
# Local development: build and copy if missing.
|
|
29
|
+
if not any(PKG_DIR.glob(p) for p in PATTERNS):
|
|
30
|
+
lib_dir = ROOT / "zig-out" / "lib"
|
|
31
|
+
subprocess.run(["zig", "build", "lib", "-Doptimize=ReleaseFast"], check=True, cwd=ROOT)
|
|
32
|
+
|
|
33
|
+
copied = False
|
|
34
|
+
for pattern in PATTERNS:
|
|
35
|
+
for lib in lib_dir.glob(pattern):
|
|
36
|
+
shutil.copy2(lib, PKG_DIR / lib.name)
|
|
37
|
+
copied = True
|
|
38
|
+
|
|
39
|
+
if not copied:
|
|
40
|
+
raise RuntimeError(f"No shared library found in {lib_dir}")
|
|
41
|
+
|
|
42
|
+
# Tell hatchling this is a platform wheel. Setting both keys because
|
|
43
|
+
# different hatchling versions look at one or the other.
|
|
44
|
+
build_data["pure_python"] = False
|
|
45
|
+
build_data["tag"] = _wheel_tag()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hypergraphz"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Python bindings for the HypergraphZ hypergraph library"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
|
|
13
|
+
[dependency-groups]
|
|
14
|
+
dev = [
|
|
15
|
+
"pytest>=8.0",
|
|
16
|
+
"ruff>=0.4",
|
|
17
|
+
"hatch-vcs>=0.4",
|
|
18
|
+
"cibuildwheel>=2.20",
|
|
19
|
+
"wheel>=0.45",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[tool.hatch.version]
|
|
23
|
+
source = "vcs"
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["python/hypergraphz"]
|
|
27
|
+
# Override .gitignore exclusions — hatchling respects gitignore by default
|
|
28
|
+
artifacts = [
|
|
29
|
+
"python/hypergraphz/libhypergraphz.so",
|
|
30
|
+
"python/hypergraphz/libhypergraphz.dylib",
|
|
31
|
+
"python/hypergraphz/hypergraphz.dll",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.hooks.custom]
|
|
35
|
+
path = "hatch_build.py"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.sdist]
|
|
38
|
+
include = [
|
|
39
|
+
"python/",
|
|
40
|
+
"src/",
|
|
41
|
+
"build.zig",
|
|
42
|
+
"build.zig.zon",
|
|
43
|
+
"hatch_build.py",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.cibuildwheel]
|
|
47
|
+
build = "cp311-*"
|
|
48
|
+
skip = "pp*"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
[tool.cibuildwheel.macos]
|
|
52
|
+
environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" }
|
|
53
|
+
|
|
54
|
+
[tool.cibuildwheel.windows]
|
|
55
|
+
archs = ["AMD64"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
src = ["python"]
|
|
59
|
+
line-length = 100
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
63
|
+
ignore = ["E501"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.isort]
|
|
66
|
+
known-first-party = ["hypergraphz"]
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
testpaths = ["tests-python"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""HypergraphZ — Python bindings for the HypergraphZ Zig library."""
|
|
2
|
+
|
|
3
|
+
from ._errors import (
|
|
4
|
+
CycleDetectedError,
|
|
5
|
+
HyperedgeNotFoundError,
|
|
6
|
+
HypergraphZError,
|
|
7
|
+
IndexOutOfBoundsError,
|
|
8
|
+
NoPathError,
|
|
9
|
+
NotBuiltError,
|
|
10
|
+
NotEnoughVerticesError,
|
|
11
|
+
VertexNotFoundError,
|
|
12
|
+
)
|
|
13
|
+
from ._graph import Hypergraph
|
|
14
|
+
from ._query import HyperedgeQuery, VertexQuery
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Hypergraph",
|
|
18
|
+
"HyperedgeQuery",
|
|
19
|
+
"VertexQuery",
|
|
20
|
+
"HypergraphZError",
|
|
21
|
+
"NotBuiltError",
|
|
22
|
+
"VertexNotFoundError",
|
|
23
|
+
"HyperedgeNotFoundError",
|
|
24
|
+
"CycleDetectedError",
|
|
25
|
+
"NoPathError",
|
|
26
|
+
"IndexOutOfBoundsError",
|
|
27
|
+
"NotEnoughVerticesError",
|
|
28
|
+
]
|