• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

peekxc / splex / 6566946791

18 Oct 2023 09:47PM UTC coverage: 82.528% (-2.2%) from 84.734%
6566946791

push

github

peekxc
Working on the abc for filtration types. I think I have a good idea of what a good wrapper type should implement. Started the Filtration abc, which going to be similar to a Sequence type mixed with a Set type, but have some additional constraints

58 of 58 new or added lines in 5 files covered. (100.0%)

666 of 807 relevant lines covered (82.53%)

2.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.16
/src/splex/filtrations/RankFiltration.py
1

2
import numbers
3✔
3
import numpy as np 
3✔
4

5
from ..meta import *
3✔
6
from ..complexes import RankComplex
3✔
7
from combin import *
3✔
8

9

10
class RankFiltration(FiltrationLike):
3✔
11
  def __init__(self, simplices: Union[ComplexLike, Iterable], f: Callable = None):
3✔
12
    if f is not None:
3✔
13
      s_dtype= np.dtype([('rank', np.uint64), ('dim', np.uint16), ('value', np.float64)])
×
14
      self.simplices = np.array([(comb_to_rank(s), len(s)-1, f(s)) for s in simplices], dtype=s_dtype)
×
15
    else: 
16
      s_dtype= np.dtype([('rank', np.uint64), ('dim', np.uint16), ('value', np.uint32)])
3✔
17
      self.simplices = np.array([(comb_to_rank(s), len(s)-1, i) for i, s in enumerate(simplices)], dtype=s_dtype)
3✔
18
  
19
  def __iter__(self) -> Iterable:
3✔
20
    """Enumerates the faces of the complex."""
21
    simplices = rank_to_comb(self.simplices['rank'], k=self.simplices['dim']+1, order='colex')
×
22
    yield from zip(self.simplices['value'], simplices)
×
23

24
  def reindex(self, f: Callable['SimplexLike', Any]) -> None:
3✔
25
    self.simplices['value'] = f()
×
26
    ind = np.argsort(self.simplices, order=('value', 'dim', 'rank'))
×
27
    self.simplices = self.simplices[ind]
×
28

29
#   ## Mapping interface
30
#   __iter__ = lambda self: iter(self.simplices['f'])
31
#   def __getitem__(self, k) -> SimplexLike: 
32
#     i = np.searchsorted(self.simplices['f'])
33
#     r,d,f = self.simplices[i][:2]
34
#     return unrank_colex(r, d)
35
  
36
#   ## Mapping mixins
37
#   keys = lambda self: iter(self.simplices['f'])
38
#   values = lambda self: self.faces()
39
#   items = lambda self: zip(self.keys(), self.values())
40
#   __eq__ = lambda self, other: all(self.simplices == other.simplices) if len(self.simplices) == len(other.simplices) else False
41
#   __ne__ = lambda self, other: any(self.simplices != other.simplices) if len(self.simplices) == len(other.simplices) else False
42

43

44
  ## MutableMapping Interface 
45
  # __setitem__, __delitem__, pop, popitem, clear, update, setdefault
46

47
# class MutableCombinatorialFiltration(CombinatorialComplex, Mapping):
48

49
    #   self.simplices = simplices
50
    #   self.indices = range(len(simplices)) if I is None else I
51
    #   assert all([isinstance(s, SimplexLike) for s in simplices]), "Must all be simplex-like"
52
    #   if I is not None: assert len(simplices) == len(I)
53
    #   self.simplices = [Simplex(s) for s in simplices]
54
    #   self.index_set = np.arange(0, len(simplices)) if I is None else np.asarray(I)
55
    #   self.dtype = [('s', Simplex), ('index', I.dtype)]
56
    # self.data = SortedDict()
57
    # self.shape = tuple()
58
    # if isinstance(iterable, SimplicialComplex):
59
    #   if isinstance(f, Callable):
60
    #     self += ((f(s), s) for s in iterable)
61
    #   elif f is None:
62
    #     index_set = np.arange(len(iterable))  
63
    #     iterable = sorted(iter(iterable), key=lambda s: (len(s), tuple(s), s)) # dimension, lex, face poset
64
    #     self += zip(iter(index_set), iterable)
65
    #   else:
66
    #     raise ValueError("Invalid input for simplicial complex")
67
    # elif isinstance(iterable, Iterable):
68
    #   self += iterable ## accept pairs, like a normal dict
69
    # elif iterable is None:
70
    #   pass
71
    # else: 
72
    #   raise ValueError("Invalid input")
73

74
  # ## delegate new behavior to new methods: __iadd__, __isub__
75
  # def update(self, other: Iterable[Tuple[Any, Collection[Integral]]]):
76
  #   for k,v in other:
77
  #     self.data.__setitem__(k, self._sorted_set(v))
78

79
  # def __getitem__(self, key: Any) -> Simplex: 
80
  #   return self.data.__getitem__(key)
81

82
  # def __setitem__(self, k: Any, v: Union[Collection[Integral], SortedSet]):
83
  #   self.data.__setitem__(k, self._sorted_set(v))
84
  
85
  # ## Returns the value of the item with the specified key.
86
  # ## If key doesn't exist, set's F[key] = default and returns default
87
  # def setdefault(self, key, default=None):
88
  #   if key in self.data:
89
  #     return self[key] # value type 
90
  #   else:
91
  #     self.__setitem__(key, default)
92
  #     return self[key]   # value type   
93

94
  # def __delitem__(self, k: Any):
95
  #   self.data.__del__(k)
96
  
97
  # def __iter__(self) -> Iterator:
