metcones 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.
- metcones/__init__.py +2 -0
- metcones/cut.py +51 -0
- metcones/metric.py +145 -0
- metcones/utils.py +20 -0
- metcones-0.0.1.dist-info/METADATA +39 -0
- metcones-0.0.1.dist-info/RECORD +8 -0
- metcones-0.0.1.dist-info/WHEEL +5 -0
- metcones-0.0.1.dist-info/top_level.txt +1 -0
metcones/__init__.py
ADDED
metcones/cut.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from itertools import combinations
|
|
3
|
+
from sage.geometry.cone import Cone
|
|
4
|
+
from sage.geometry.polyhedron.constructor import Polyhedron
|
|
5
|
+
from sage.all import QQ
|
|
6
|
+
from metcones.utils import link_obj_methods
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_cut_rays_vectorized(n):
|
|
10
|
+
inc_matrix = np.arange(2 ** (n - 1))[:, None]
|
|
11
|
+
inc_steps = inc_matrix & (1 << np.arange(n - 1))
|
|
12
|
+
inc_steps = (inc_steps > 0).astype(int)
|
|
13
|
+
nodes = np.hstack([np.ones((2 ** (n - 1), 1)), inc_steps])
|
|
14
|
+
edges = np.array(list(combinations(range(n), 2)))
|
|
15
|
+
semi_metrics = (nodes[:, edges[:, 0]] != nodes[:, edges[:, 1]]).astype(int)
|
|
16
|
+
return semi_metrics[np.any(semi_metrics, axis=1)]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def CutPoly(n, **kwargs):
|
|
20
|
+
if "backend" in kwargs and "base_ring" not in kwargs:
|
|
21
|
+
kwargs["base_ring"] = QQ
|
|
22
|
+
elif "backend" not in kwargs and "base_ring" not in kwargs:
|
|
23
|
+
kwargs["backend"] = "normaliz"
|
|
24
|
+
kwargs["base_ring"] = QQ
|
|
25
|
+
cut_rays = get_cut_rays_vectorized(n)
|
|
26
|
+
return Polyhedron(rays=cut_rays, **kwargs)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def CutCone(n, **kwargs):
|
|
30
|
+
cut_rays = get_cut_rays_vectorized(n)
|
|
31
|
+
poly = Polyhedron(rays=cut_rays, **kwargs)
|
|
32
|
+
c = Cone(poly)
|
|
33
|
+
methods = [
|
|
34
|
+
"n_Hrepresentation",
|
|
35
|
+
"n_Vrepresentation",
|
|
36
|
+
"n_equations",
|
|
37
|
+
"n_inequalities",
|
|
38
|
+
"n_vertices",
|
|
39
|
+
"n_rays",
|
|
40
|
+
"n_lines",
|
|
41
|
+
"n_facets",
|
|
42
|
+
"f_vector",
|
|
43
|
+
"triangulate",
|
|
44
|
+
]
|
|
45
|
+
link_obj_methods(poly, c, methods, src_name="poly")
|
|
46
|
+
return c
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
print(get_cut_rays_vectorized(3))
|
|
51
|
+
print(get_cut_rays_vectorized(4))
|
metcones/metric.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.sparse import csr_matrix
|
|
3
|
+
from itertools import combinations
|
|
4
|
+
|
|
5
|
+
from sage.all import QQ
|
|
6
|
+
from sage.geometry.polyhedron.constructor import Polyhedron
|
|
7
|
+
from sage.geometry.cone import Cone
|
|
8
|
+
from metcones.utils import link_obj_methods
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def MetricPoly(n_pts, include_pos: bool = False, no_limit: bool = False, **kwargs):
|
|
12
|
+
if n_pts < 3:
|
|
13
|
+
raise ValueError(
|
|
14
|
+
"Invalid metric poly definition. (Triangle inequality definition requires a minimum of three points)"
|
|
15
|
+
)
|
|
16
|
+
elif not no_limit and n_pts > 7:
|
|
17
|
+
raise ValueError(
|
|
18
|
+
"Value exceeds calculation limit. (Combinatorial explosion on metric cones n >= 7)"
|
|
19
|
+
)
|
|
20
|
+
ieqs = _calc_ieqs(n_pts, include_pos=include_pos).astype(int)
|
|
21
|
+
ieqs = _ieqs_to_sage_ieqs(ieqs)
|
|
22
|
+
if "backend" in kwargs and "base_ring" not in kwargs:
|
|
23
|
+
kwargs["base_ring"] = QQ
|
|
24
|
+
elif "backend" not in kwargs and "base_ring" not in kwargs:
|
|
25
|
+
kwargs["backend"] = "normaliz"
|
|
26
|
+
kwargs["base_ring"] = QQ
|
|
27
|
+
poly = Polyhedron(ieqs=ieqs, **kwargs)
|
|
28
|
+
return poly
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def MetricCone(n_pts, include_pos=False, no_limit: bool = False, **kwargs):
|
|
32
|
+
poly = MetricPoly(n_pts, include_pos=include_pos, no_limit=no_limit, **kwargs)
|
|
33
|
+
c = Cone(poly)
|
|
34
|
+
methods = [
|
|
35
|
+
"n_Hrepresentation",
|
|
36
|
+
"n_Vrepresentation",
|
|
37
|
+
"n_equations",
|
|
38
|
+
"n_inequalities",
|
|
39
|
+
"n_vertices",
|
|
40
|
+
"n_rays",
|
|
41
|
+
"n_lines",
|
|
42
|
+
"n_facets",
|
|
43
|
+
"f_vector",
|
|
44
|
+
"triangulate",
|
|
45
|
+
]
|
|
46
|
+
link_obj_methods(poly, c, methods, src_name="poly")
|
|
47
|
+
return c
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def UMetPoly(n_pts, include_pos: bool = False, no_limit: bool = False, **kwargs):
|
|
51
|
+
if n_pts < 3:
|
|
52
|
+
raise ValueError(
|
|
53
|
+
"Invalid metric poly definition. (Triangle inequality definition requires a minimum of three points)"
|
|
54
|
+
)
|
|
55
|
+
elif not no_limit and n_pts > 7:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"Value exceeds calculation limit. (Combinatorial explosion on metric cones n >= 7)"
|
|
58
|
+
)
|
|
59
|
+
ieqs = _calc_ultra_ieqs(n_pts, include_pos=include_pos).astype(int)
|
|
60
|
+
ieqs = _ieqs_to_sage_ieqs(ieqs)
|
|
61
|
+
if "backend" in kwargs and "base_ring" not in kwargs:
|
|
62
|
+
kwargs["base_ring"] = QQ
|
|
63
|
+
elif "backend" not in kwargs and "base_ring" not in kwargs:
|
|
64
|
+
kwargs["backend"] = "normaliz"
|
|
65
|
+
kwargs["base_ring"] = QQ
|
|
66
|
+
poly = Polyhedron(ieqs=ieqs, **kwargs)
|
|
67
|
+
return poly
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def UMetCone(n_pts, include_pos=False, no_limit: bool = False, **kwargs):
|
|
71
|
+
poly = UMetPoly(n_pts, include_pos=include_pos, no_limit=no_limit, **kwargs)
|
|
72
|
+
c = Cone(poly)
|
|
73
|
+
methods = [
|
|
74
|
+
"n_Hrepresentation",
|
|
75
|
+
"n_Vrepresentation",
|
|
76
|
+
"n_equations",
|
|
77
|
+
"n_inequalities",
|
|
78
|
+
"n_vertices",
|
|
79
|
+
"n_rays",
|
|
80
|
+
"n_lines",
|
|
81
|
+
"n_facets",
|
|
82
|
+
"f_vector",
|
|
83
|
+
"triangulate",
|
|
84
|
+
]
|
|
85
|
+
link_obj_methods(poly, c, methods, src_name="poly")
|
|
86
|
+
return c
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _calc_ieqs(n_pts: int, include_pos: bool = False):
|
|
90
|
+
pts = range(n_pts)
|
|
91
|
+
edges = list(combinations(pts, 2))
|
|
92
|
+
num_edges = len(edges)
|
|
93
|
+
edge_map = {edge: i for i, edge in enumerate(edges)}
|
|
94
|
+
trips = np.array(list(combinations(pts, 3)))
|
|
95
|
+
num_trips = len(trips)
|
|
96
|
+
idx = np.array(
|
|
97
|
+
[
|
|
98
|
+
[edge_map[(t[0], t[1])], edge_map[(t[1], t[2])], edge_map[(t[0], t[2])]]
|
|
99
|
+
for t in trips
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
cols = idx[:, [[0, 1, 2], [0, 2, 1], [1, 2, 0]]].reshape(-1, 3)
|
|
103
|
+
rows = np.repeat(np.arange(num_trips * 3), 3)
|
|
104
|
+
data = np.tile([1, 1, -1], len(trips) * 3)
|
|
105
|
+
ieqs = csr_matrix(
|
|
106
|
+
(data, (rows, cols.ravel())), shape=(num_trips * 3, num_edges)
|
|
107
|
+
).toarray()
|
|
108
|
+
if include_pos:
|
|
109
|
+
ieqs = np.vstack([ieqs, np.eye(num_edges)])
|
|
110
|
+
return ieqs
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _calc_ultra_ieqs(n_pts: int, include_pos: bool = False):
|
|
114
|
+
pts = range(n_pts)
|
|
115
|
+
edges = list(combinations(pts, 2))
|
|
116
|
+
num_edges = len(edges)
|
|
117
|
+
edge_map = {edge: i for i, edge in enumerate(edges)}
|
|
118
|
+
trips = np.array(list(combinations(pts, 3)))
|
|
119
|
+
num_trips = len(trips)
|
|
120
|
+
idx = np.array(
|
|
121
|
+
[
|
|
122
|
+
[edge_map[(t[0], t[1])], edge_map[(t[1], t[2])], edge_map[(t[0], t[2])]]
|
|
123
|
+
for t in trips
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
cols = idx[:, [[0, 1], [0, 2], [1, 2], [1, 0], [2, 0], [2, 1]]].reshape(-1, 2)
|
|
127
|
+
rows = np.repeat(np.arange(num_trips * 6), 2)
|
|
128
|
+
data = np.tile([1, -1], num_trips * 6)
|
|
129
|
+
ieqs = csr_matrix(
|
|
130
|
+
(data, (rows, cols.ravel())), shape=(num_trips * 6, num_edges)
|
|
131
|
+
).toarray()
|
|
132
|
+
if include_pos:
|
|
133
|
+
ieqs = np.vstack([ieqs, np.eye(num_edges)])
|
|
134
|
+
return ieqs
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _ieqs_to_sage_ieqs(ieqs: np.ndarray) -> np.ndarray:
|
|
138
|
+
assert ieqs is not None, "ieqs must be a valid sparse matrix"
|
|
139
|
+
rows, _ = ieqs.shape
|
|
140
|
+
zeros = np.zeros((rows, 1))
|
|
141
|
+
return np.hstack((zeros, ieqs))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
print(MetricPoly(3).f_vector())
|
metcones/utils.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from sage.geometry.polyhedron.parent import Polyhedra
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def link_obj_methods(src, dst, method_names, src_name: str = "__src_obj"):
|
|
5
|
+
setattr(dst, src_name, src)
|
|
6
|
+
|
|
7
|
+
for name in method_names:
|
|
8
|
+
|
|
9
|
+
def make_method(n, s_name):
|
|
10
|
+
return lambda *args, **kwargs: getattr(getattr(dst, s_name), n)(
|
|
11
|
+
*args, **kwargs
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
setattr(dst, name, make_method(name, src_name))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def compare_rays(C1, C2):
|
|
18
|
+
rays1 = set(tuple(ray) for ray in C1.rays())
|
|
19
|
+
rays2 = set(tuple(ray) for ray in C2.rays())
|
|
20
|
+
return rays1 == rays2
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: metcones
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Library adding Metric and Cut Cone generation utilizing sage math.
|
|
5
|
+
Author-email: Daniel Castro <Danielaca1818@gmail.com>
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: sage
|
|
8
|
+
|
|
9
|
+
# MetCones
|
|
10
|
+
|
|
11
|
+
A library to generate metric and cut cones as SageMath objects.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install metcones
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Generating the cones as SageMath objects:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from metcones import CutCone, MetricCone, CutPoly, MetricPoly
|
|
25
|
+
|
|
26
|
+
cut = CutCone(3)
|
|
27
|
+
metric = MetricCone(3)
|
|
28
|
+
cut_poly = CutPoly(3)
|
|
29
|
+
met_poly = MetricPoly(3)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- SageMath
|
|
35
|
+
- PyNormaliz
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
metcones/__init__.py,sha256=G8Ms2PUa-qlOpZRajl6AflkTK8bvvT3FiYDan01cl6E,41
|
|
2
|
+
metcones/cut.py,sha256=6PfZ-noM4jSmt1UUvE1jObBnh4JFfxFqP6l5SIwx0Zo,1555
|
|
3
|
+
metcones/metric.py,sha256=TfMvSj8vcytUuUo-ZI6nyCFQWxQKknbU_IAywKllwpg,4776
|
|
4
|
+
metcones/utils.py,sha256=LKzH9kteULeL0Y7DNrodJA0sX3hnrgxOHvG5XDBA-S8,563
|
|
5
|
+
metcones-0.0.1.dist-info/METADATA,sha256=XTS_PK7ENNHua19uc-eaUrLuSCfpPsFA3JmLA-euRbI,647
|
|
6
|
+
metcones-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
metcones-0.0.1.dist-info/top_level.txt,sha256=cc7eGxtws6cj_Qb5RllvEabLUaJ8KQ_1l9_1flPEOdg,9
|
|
8
|
+
metcones-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
metcones
|