arx-net 0.1.0__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.
arx_net-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dwarakesh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
arx_net-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: arx-net
3
+ Version: 0.1.0
4
+ Summary: Graph parsing for arx net (https://dwarakesh-v.github.io/arx-net/) and graph representation conversion library.
5
+ License: MIT
6
+ Keywords: arx-net,graph,network,adjacency,graph-parser
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ # Arx-Net
16
+
17
+ A lightweight Python library for parsing graph edge strings and converting
18
+ between common graph representations.
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install arx-net
24
+ ```
25
+
26
+ ## Features
27
+
28
+ - Parse edge strings
29
+ - Directed and undirected graphs
30
+ - Weighted and unweighted graphs
31
+ - Convert between
32
+
33
+ - adjacency lists
34
+ - edge lists
35
+ - adjacency matrices
36
+
37
+ ## Example
38
+
39
+ ```python
40
+ import arx_net
41
+
42
+ graph = arx_net.parse_edges(
43
+ "(A,B,4),(B,C,2),(C,D)"
44
+ )
45
+
46
+ print(graph)
47
+ ```
48
+
49
+ Output
50
+
51
+ ```python
52
+ {
53
+ "A": [("B",4)],
54
+ "B": [("C",2)],
55
+ "C": [("D",1)],
56
+ "D": []
57
+ }
58
+ ```
59
+
60
+ Convert formats
61
+
62
+ ```python
63
+ edges = arx_net.convert_type(graph, "edge")
64
+ matrix, order = arx_net.convert_type(graph, "matrix")
65
+ ```
66
+
67
+ ## Supported formats
68
+
69
+ ### Adjacency List
70
+
71
+ ```python
72
+ {
73
+ "A": ["B", "C"]
74
+ }
75
+ ```
76
+
77
+ ### Edge List
78
+
79
+ ```python
80
+ [
81
+ ("A","B"),
82
+ ("A","C")
83
+ ]
84
+ ```
85
+
86
+ ### Matrix
87
+
88
+ ```python
89
+ [
90
+ [0,1,1],
91
+ [0,0,1],
92
+ [0,0,0]
93
+ ]
94
+ ```
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,84 @@
1
+ # Arx-Net
2
+
3
+ A lightweight Python library for parsing graph edge strings and converting
4
+ between common graph representations.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ pip install arx-net
10
+ ```
11
+
12
+ ## Features
13
+
14
+ - Parse edge strings
15
+ - Directed and undirected graphs
16
+ - Weighted and unweighted graphs
17
+ - Convert between
18
+
19
+ - adjacency lists
20
+ - edge lists
21
+ - adjacency matrices
22
+
23
+ ## Example
24
+
25
+ ```python
26
+ import arx_net
27
+
28
+ graph = arx_net.parse_edges(
29
+ "(A,B,4),(B,C,2),(C,D)"
30
+ )
31
+
32
+ print(graph)
33
+ ```
34
+
35
+ Output
36
+
37
+ ```python
38
+ {
39
+ "A": [("B",4)],
40
+ "B": [("C",2)],
41
+ "C": [("D",1)],
42
+ "D": []
43
+ }
44
+ ```
45
+
46
+ Convert formats
47
+
48
+ ```python
49
+ edges = arx_net.convert_type(graph, "edge")
50
+ matrix, order = arx_net.convert_type(graph, "matrix")
51
+ ```
52
+
53
+ ## Supported formats
54
+
55
+ ### Adjacency List
56
+
57
+ ```python
58
+ {
59
+ "A": ["B", "C"]
60
+ }
61
+ ```
62
+
63
+ ### Edge List
64
+
65
+ ```python
66
+ [
67
+ ("A","B"),
68
+ ("A","C")
69
+ ]
70
+ ```
71
+
72
+ ### Matrix
73
+
74
+ ```python
75
+ [
76
+ [0,1,1],
77
+ [0,0,1],
78
+ [0,0,0]
79
+ ]
80
+ ```
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "arx-net"
7
+ version = "0.1.0"
8
+ description = "Graph parsing for arx net (https://dwarakesh-v.github.io/arx-net/) and graph representation conversion library."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = {text = "MIT"}
12
+
13
+ keywords = [
14
+ "arx-net",
15
+ "graph",
16
+ "network",
17
+ "adjacency",
18
+ "graph-parser"
19
+ ]
20
+
21
+ classifiers = [
22
+ "Programming Language :: Python :: 3",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ ]
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,9 @@
1
+ from .parser import parse_edges
2
+ from .convert import convert_type
3
+
4
+ __version__ = "0.1.0"
5
+
6
+ __all__ = [
7
+ "parse_edges",
8
+ "convert_type",
9
+ ]
@@ -0,0 +1,197 @@
1
+ from warnings import warn
2
+
3
+ # Type conversion
4
+ # Parse to canonical edges
5
+ def _parse_adj(adj):
6
+ nodes = set(adj.keys())
7
+ edges = []
8
+ for src, neighbors in adj.items():
9
+ for nb in neighbors:
10
+ if isinstance(nb, (tuple, list)) and len(nb) == 2:
11
+ tgt, w = nb
12
+ else:
13
+ tgt, w = nb, None
14
+ nodes.add(tgt)
15
+ edges.append((src, tgt, w))
16
+ return edges, nodes
17
+
18
+ def _parse_edge_list(edge_list):
19
+ nodes = set()
20
+ edges = []
21
+ for e in edge_list:
22
+ if isinstance(e, dict):
23
+ src = e.get('source', e.get('src'))
24
+ tgt = e.get('target', e.get('tgt'))
25
+ w = e.get('weight', e.get('w'))
26
+ elif len(e) == 3:
27
+ src, tgt, w = e
28
+ elif len(e) == 2:
29
+ src, tgt, w = *e, None
30
+ else:
31
+ warn(f"Skipping malformed edge: {e}")
32
+ continue
33
+ if src is None or tgt is None:
34
+ warn(f"Skipping edge with missing src/tgt: {e}")
35
+ continue
36
+ nodes.update([src, tgt])
37
+ edges.append((src, tgt, w))
38
+ return edges, nodes
39
+
40
+ def _parse_matrix(matrix):
41
+ edges = []
42
+ if isinstance(matrix, dict):
43
+ nodes = set(matrix.keys())
44
+ for src, row in matrix.items():
45
+ for tgt, val in row.items():
46
+ nodes.add(tgt)
47
+ if val != 0:
48
+ edges.append((src, tgt, val))
49
+ else:
50
+ n = len(matrix)
51
+ nodes = set(range(n))
52
+ for i, row in enumerate(matrix):
53
+ for j, val in enumerate(row):
54
+ if val != 0:
55
+ edges.append((i, j, val))
56
+ return edges, nodes
57
+
58
+ def convert_type(input_graph, to, directed=None, weighted=None, from_type=None):
59
+ """
60
+ Convert between graph representations seamlessly.
61
+
62
+ Args:
63
+ input_graph : Input graph in any supported format -
64
+ Adjacency list : { node: [neighbor, ...] } or { node: [(neighbor, weight), ...] }
65
+ Edge list : [(src, tgt), ...] or [(src, tgt, weight), ...]
66
+ or list of dicts with 'source'/'target'/'weight' keys (parse_edges output)
67
+ Matrix : 2D list (nodes = row/col indices)
68
+ or dict-of-dicts { src: { tgt: weight } }
69
+ to : Target format - 'adj', 'edge', or 'matrix'
70
+ directed : bool. Auto-detected from input symmetry if None.
71
+ weighted : bool. Auto-detected from input structure if None.
72
+ from_type : Input format - 'adj', 'edge', or 'matrix'. Auto-detected if None.
73
+ ('from' is a Python keyword, so from_type is used instead.)
74
+
75
+ Returns:
76
+ 'adj'
77
+ {node: [neighbor, ...]} or
78
+ {node: [(neighbor, weight), ...]}
79
+
80
+ 'edge'
81
+ [(src, tgt), ...] or
82
+ [(src, tgt, weight), ...]
83
+
84
+ 'matrix'
85
+ (2D list, node_order)
86
+ node_order[i] gives the label for row/column i.
87
+
88
+ Notes:
89
+ - For binary (0/1) matrices, weighted is inferred as False since 1 = "edge exists".
90
+ If your graph genuinely has weight=1 on all edges, pass weighted=True explicitly.
91
+ - Directed detection is a heuristic: if every edge (u,v) has a reverse (v,u),
92
+ the graph is assumed undirected. Pass directed=True/False to override.
93
+ """
94
+
95
+ FORMAT_ALIASES = {
96
+ 'adj': 'adj', 'adjacency_list': 'adj', 'adjacency': 'adj',
97
+ 'edge': 'edge', 'edge_list': 'edge', 'edges': 'edge',
98
+ 'matrix': 'matrix', 'adjacency_matrix': 'matrix', 'mat': 'matrix',
99
+ }
100
+
101
+ to = FORMAT_ALIASES.get(to, to)
102
+ if to not in ('adj', 'edge', 'matrix'):
103
+ raise ValueError(f"Unknown target format '{to}'. Use 'adj', 'edge', or 'matrix'.")
104
+
105
+ if from_type is not None:
106
+ from_type = FORMAT_ALIASES.get(from_type, from_type)
107
+
108
+ # Auto-detect input format
109
+ def detect_format(g):
110
+ if isinstance(g, dict):
111
+ if not g:
112
+ return 'adj'
113
+ first_val = next(iter(g.values()))
114
+ return 'matrix' if isinstance(first_val, dict) else 'adj'
115
+ if isinstance(g, list):
116
+ if not g:
117
+ return 'edge'
118
+ return 'matrix' if isinstance(g[0], list) else 'edge'
119
+ raise ValueError("Cannot auto-detect format. Specify `from_type` explicitly.")
120
+
121
+ fmt = from_type if from_type is not None else detect_format(input_graph)
122
+ if fmt not in ('adj', 'edge', 'matrix'):
123
+ raise ValueError(f"Unknown input format '{fmt}'.")
124
+
125
+ parsers = {'adj': _parse_adj, 'edge': _parse_edge_list, 'matrix': _parse_matrix}
126
+ edges, nodes = parsers[fmt](input_graph)
127
+
128
+ # Auto-detect weighted
129
+ if weighted is None:
130
+ if fmt == 'matrix':
131
+ # Binary 0/1 matrix → unweighted; any other value → weighted
132
+ weighted = any(w not in (0, 1) for _, _, w in edges)
133
+ else:
134
+ weighted = any(w is not None for _, _, w in edges)
135
+
136
+ # Auto-detect directed
137
+ if directed is None:
138
+ if fmt == 'matrix' and isinstance(input_graph, list):
139
+ n = len(input_graph)
140
+ directed = any(
141
+ input_graph[i][j] != input_graph[j][i]
142
+ for i in range(n) for j in range(i, n)
143
+ )
144
+ else:
145
+ edge_set = {(s, t) for s, t, _ in edges}
146
+ directed = not all(
147
+ (t, s) in edge_set
148
+ for s, t, _ in edges if s != t
149
+ )
150
+
151
+ # Normalize
152
+ if weighted:
153
+ edges = [(s, t, w if w is not None else 1) for s, t, w in edges]
154
+ else:
155
+ edges = [(s, t, None) for s, t, _ in edges]
156
+
157
+ if not directed:
158
+ seen = set()
159
+ deduped = []
160
+ for s, t, w in edges:
161
+ key = frozenset([s, t])
162
+ if key not in seen:
163
+ seen.add(key)
164
+ deduped.append((s, t, w))
165
+ edges = deduped
166
+
167
+ # Build target format
168
+
169
+ def build_adj(edges, nodes, directed, weighted):
170
+ adj = {n: [] for n in nodes}
171
+ for s, t, w in edges:
172
+ adj.setdefault(s, []).append((t, w) if weighted else t)
173
+ if not directed:
174
+ adj.setdefault(t, []).append((s, w) if weighted else s)
175
+ return adj
176
+
177
+ def build_edge_list(edges, weighted):
178
+ return [(s, t, w) if weighted else (s, t) for s, t, w in edges]
179
+
180
+ def build_matrix(edges, nodes, directed, weighted):
181
+ node_order = sorted(nodes, key=str)
182
+ idx = {n: i for i, n in enumerate(node_order)}
183
+ size = len(node_order)
184
+ matrix = [[0] * size for _ in range(size)]
185
+ for s, t, w in edges:
186
+ val = w if weighted else 1
187
+ matrix[idx[s]][idx[t]] = val
188
+ if not directed:
189
+ matrix[idx[t]][idx[s]] = val
190
+ return matrix, node_order
191
+
192
+ builders = {
193
+ 'adj': lambda: build_adj(edges, nodes, directed, weighted),
194
+ 'edge': lambda: build_edge_list(edges, weighted),
195
+ 'matrix': lambda: build_matrix(edges, nodes, directed, weighted),
196
+ }
197
+ return builders[to]()
@@ -0,0 +1,100 @@
1
+ import re
2
+
3
+ # Parse Arx-Net style edges
4
+ def _split_top_level(input_str):
5
+ result = []
6
+ depth = 0
7
+ current = ''
8
+ for char in input_str:
9
+ if char == '(':
10
+ depth += 1
11
+ if char == ')':
12
+ depth -= 1
13
+ if char == ',' and depth == 0:
14
+ result.append(current)
15
+ current = ''
16
+ else:
17
+ current += char
18
+ result.append(current)
19
+ return [s for s in result if s.strip() != '']
20
+
21
+ def parse_edges(edges_input, directed=True, strict=False):
22
+ """
23
+ Parse the arx net application format into a python compatible adjacency list format.
24
+
25
+ Args:
26
+ edges_input : Arx-Tet type input edge format
27
+ directed : Directed or Undirected
28
+ strict : Continue or terminate for invalid cases
29
+
30
+ Returns:
31
+ Python compatible adjacency list of type dict.
32
+ """
33
+ simple_format = re.compile(r'^([a-zA-Z0-9]{2})(-?\d*\.?\d*)$')
34
+ paren_format = re.compile(r'^\(\s*([a-zA-Z0-9]+)\s*,\s*([a-zA-Z0-9]+)\s*(?:,\s*(-?\d*\.?\d*)\s*)?\)$')
35
+
36
+ edges_raw = []
37
+ for edge in _split_top_level(edges_input):
38
+ edge = edge.strip()
39
+
40
+ source = target = weight = None
41
+
42
+ if m := simple_format.match(edge):
43
+ source = m.group(1)[0]
44
+ target = m.group(1)[1]
45
+ w = m.group(2)
46
+ weight = float(w) if w else None
47
+ elif m := paren_format.match(edge):
48
+ source = m.group(1)
49
+ target = m.group(2)
50
+ w = m.group(3)
51
+ weight = float(w) if w else None
52
+ elif len(edge) == 1:
53
+ source = edge
54
+ target = None
55
+ weight = None
56
+ else:
57
+ if strict:
58
+ raise ValueError(f"Invalid edge format: {edge}")
59
+ continue
60
+
61
+ if weight is None or (isinstance(weight, float) and weight != weight): # NaN check
62
+ weight = None # keep as None rather than defaulting to 1, so we know if weights are absent
63
+
64
+ edges_raw.append({'source': source, 'target': target, 'weight': weight})
65
+
66
+ # Remove duplicates, keeping last occurrence
67
+ edge_map = {}
68
+ for edge in edges_raw:
69
+ if edge['source'] and edge['target']:
70
+ key = f"{edge['source']}_{edge['target']}"
71
+ edge_map[key] = edge
72
+ if not directed:
73
+ reverse_key = f"{edge['target']}_{edge['source']}"
74
+ if reverse_key in edge_map:
75
+ del edge_map[reverse_key]
76
+
77
+ edges_raw = list(edge_map.values())
78
+
79
+ has_weights = any(e['weight'] is not None for e in edges_raw)
80
+
81
+ adj = {}
82
+ for edge in edges_raw:
83
+ src, tgt, w = edge['source'], edge['target'], edge['weight']
84
+ if src is None:
85
+ continue
86
+
87
+ for node in ([src, tgt] if tgt else [src]):
88
+ if node not in adj:
89
+ adj[node] = []
90
+
91
+ if tgt is not None:
92
+ neighbor = (tgt, w if w is not None else 1) if has_weights else tgt
93
+ adj[src].append(neighbor)
94
+
95
+ if not directed:
96
+ neighbor_rev = (src, w if w is not None else 1) if has_weights else src
97
+ adj[tgt].append(neighbor_rev)
98
+
99
+ return adj
100
+
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: arx-net
3
+ Version: 0.1.0
4
+ Summary: Graph parsing for arx net (https://dwarakesh-v.github.io/arx-net/) and graph representation conversion library.
5
+ License: MIT
6
+ Keywords: arx-net,graph,network,adjacency,graph-parser
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ # Arx-Net
16
+
17
+ A lightweight Python library for parsing graph edge strings and converting
18
+ between common graph representations.
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install arx-net
24
+ ```
25
+
26
+ ## Features
27
+
28
+ - Parse edge strings
29
+ - Directed and undirected graphs
30
+ - Weighted and unweighted graphs
31
+ - Convert between
32
+
33
+ - adjacency lists
34
+ - edge lists
35
+ - adjacency matrices
36
+
37
+ ## Example
38
+
39
+ ```python
40
+ import arx_net
41
+
42
+ graph = arx_net.parse_edges(
43
+ "(A,B,4),(B,C,2),(C,D)"
44
+ )
45
+
46
+ print(graph)
47
+ ```
48
+
49
+ Output
50
+
51
+ ```python
52
+ {
53
+ "A": [("B",4)],
54
+ "B": [("C",2)],
55
+ "C": [("D",1)],
56
+ "D": []
57
+ }
58
+ ```
59
+
60
+ Convert formats
61
+
62
+ ```python
63
+ edges = arx_net.convert_type(graph, "edge")
64
+ matrix, order = arx_net.convert_type(graph, "matrix")
65
+ ```
66
+
67
+ ## Supported formats
68
+
69
+ ### Adjacency List
70
+
71
+ ```python
72
+ {
73
+ "A": ["B", "C"]
74
+ }
75
+ ```
76
+
77
+ ### Edge List
78
+
79
+ ```python
80
+ [
81
+ ("A","B"),
82
+ ("A","C")
83
+ ]
84
+ ```
85
+
86
+ ### Matrix
87
+
88
+ ```python
89
+ [
90
+ [0,1,1],
91
+ [0,0,1],
92
+ [0,0,0]
93
+ ]
94
+ ```
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/arx_net/__init__.py
5
+ src/arx_net/convert.py
6
+ src/arx_net/parser.py
7
+ src/arx_net.egg-info/PKG-INFO
8
+ src/arx_net.egg-info/SOURCES.txt
9
+ src/arx_net.egg-info/dependency_links.txt
10
+ src/arx_net.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ arx_net