98
  #   return iter(self.keys())
99

100
  # def __len__(self) -> int:
101
  #   return sum(self.shape)
102
  #   #return self.data.__len__()
103

104
  # # https://peps.python.org/pep-0584/
105
  # def __or__(self, other: Union[Iterable[Tuple[int, int]], Mapping]):
106
  #   new = self.copy()
107
  #   new.update(SortedDict(other))
108
  #   return new
109

110
  # def __ror__(self, other: Union[Iterable[Tuple[int, int]], Mapping]):
111
  #   new = SortedDict(other)
112
  #   new.update(self.data)
113
  #   return new
114

115
  # ## In-place union '|=' operator 
116
  # # TODO: map Collection[Integral] -> SimplexLike? 
117
  # def __ior__(self, other: Union[Iterable[Tuple[int, int]], Mapping]):
118
  #   self.data.update(other)
119
  #   return self
120
  
121
  # ## In-place append '+=' operator ; true dict union/merge, retaining values
122
  # def __iadd__(self, other: Iterable[Tuple[int, int]]):
123
  #   for k,v in other:
124
  #     if len(Simplex(v)) >= 1:
125
  #       # print(f"key={str(k)}, val={str(v)}")
126
  #       s_set = self.setdefault(k, self._sorted_set())
127
  #       f = Simplex(v)
128
  #       if not(f in s_set):
129
  #         s_set.add(f)
130
  #         if len(f) > len(self.shape):
131
  #           self.shape = tuple(list(tuple(self.shape)) + [1])
132
  #         else:
133
  #           t = self.shape
134
  #           self.shape = tuple(t[i]+1 if i == (len(f)-1) else t[i] for i in range(len(t)))   
135
  #   return self
136

137
  # ## Copy-add '+' operator 
138
  # def __add__(self, other: Iterable[Tuple[int, int]]):
139
  #   new = self.copy()
140
  #   new += other 
141
  #   return new
142

143
  # ## Simple copy operator 
144
  # def copy(self) -> 'MutableFiltration':
145
  #   new = MutableFiltration()
146
  #   new.data = self.data.copy()
147
  #   new.shape = self.shape.copy()
148
  #   return new 
149

150
  # ## Keys yields the index set. Set expand = True to get linearized order. 
151
  # ## TODO: Make view objects
152
  # def keys(self):
153
  #   it_keys = chain()
154
  #   for k,v in self.data.items():
155
  #     it_keys = chain(it_keys, repeat(k, len(v)))
156
  #   return it_keys
157

158
  # def values(self):
159
  #   it_vals = chain()
160
  #   for v in self.data.values():
161
  #     it_vals = chain(it_vals, iter(v))
162
  #   return it_vals
163

164
  # def items(self):
165
  #   it_keys, it_vals = chain(), chain()
166
  #   for k,v in self.data.items():
167
  #     it_keys = chain(it_keys, repeat(k, len(v)))
168
  #     it_vals = chain(it_vals, iter(v))
169
  #   return zip(it_keys, it_vals)
170

171
  # def reindex_keys(self, index_set: Iterable):
172
  #   ''' Given a totally ordered key set of the same length of the filtation, reindexes '''
173
  #   assert len(index_set) == len(self)
174
  #   assert all((i <= j for i,j in pairwise(index_set)))
175
  #   new = MutableFiltration(zip(iter(index_set), self.values()))
176
  #   return new
177

178
  # def faces(self, p: int = None) -> Iterable:
179
  #   return filter(lambda s: len(s) == p+1, self.values())
180

181
  # def __repr__(self) -> str:
182
  #   # from collections import Counter
183
  #   # cc = Counter([len(s)-1 for s in self.values()])
184
  #   # cc = dict(sorted(cc.items()))
185
  #   n = len(self.shape)
186
  #   return f"{n-1}-d filtered complex with {self.shape}-simplices of dimension {tuple(range(n))}"
187

188
  # def print(self, **kwargs) -> None:
189
  #   import sys
190
  #   fv_s, fs_s = [], []
191
  #   for k,v in self.items():
192
  #     ks = len(str(v))
193
  #     fv_s.append(f"{str(k):<{ks}.{ks}}")
194
  #     fs_s.append(f"{str(v): <{ks}}")
195
  #     assert len(fv_s[-1]) == len(fs_s[-1])
196
  #   sym_le, sym_inc = (' ≤ ', ' ⊆ ') if sys.getdefaultencoding()[:3] == 'utf' else (' <= ', ' <= ') 
197
  #   print(repr(self))
198
  #   print("I: " + sym_le.join(fv_s[:5]) + sym_le + ' ... ' + sym_le + sym_le.join(fv_s[-2:]), **kwargs)
199
  #   print("S: " + sym_inc.join(fs_s[:5]) + sym_inc + ' ... ' + sym_inc + sym_inc.join(fs_s[-2:]), **kwargs)
200

201
  # def validate(self, light: bool = True) -> bool:
202
  #   fs = list(self.values())
203
  #   for i, s in enumerate(fs): 
204
  #     p = s.dimension() - 1 if light and len(s) >= 2 else None
205
  #     assert all([fs.index(face) <= i for face in s.faces(p)])
206
  #   assert all([k1 <= k2 for k1, k2 in pairwise(self.keys())])
207

208
  # def __format__(self, format_spec = "default") -> str:
209
  #   from io import StringIO
210
  #   s = StringIO()
211
  #   self.print(file=s)
212
  #   res = s.getvalue()
213
  #   s.close()
214
  #   return res
215
  
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc