sage_pgr 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.
sage_pgr-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 turnip314
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.
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: sage_pgr
3
+ Version: 0.1.0
4
+ Summary: Parametric Geometric Resolutions
5
+ Author-email: Andrew Luo <j92luo@uwaterloo.ca>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 turnip314
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: repository, https://github.com/turnip314/sage_pgr
29
+ Requires-Python: >=3.9
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Dynamic: license-file
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "sage_pgr"
3
+ version = "0.1.0"
4
+ description = "Parametric Geometric Resolutions"
5
+ authors = [
6
+ { name="Andrew Luo", email="j92luo@uwaterloo.ca" }
7
+ ]
8
+ readme = "README.md"
9
+ license = { file = "LICENSE" }
10
+ requires-python = ">=3.9"
11
+ dependencies = []
12
+
13
+ [project.urls]
14
+ repository = "https://github.com/turnip314/sage_pgr"
15
+
16
+ [build-system]
17
+ requires = ["setuptools>=61.0"]
18
+ build-backend = "setuptools.build_meta"
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,450 @@
1
+ from sage.all import QQ, QQbar, log, Ideal, PolynomialRing, xgcd, FractionField, matrix, SR, vector, PowerSeriesRing, TermOrder
2
+
3
+ def debug(*args, verbosity=1):
4
+ threshold = 11
5
+ if verbosity > threshold:
6
+ print(*args)
7
+
8
+ def jacobian(sys, vs):
9
+ return matrix(
10
+ [
11
+ [f.derivative(v) for v in vs]
12
+ for f in sys
13
+ ]
14
+ )
15
+
16
+ def truncate_base_poly(poly, params, max_degree):
17
+ """Safely drops terms from a base polynomial exceeding max_degree."""
18
+ if not poly:
19
+ return poly
20
+
21
+ R_base = poly.parent()
22
+ R = PolynomialRing(QQ, len(poly.parent().gens()), list(poly.parent().gens()))
23
+ poly, params = apply_ring_morphism(R, poly, params)
24
+ res = R(0)
25
+ for c, v in poly:
26
+ # exps is a tuple of degrees for each parameter
27
+ if v.degree() < max_degree:
28
+ res += c*v
29
+
30
+ return R_base(res)
31
+
32
+ def expand_fraction(frac, params, max_degree):
33
+ """Computes the Taylor expansion of N/D up to max_degree."""
34
+ R_base = params[0].parent()
35
+ num = R_base(frac.numerator())
36
+ den = R_base(frac.denominator())
37
+
38
+ # 1. Evaluate the denominator at the origin (params = 0)
39
+ d0_val = den.subs({p: 0 for p in params})
40
+ if d0_val == 0:
41
+ raise ValueError("Division by zero: Denominator vanishes at expansion point.")
42
+
43
+ # 2. Setup 1 / (d0 - M)
44
+ d0 = R_base(d0_val)
45
+ M = d0 - den
46
+ M_over_d0 = M / d0
47
+
48
+ inv_den = R_base(0)
49
+ term = R_base(1) / d0
50
+
51
+ # 3. Accumulate the geometric series
52
+ for _ in range(max_degree):
53
+ inv_den += term
54
+ term = truncate_base_poly(term * M_over_d0, params, max_degree)
55
+
56
+ # Multiply the numerator by the expanded denominator and truncate one last time
57
+ return truncate_base_poly(num * inv_den, params, max_degree)
58
+
59
+ def truncate_coeffs(f, params, degree):
60
+ """Truncates the fractional coefficients of a polynomial f."""
61
+ res = 0
62
+ for c, v in f:
63
+ c_trunc = expand_fraction(c, params, degree)
64
+ res += c_trunc * v
65
+ return res
66
+
67
+ def mod_by_P(f, P):
68
+ Quo = P.parent().quotient(P)
69
+ return Quo(f).lift()
70
+
71
+ def mod_truncate(f, P, params, degree):
72
+ return truncate_coeffs(mod_by_P(f, P), params, degree)
73
+
74
+ def inv(f, P):
75
+ """
76
+ Find inverse of f modulo P using extended Euclidean algorithm.
77
+ Returns g such that f*g = 1 mod P.
78
+ """
79
+
80
+ Quo = P.parent().quotient(P)
81
+ return Quo(f).inverse().lift()
82
+
83
+ def specialize_system(f_list, param_vars, param_point):
84
+ """
85
+ Substitute u = p (a concrete tuple) into each polynomial in f_list.
86
+
87
+ Parameters
88
+ ----------
89
+ f_list : list of multivariate polynomials in k[u, x]
90
+ param_vars : sequence of parameter variable objects
91
+ param_point : sequence of field elements (the specialisation p)
92
+
93
+ Returns
94
+ -------
95
+ list of polynomials in k[x]
96
+ """
97
+ R = f_list[0].parent()
98
+ R_new = PolynomialRing(QQ, [v for v in R.gens() if v not in param_vars])
99
+ R_to_new = R.hom([param_point[i] for i in range(len(param_vars))] + list(R_new.gens()))
100
+ return [R_to_new(f) for f in f_list], list(R_new.gens())
101
+
102
+ def primitive_element(x_vars, coeffs=None):
103
+ """
104
+ Build the linear form T = sum_i lambda_i * x_i.
105
+
106
+ coeffs : if None, choose random lambda_i in the base field.
107
+ """
108
+ if coeffs is None:
109
+ # random nonzero coefficients – succeed with high probability
110
+ F = x_vars[0].parent().base_ring()
111
+ coeffs = [F.random_element() for _ in x_vars]
112
+ while 0 in coeffs:
113
+ coeffs = [F.random_element() for _ in x_vars]
114
+ T_expr = sum(c * x for c, x in zip(coeffs, x_vars))
115
+ return T_expr, coeffs
116
+
117
+ def to_shape_lemma(P, Qs, u_):
118
+ Pd = P.derivative(u_)
119
+ Pd_inv = xgcd(Pd, P)[1]
120
+ return [Q*Pd_inv % P for Q in Qs]
121
+
122
+ def to_kronecker(P, Qs, params, u_, prec):
123
+ Pd = P.derivative(u_)
124
+ return [mod_truncate(Q*Pd, P, params, prec) for Q in Qs]
125
+
126
+ def apply_ring_morphism(R, *args):
127
+ res = []
128
+ for fs in args:
129
+ if type(fs) == list:
130
+ res.append([R(f) for f in fs])
131
+ elif type(fs) == tuple:
132
+ res.append((R(f) for f in fs))
133
+ else:
134
+ res.append(R(fs))
135
+ return tuple(res)
136
+
137
+ def construct_rational(gamma, r, params, prec):
138
+ r"""
139
+ Construct rational approximation for power series `r` in variables
140
+ `params` up to specified precision
141
+ """
142
+ R_base = r.parent()
143
+
144
+ s = SR.var('s')
145
+ ys = list(SR.var('y', len(params)-1)) if len(params) > 1 else []
146
+
147
+ Rs = PolynomialRing(QQ, len(params)+1, [*params, s])
148
+ R_base_to_Rs = R_base.hom(list(Rs.gens())[:-1])
149
+
150
+ r, params = apply_ring_morphism(R_base_to_Rs, r, params)
151
+ r_tilde = Rs(r.subs({v: v*s for v in params[1:]} | {params[0]: s}))
152
+ s = Rs.gens()[-1]
153
+
154
+ R_new_base = PolynomialRing(QQ, len(ys), [*ys]) if ys else QQ
155
+ R_new = PowerSeriesRing(R_new_base, [s])
156
+ R_poly = PolynomialRing(R_new_base, 1, [s])
157
+ flatten_R_new = R_poly.flattening_morphism()
158
+
159
+ # Convert to ring Q[y_2, ..., y_m][s]
160
+ s_new = R_new.gen()
161
+ ys = list(R_new_base.gens())
162
+ r_tilde = R_new(r_tilde.subs({v: y + gamma for v, y, gamma in zip(params[1:],ys, gamma)} | {s: s_new}))
163
+ #debug("rt:", r_tilde)
164
+
165
+ pd = R_new(r_tilde).pade(prec, prec)
166
+ #debug("pd:", pd)
167
+ p, q = pd.numerator(), pd.denominator()
168
+ p, q = R_new(p), R_new(q)
169
+ p = p.truncate(prec)
170
+ q = q.truncate(prec)
171
+
172
+ # Truncate coefficients by prec and convert everything back to polynomial
173
+ p = R_poly(p)
174
+ q = R_poly(q)
175
+ p = sum([c * f for c, f in p if f.degree() < prec])
176
+ q = sum([c * f for c, f in q if f.degree() < prec])
177
+ p, q, s_new = R_poly(p), R_poly(q), R_poly(s_new.polynomial())
178
+
179
+ # Sub back to original variables
180
+ p, q, s_new = apply_ring_morphism(flatten_R_new, p, q, s_new)
181
+
182
+ # 2. Build the exact algebraic inverse map
183
+ # s maps to c (params[0])
184
+ # y_i maps to (v / c) - gamma
185
+ subs_dict = {flatten_R_new(s_new): R_base(params[0])}
186
+ for y, v, g in zip(ys, params[1:], gamma):
187
+ subs_dict[flatten_R_new(y)] = R_base(v) / R_base(params[0]) - R_base(g)
188
+
189
+ # 3. Perform substitution directly.
190
+ # Because the values are in Frac_R, SageMath handles all division and clearing of denominators safely.
191
+ r_final = p.subs(subs_dict) / q.subs(subs_dict)
192
+
193
+ return r_final
194
+
195
+ def rational_reconstruction(gamma, P, kronecker_param, params, prec):
196
+ r"""
197
+ Construct rational approximation for coefficients of `P` and `kronecker_param`
198
+ as rational functions in `params` up to specified precision
199
+ """
200
+
201
+ debug("RECON START")
202
+
203
+ # Convert params to base ring
204
+ R_base = params[0].parent()
205
+
206
+ # Reconstruct coefficients of P as a polynomial in u_ with coefficients in params
207
+ debug()
208
+ debug("Reconstructing P")
209
+ debug(P.parent())
210
+ rat_P = sum(
211
+ [construct_rational(gamma, r, params, prec)*v.change_ring(R_base) for r, v in P]
212
+ )
213
+ debug("RECON P:", P)
214
+ debug()
215
+
216
+ # Same thing with all kronecker terms
217
+ debug("Reconstructing kronecker")
218
+ debug(kronecker_param[0].parent())
219
+ rat_kronecker = [
220
+ sum([construct_rational(gamma, r, params, prec)*v.change_ring(R_base) for r, v in f])
221
+ for f in kronecker_param
222
+ ]
223
+ debug()
224
+
225
+ return rat_P, rat_kronecker
226
+
227
+ def newton_lift(F, P, shape_param, vs, u_, params, linear_form, prec):
228
+ r"""
229
+ Lift precision of power series approximation for shape parametrization of `F`
230
+ """
231
+ # 1. Define the substitution X -> V(U)
232
+ Tsubs = {v: w for v, w in zip(vs, shape_param)}
233
+ debug("prec:", prec)
234
+
235
+ # sys = F in the algorithm by Schost
236
+ T = [v - w for v, w in zip(vs, shape_param)] + [P]
237
+ sys = F + [u_ - linear_form]
238
+
239
+ # 2. Evaluate Jacobians and explicitly substitute X = V(U) early
240
+ # This ensures no 'x' or 'y' variables sneak into the matrix inverses.
241
+ JacF = jacobian(sys, [*vs, u_]).subs(Tsubs)
242
+ JacT = jacobian(T, [*vs, u_]).subs(Tsubs)
243
+ debug("JacF:", JacF)
244
+
245
+ # 3. Invert JacF in the precision-k quotient ring (modulo P and params^prec)
246
+ R = u_.parent()
247
+ R_base = R.base_ring()
248
+
249
+ debug("T:", T)
250
+ debug("sys:", sys)
251
+
252
+ # 5. Evaluate the defect (sys) at X = V(U) to retain terms up to O(params^{2*prec})
253
+ sys_eval = vector([f.subs(Tsubs) for f in sys])
254
+ JacF_inv_times_sys_eval = JacF.solve_right(sys_eval)
255
+ JacF_inv_times_sys_eval = vector(
256
+ [f.numerator() * inv(f.denominator(), P) for f in JacF_inv_times_sys_eval]
257
+ )
258
+ debug("eval:", sys_eval)
259
+
260
+ # 6. Multiply M by the defect and reduce modulo P and params^{2*prec}
261
+ deltas = JacT * JacF_inv_times_sys_eval #M * sys_eval
262
+ debug("deltas:", deltas)
263
+ deltas = [mod_truncate(d, P, params, 2*prec) for d in deltas]
264
+ debug("P:", P)
265
+ debug("deltas:", deltas)
266
+
267
+ # 7. Apply updates: V_new = V_old - delta_V, P_new = P_old + delta_P
268
+ new_shape_param = [truncate_coeffs(w-delta, params, 2*prec) for w, delta in zip(shape_param, deltas[:-1])]
269
+ newP = truncate_coeffs(P+deltas[-1], params, 2*prec)
270
+ debug("oldP", P)
271
+ debug("P_update", deltas[-1])
272
+ debug("test:", P+deltas[-1])
273
+ debug("newP", newP)
274
+
275
+ return newP, new_shape_param
276
+
277
+ def stop_criterion(F, P, params, kronecker_param, test_param, u_, vs):
278
+ r"""
279
+
280
+ """
281
+ R_base = params[0].parent()
282
+ test_subs = {v:p for v, p in zip(params, test_param)}
283
+ f_subs = {v:q for v, q in zip(vs, kronecker_param)}
284
+
285
+ # Test 1: Check that no denominators vanish at test parameter
286
+ for coeff in P.coefficients():
287
+ if R_base(coeff.denominator()).subs(test_subs) == 0:
288
+ debug("Coefficient vanishes at test point", verbosity=10)
289
+ return False
290
+ for f in kronecker_param:
291
+ for coeff in f.coefficients():
292
+ if R_base(coeff.denominator()).subs(test_subs) == 0:
293
+ debug("Coefficient vanishes at test point", verbosity=10)
294
+ return False
295
+
296
+ def specialize_at_param(f, subs):
297
+ return sum(c.subs(subs) * v for c, v in f).change_ring(QQ)
298
+
299
+ # Test 2: Check that P is square-free when specialized at test parameter
300
+ P_specialized = specialize_at_param(P, test_subs)
301
+ if not P_specialized.is_squarefree():
302
+ debug("P is not square-free", verbosity=10)
303
+ return False
304
+
305
+ # Test 3: Check that the roots of P when specialized at test parameter
306
+ # give solutions to the original system
307
+ kronecker_specialized = [
308
+ specialize_at_param(f, test_subs)
309
+ for f in kronecker_param
310
+ ]
311
+
312
+ R_specialized = P_specialized.parent()
313
+ u_ = R_specialized(u_)
314
+ vs = [R_specialized(v) for v in vs]
315
+ F_specialized = [
316
+ sum(c.subs(test_subs) * v for c, v in f).change_ring(QQ)
317
+ for f in F
318
+ ]
319
+ debug("F_specialized", F_specialized, verbosity=10)
320
+ debug("P_Specialized:", P_specialized, verbosity=10)
321
+ debug("kronecker_specialized:", kronecker_specialized, verbosity=10)
322
+ sols = []
323
+ for u in P_specialized.polynomial(u_).roots(QQbar, multiplicities=False):
324
+ sols.append(
325
+ {
326
+ v: QQbar((Q/P_specialized.derivative(u_)).subs({u_:u}))
327
+ for v, Q in zip(vs, kronecker_specialized)
328
+ }
329
+ )
330
+ debug("sols:", sols, verbosity=10)
331
+
332
+ for f in F_specialized:
333
+ if any(f.subs(sol) != 0 for sol in sols):
334
+ debug(f, verbosity=5)
335
+ debug(sols, verbosity=5)
336
+ for sol in sols:
337
+ debug(f.subs(sol), verbosity=5)
338
+ debug("Solution not satisfied", verbosity=10)
339
+ return False
340
+
341
+ det = jacobian(F_specialized, vs).determinant()
342
+ if any(det.subs(sol) == 0 for sol in sols):
343
+ debug("Singular Jacobian", verbosity=10)
344
+ return False
345
+
346
+ return True
347
+
348
+ def parametric_geometric_resolution(F, num_params, param_point=None, test_param=None, gamma=None):
349
+ r"""
350
+ Comput a parametric geometric resolution for system of polynomials `F = (f_1,\ldots,f_n)` in
351
+ `\mathbb{Q}[p_1,\ldots,p_m,x_1,\ldots,x_n]` where ``m = num_params``.
352
+ """
353
+ # Separate input by parameters and variables
354
+ R_original = F[0].parent()
355
+ params = list(R_original.gens())[:num_params]
356
+ vs = list(R_original.gens())[num_params:]
357
+
358
+ # Specialize F at random parameter point
359
+ if param_point is None:
360
+ param_point = [QQ.random_element(10) for _ in params]
361
+ if test_param is None:
362
+ test_param = [QQ.random_element(10) for _ in params]
363
+ if gamma is None:
364
+ gamma = [QQ.random_element(10) for _ in params[:-1]]
365
+
366
+ # Shift F over by param point, so expansions are done around 0
367
+ F = [f.subs({v:v+p for v, p in zip(params, param_point)}) for f in F]
368
+
369
+ #linear_form, lam = primitive_element(vs)
370
+ F_p, vsp = specialize_system(F, params, [0 for _ in params])
371
+
372
+ # Compute kronecker and shape representations at specialized point
373
+ from sage_acsv import ACSVSettings as AS
374
+ AS.set_default_kronecker_backend(AS.Kronecker.MSOLVE)
375
+ from sage_acsv.kronecker import kronecker_representation
376
+ P, kronecker_param, linear_form = kronecker_representation(F_p, vsp, return_linear_form=True)
377
+ u_ = P.parent().gen()
378
+ shape_param = to_shape_lemma(P, kronecker_param, u_)
379
+
380
+ # Generate rings that will be used throughout
381
+ R_base = PolynomialRing(QQ, len(params), params).fraction_field()
382
+ params = [R_base(p) for p in params]
383
+ R = PolynomialRing(R_base, vs + [u_])
384
+ original_to_R = R_original.hom(
385
+ list(R_base.gens()) + list(R.gens())[:-1]
386
+ )
387
+ F, vs = apply_ring_morphism(original_to_R, F, vs)
388
+
389
+ u_to_R = u_.parent().hom([R.gens()[-1]])
390
+ u_, P, kronecker_param, shape_param = apply_ring_morphism(
391
+ u_to_R, u_, P, kronecker_param, shape_param
392
+ )
393
+ debug("Original:", verbosity=10)
394
+ debug("linear_form:", linear_form, verbosity=10)
395
+ debug("P:", P, verbosity=10)
396
+ debug("kronecker:", kronecker_param, verbosity=10)
397
+ debug("shape:", shape_param, verbosity=10)
398
+ debug(verbosity=10)
399
+
400
+ D = max(f.degree() for f in F)
401
+ n = len(F)
402
+ MAX_ITER = int(log(2*D**n+1, 2))+1
403
+
404
+ prec = 1
405
+ for kappa in range(MAX_ITER):
406
+ # Convert shape param to kronecker
407
+ debug("ITER:", kappa, verbosity=10)
408
+ kronecker_param = to_kronecker(P, shape_param, params, u_, prec)
409
+ debug("TO KRONECKER", verbosity=10)
410
+ debug(P, verbosity=10)
411
+ debug(kronecker_param, verbosity=10)
412
+ debug(verbosity=10)
413
+
414
+ if prec >= 2:
415
+ # Reconstruct rational coefficients and check if done
416
+ rational_P, rational_params = rational_reconstruction(gamma, P, kronecker_param, params, prec//2)
417
+ debug("RECONSTRUCTED KRONECKER:", verbosity=10)
418
+ debug(rational_P, verbosity=10)
419
+ debug(rational_params, verbosity=10)
420
+ debug(verbosity=10)
421
+ debug("STOP CRITERION:", verbosity=10)
422
+ if stop_criterion(F, rational_P, params, rational_params, test_param, u_, vs):
423
+ # Shift params back
424
+ rational_P = sum(
425
+ c.subs({v:v-p for v, p in zip(params, param_point)}) * v for c, v in rational_P
426
+ )
427
+ rational_params = [
428
+ sum(
429
+ c.subs({v:v-p for v, p in zip(params, param_point)}) * v for c, v in f
430
+ )
431
+ for f in rational_params
432
+ ]
433
+ debug("DONE", verbosity=10)
434
+ return rational_P, rational_params
435
+ else:
436
+ debug("CRITERION FAILED", verbosity=10)
437
+ debug(verbosity=10)
438
+
439
+ # Lift shape param to higher order
440
+ P, shape_param = newton_lift(F, P, shape_param, vs, u_, params, linear_form, prec)
441
+ debug("LIFTED", verbosity=10)
442
+ debug(P, verbosity=10)
443
+ debug(shape_param, verbosity=10)
444
+ debug(verbosity=10)
445
+
446
+ prec *= 2
447
+ debug(verbosity=10)
448
+ debug("------------------------------------", verbosity=10)
449
+
450
+ return None
@@ -0,0 +1 @@
1
+ from pgr.PGR import *
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: sage_pgr
3
+ Version: 0.1.0
4
+ Summary: Parametric Geometric Resolutions
5
+ Author-email: Andrew Luo <j92luo@uwaterloo.ca>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 turnip314
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: repository, https://github.com/turnip314/sage_pgr
29
+ Requires-Python: >=3.9
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Dynamic: license-file
@@ -0,0 +1,8 @@
1
+ LICENSE
2
+ pyproject.toml
3
+ src/pgr/PGR.py
4
+ src/pgr/__init__.py
5
+ src/sage_pgr.egg-info/PKG-INFO
6
+ src/sage_pgr.egg-info/SOURCES.txt
7
+ src/sage_pgr.egg-info/dependency_links.txt
8
+ src/sage_pgr.egg-info/top_level.txt