regfans 0.0.1__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.
- regfans/__init__.py +4 -0
- regfans/circuits.py +371 -0
- regfans/fan.py +1816 -0
- regfans/util.py +437 -0
- regfans/vectorconfig.py +1219 -0
- regfans-0.0.1.dist-info/METADATA +36 -0
- regfans-0.0.1.dist-info/RECORD +10 -0
- regfans-0.0.1.dist-info/WHEEL +5 -0
- regfans-0.0.1.dist-info/licenses/LICENSE +674 -0
- regfans-0.0.1.dist-info/top_level.txt +1 -0
regfans/__init__.py
ADDED
regfans/circuits.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Copyright (C) 2025 Nate MacFadden for the Liam McAllister Group
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
# =============================================================================
|
|
17
|
+
#
|
|
18
|
+
# -----------------------------------------------------------------------------
|
|
19
|
+
# Description: This module contains a class containing information about
|
|
20
|
+
# circuits
|
|
21
|
+
# -----------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
# external imports
|
|
24
|
+
from collections.abc import Iterable
|
|
25
|
+
from typing import Union
|
|
26
|
+
|
|
27
|
+
class Circuit():
|
|
28
|
+
"""
|
|
29
|
+
This class is a helper data structure to contain a single circuit of some
|
|
30
|
+
vector configuration.
|
|
31
|
+
|
|
32
|
+
**Description:**
|
|
33
|
+
Constructs a `Circuit` object describing a circuit of a vector
|
|
34
|
+
configuration. This is handled by the hidden [`__init__`](#__init__)
|
|
35
|
+
function.
|
|
36
|
+
|
|
37
|
+
**Arguments:**
|
|
38
|
+
- `vc`: The ambient vector configuration.
|
|
39
|
+
- `Z`: The support of the circuit.
|
|
40
|
+
- `Zpos`: The 'positive' side of the circuit.
|
|
41
|
+
- `Zpos`: The 'negative' side of the circuit.
|
|
42
|
+
- `lmbda`: A dependency vector demonstrating the circuit.
|
|
43
|
+
- `signature`: The signature (|Zpos|, |Zneg|) of the circuit.
|
|
44
|
+
|
|
45
|
+
**Returns:**
|
|
46
|
+
Nothing.
|
|
47
|
+
"""
|
|
48
|
+
def __init__(self, vc, Z, Zpos, Zneg, lmbda, signature):
|
|
49
|
+
"""
|
|
50
|
+
**Description:**
|
|
51
|
+
Initializes a `Circuit` object.
|
|
52
|
+
|
|
53
|
+
**Arguments:**
|
|
54
|
+
- `vc`: The ambient vector configuration.
|
|
55
|
+
- `Z`: The support of the circuit.
|
|
56
|
+
- `Zpos`: The 'positive' side of the circuit.
|
|
57
|
+
- `Zpos`: The 'negative' side of the circuit.
|
|
58
|
+
- `lmbda`: A dependency vector demonstrating the circuit.
|
|
59
|
+
- `signature`: The signature (|Zpos|, |Zneg|) of the circuit.
|
|
60
|
+
|
|
61
|
+
**Returns:**
|
|
62
|
+
Nothing.
|
|
63
|
+
"""
|
|
64
|
+
self.vc = vc
|
|
65
|
+
self.Z = Z
|
|
66
|
+
self.Zpos = Zpos
|
|
67
|
+
self.Zneg = Zneg
|
|
68
|
+
self.lmbda = lmbda
|
|
69
|
+
self.signature = tuple(signature)
|
|
70
|
+
|
|
71
|
+
# positive/negative triangulations
|
|
72
|
+
self.Tpos = []
|
|
73
|
+
self.Tneg = []
|
|
74
|
+
|
|
75
|
+
self.normal = [0] * self.vc.size
|
|
76
|
+
for i,j in enumerate([self.vc.label_to_ind(z) for z in self.Z]):
|
|
77
|
+
self.normal[j] = self.lmbda[i]
|
|
78
|
+
self.normal = tuple(self.normal)
|
|
79
|
+
|
|
80
|
+
def __repr__(self):
|
|
81
|
+
out = f"A circuit with (Z+,Z-)= ({self.Zpos}, {self.Zneg})"
|
|
82
|
+
out += f"; lambda = {self.lmbda}"
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def data(self):
|
|
87
|
+
# spoof dictionaries
|
|
88
|
+
return {
|
|
89
|
+
'Z':self.Z,
|
|
90
|
+
'Z+':self.Zpos,
|
|
91
|
+
'Z-':self.Zneg,
|
|
92
|
+
'lambda':self.lmbda,
|
|
93
|
+
'type':self.signature,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
def reorient(self):
|
|
97
|
+
# allow swapping Zpos <-> Zneg
|
|
98
|
+
reoriented = Circuit(
|
|
99
|
+
self.vc,
|
|
100
|
+
self.Z,
|
|
101
|
+
self.Zneg,
|
|
102
|
+
self.Zpos,
|
|
103
|
+
tuple([-x for x in self.lmbda]),
|
|
104
|
+
(self.signature[1], self.signature[0])
|
|
105
|
+
)
|
|
106
|
+
return reoriented
|
|
107
|
+
|
|
108
|
+
class Circuits():
|
|
109
|
+
"""
|
|
110
|
+
This class is a helper data structure to contain the circuits of some
|
|
111
|
+
vector configuration.
|
|
112
|
+
|
|
113
|
+
**Description:**
|
|
114
|
+
Constructs a `Circuits` object describing all circuits of some VC. This
|
|
115
|
+
is handled by the hidden [`__init__`](#__init__) function.
|
|
116
|
+
|
|
117
|
+
**Arguments:**
|
|
118
|
+
None.
|
|
119
|
+
|
|
120
|
+
**Returns:**
|
|
121
|
+
Nothing.
|
|
122
|
+
"""
|
|
123
|
+
def __init__(self):
|
|
124
|
+
"""
|
|
125
|
+
**Description:**
|
|
126
|
+
Initializes a `Circuits` object.
|
|
127
|
+
|
|
128
|
+
**Arguments:**
|
|
129
|
+
None.
|
|
130
|
+
|
|
131
|
+
**Returns:**
|
|
132
|
+
Nothing.
|
|
133
|
+
"""
|
|
134
|
+
self.clear_cache() # set attributes here
|
|
135
|
+
|
|
136
|
+
# clear cache
|
|
137
|
+
# -----------
|
|
138
|
+
def clear_cache(self):
|
|
139
|
+
# main data type - map from the (encoded) unsigned circuit to a Circuit
|
|
140
|
+
# object
|
|
141
|
+
# **Don't directly access... use getters/setters instead...***
|
|
142
|
+
self.circuits = dict()
|
|
143
|
+
|
|
144
|
+
# map from cone to the circuits it is involved in
|
|
145
|
+
self.cone_to_circuit = dict()
|
|
146
|
+
|
|
147
|
+
# non-dependencies
|
|
148
|
+
self.non_dependencies = set()
|
|
149
|
+
|
|
150
|
+
# whether we have computed/saved all circuits
|
|
151
|
+
self.know_all_circuits = False
|
|
152
|
+
|
|
153
|
+
# default methods
|
|
154
|
+
# ---------------
|
|
155
|
+
def __repr__(self):
|
|
156
|
+
# hijack `dict` and `Circuit`
|
|
157
|
+
return self.circuits.__repr__()
|
|
158
|
+
|
|
159
|
+
def __str__(self):
|
|
160
|
+
# hijack `dict` and `Circuit`
|
|
161
|
+
return self.circuits.__str__()
|
|
162
|
+
|
|
163
|
+
def __len__(self):
|
|
164
|
+
# hijack `dict`
|
|
165
|
+
return len(self.circuits)
|
|
166
|
+
|
|
167
|
+
def __contains__(self, label_inds: Iterable[int]):
|
|
168
|
+
encoding = self.encode(label_inds)
|
|
169
|
+
return encoding in self.circuits
|
|
170
|
+
|
|
171
|
+
def __getitem__(self, label_inds: Iterable[int]) -> Union["Circuit", int]:
|
|
172
|
+
"""
|
|
173
|
+
**Description:**
|
|
174
|
+
Get the circuit corresponding to the indicated indices.
|
|
175
|
+
|
|
176
|
+
**Arguments:**
|
|
177
|
+
- `label_inds`: The iterable of vector/label indices.
|
|
178
|
+
|
|
179
|
+
**Returns:**
|
|
180
|
+
Cases
|
|
181
|
+
- if indices correspond to known circuit -> the `Circuit`
|
|
182
|
+
- if indices correspond to non-circuit -> -1
|
|
183
|
+
- if indices aren't known -> 0
|
|
184
|
+
"""
|
|
185
|
+
encoding = self.encode(label_inds)
|
|
186
|
+
|
|
187
|
+
# check if these labels are known to contain a circuit
|
|
188
|
+
if encoding in self.circuits:
|
|
189
|
+
return self.circuits[encoding]
|
|
190
|
+
|
|
191
|
+
# if we know all circuits, then indices cannot correspond to a circuit
|
|
192
|
+
if self.know_all_circuits:
|
|
193
|
+
return -1
|
|
194
|
+
|
|
195
|
+
# if we don't know all circuits, we check against known non-circuits
|
|
196
|
+
for non_dependency in self.non_dependencies:
|
|
197
|
+
if self.is_subset(encoding, non_dependency):
|
|
198
|
+
return -1
|
|
199
|
+
|
|
200
|
+
# unclear if this is a circuit
|
|
201
|
+
return 0
|
|
202
|
+
|
|
203
|
+
def set_circuit(self,
|
|
204
|
+
circuit: "Circuit",
|
|
205
|
+
verbosity: int = 0) -> None:
|
|
206
|
+
"""
|
|
207
|
+
**Description:**
|
|
208
|
+
Set the circuit properties corresponding to the indicated indices.
|
|
209
|
+
|
|
210
|
+
**Arguments:**
|
|
211
|
+
- `circuit`: Dict describing the circuit.
|
|
212
|
+
- `verbosity`: The verbosity level.
|
|
213
|
+
|
|
214
|
+
**Returns:**
|
|
215
|
+
Nothing.
|
|
216
|
+
"""
|
|
217
|
+
encoding = self.encode(circuit.Z)
|
|
218
|
+
|
|
219
|
+
# setting a circuit
|
|
220
|
+
self.circuits[encoding] = circuit
|
|
221
|
+
|
|
222
|
+
# keep a map from cones to the circuits they have
|
|
223
|
+
for c in circuit.Tpos:
|
|
224
|
+
self.cone_to_circuit[c] = self.cone_to_circuit.get(c,set())
|
|
225
|
+
self.cone_to_circuit[c].add(encoding)
|
|
226
|
+
|
|
227
|
+
def set_non_dependency(self,
|
|
228
|
+
label_inds: Iterable[int],
|
|
229
|
+
verbosity: int = 0) -> None:
|
|
230
|
+
"""
|
|
231
|
+
**Description:**
|
|
232
|
+
Record a set of points that is not dependent
|
|
233
|
+
|
|
234
|
+
**Arguments:**
|
|
235
|
+
- `label_inds`: The iterable of vector/label indices.
|
|
236
|
+
- `verbosity`: The verbosity level.
|
|
237
|
+
|
|
238
|
+
**Returns:**
|
|
239
|
+
Nothing.
|
|
240
|
+
"""
|
|
241
|
+
encoding = self.encode(label_inds)
|
|
242
|
+
|
|
243
|
+
new_non_dependencies= set()
|
|
244
|
+
for non_dependency in self.non_dependencies:
|
|
245
|
+
if self.is_subset(non_dependency, encoding):
|
|
246
|
+
# non_dependency is weaker than encoding...
|
|
247
|
+
# don't save it in our new list
|
|
248
|
+
if verbosity >= 1:
|
|
249
|
+
print(f"Outdated non-dependency = {non_dependency}...")
|
|
250
|
+
|
|
251
|
+
pass
|
|
252
|
+
else:
|
|
253
|
+
# not a subset of encoding -> not trivial
|
|
254
|
+
new_non_dependencies.add(non_dependency)
|
|
255
|
+
|
|
256
|
+
new_non_dependencies.add(encoding)
|
|
257
|
+
self.non_dependencies = new_non_dependencies
|
|
258
|
+
|
|
259
|
+
# dictionary methods
|
|
260
|
+
# ------------------
|
|
261
|
+
def values(self) -> Iterable["Circuit"]:
|
|
262
|
+
"""
|
|
263
|
+
**Description:**
|
|
264
|
+
Get the values (the actual circuits)
|
|
265
|
+
|
|
266
|
+
**Arguments:**
|
|
267
|
+
None
|
|
268
|
+
|
|
269
|
+
**Returns:**
|
|
270
|
+
The circuits.
|
|
271
|
+
"""
|
|
272
|
+
return self.circuits.values()
|
|
273
|
+
|
|
274
|
+
def copy(self) -> "Circuits":
|
|
275
|
+
"""
|
|
276
|
+
**Description:**
|
|
277
|
+
Copy the circuits object
|
|
278
|
+
|
|
279
|
+
**Arguments:**
|
|
280
|
+
None
|
|
281
|
+
|
|
282
|
+
**Returns:**
|
|
283
|
+
A copy of the circuits.
|
|
284
|
+
"""
|
|
285
|
+
copied = Circuits()
|
|
286
|
+
copied.circuits = {Z:circ for Z,circ in self.circuits.items()}
|
|
287
|
+
copied.cone_to_circuit = {c:Zs.copy() for c,Zs in \
|
|
288
|
+
self.cone_to_circuit.items()}
|
|
289
|
+
copied.non_dependencies = self.non_dependencies.copy()
|
|
290
|
+
copied.know_all_circuits = self.know_all_circuits
|
|
291
|
+
|
|
292
|
+
return copied
|
|
293
|
+
|
|
294
|
+
def pop(self, *args, **kwargs):
|
|
295
|
+
"""
|
|
296
|
+
Pop an element from the circuits dict
|
|
297
|
+
"""
|
|
298
|
+
out = self.circuits.pop(*args, **kwargs)
|
|
299
|
+
|
|
300
|
+
# basic bit helpers
|
|
301
|
+
# -----------------
|
|
302
|
+
def encode(self, label_inds: Iterable[int]) -> int:
|
|
303
|
+
"""
|
|
304
|
+
**Description:**
|
|
305
|
+
Convert an iterable of integers to a binary vector, b, such that
|
|
306
|
+
b_i = 1 <=> i in label_inds
|
|
307
|
+
|
|
308
|
+
**Arguments:**
|
|
309
|
+
- `label_inds`: The iterable of integers.
|
|
310
|
+
|
|
311
|
+
**Returns:**
|
|
312
|
+
The encoding
|
|
313
|
+
"""
|
|
314
|
+
# as bitvector
|
|
315
|
+
if isinstance(label_inds, int):
|
|
316
|
+
return label_inds
|
|
317
|
+
|
|
318
|
+
encoding = 0
|
|
319
|
+
for label_ind in label_inds:
|
|
320
|
+
encoding |= (1 << int(label_ind))
|
|
321
|
+
return encoding
|
|
322
|
+
|
|
323
|
+
def decode(self, encoding) -> list[int]:
|
|
324
|
+
"""
|
|
325
|
+
**Description:**
|
|
326
|
+
Convert a binary vector b to a list of of integers such that
|
|
327
|
+
b_i = 1 <=> i in label_inds
|
|
328
|
+
|
|
329
|
+
**Arguments:**
|
|
330
|
+
- `encoding`: The encoding to map to label indices
|
|
331
|
+
|
|
332
|
+
**Returns:**
|
|
333
|
+
The label indices
|
|
334
|
+
"""
|
|
335
|
+
# as bitvector
|
|
336
|
+
label_inds = []
|
|
337
|
+
|
|
338
|
+
for shift in range(len(bin(encoding))-2):
|
|
339
|
+
if 1&(encoding>>shift):
|
|
340
|
+
label_inds.append(shift)
|
|
341
|
+
|
|
342
|
+
return label_inds
|
|
343
|
+
|
|
344
|
+
def is_superset(self, setA, setB) -> bool:
|
|
345
|
+
"""
|
|
346
|
+
**Description:**
|
|
347
|
+
Check if the set encoded by setA is a superset of setB.
|
|
348
|
+
|
|
349
|
+
**Arguments:**
|
|
350
|
+
- `setA`: The candidate-superset encoding.
|
|
351
|
+
- `setB`: The candidate-subset encoding.
|
|
352
|
+
|
|
353
|
+
**Returns:**
|
|
354
|
+
Whether setA is a superset of setB.
|
|
355
|
+
"""
|
|
356
|
+
# as bitvector
|
|
357
|
+
return (setA & setB) == setB
|
|
358
|
+
|
|
359
|
+
def is_subset(self, setA: int, setB: int) -> bool:
|
|
360
|
+
"""
|
|
361
|
+
**Description:**
|
|
362
|
+
Check if the set encoded by setA is a subset of setB.
|
|
363
|
+
|
|
364
|
+
**Arguments:**
|
|
365
|
+
- `setA`: The candidate-superset encoding.
|
|
366
|
+
- `setB`: The candidate-subset encoding.
|
|
367
|
+
|
|
368
|
+
**Returns:**
|
|
369
|
+
Whether setA is a subset of setB.
|
|
370
|
+
"""
|
|
371
|
+
return self.is_superset(setB, setA)
|