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

peekxc / splex / 9978389681

17 Jul 2024 05:02PM UTC coverage: 84.772% (-0.2%) from 84.929%
9978389681

push

github

peekxc
v0.3.2

20 of 22 new or added lines in 5 files covered. (90.91%)

17 existing lines in 4 files now uncovered.

835 of 985 relevant lines covered (84.77%)

2.54 hits per line

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

67.24
/src/splex/generics.py
1
## --- GENERICS --- 
2
import numpy as np 
3✔
3
import itertools as it
3✔
4
from numbers import Integral
3✔
5
from more_itertools import unique_everseen
3✔
6
from collections.abc import Collection
3✔
7

8
from .meta import _data_attributes
3✔
9
from .predicates import *
3✔
10

11
def zip_data(g: Iterable[SimplexConvertible], data: Union[bool, dict, str]) -> Iterable:
3✔
12
  """Converts an iterable of data-attributed simplex-like objects into a sequence of tuples (s, s_data).
13
  
14
  This function decouples data attributes (stored in __slots__ or __dict__) from an iterable of simplex-like objects
15
  via a zip operation, remapping key-value pairs to an dictionary per simplex. 
16
  """
17
  if isinstance(data, bool):
3✔
18
    if data == False: 
3✔
19
      yield from g
3✔
20
    for el in g: 
3✔
21
      yield (el, { attr_name : getattr(el, attr_name) for attr_name in _data_attributes(el) })
3✔
22
    # return zip(g, (extract_data(e) for e in g))
UNCOV
23
  elif isinstance(data, str):
×
24
    for el in g: 
×
25
      yield el, getattr(el, data) if hasattr(el, data) else None
×
NEW
26
  elif isinstance(data, Collection):
×
NEW
27
    yield from zip(g, data)
×
28
  else: 
UNCOV
29
    raise ValueError(f"Invalid data input of type '{type(data)}'")
×
30

31
def dim(s: Union[SimplexConvertible, ComplexLike], **kwargs) -> int:
3✔
32
  """Returns the dimension of a simplicial object.
33
  
34
  If _s_ has an existing method _s.dim(...)_, then that method is called with additional keyword arguments _kwargs_.
35

36
  Otherwise, the behavior of this function depends on the type-class of _s_. Namely, 
37
  - if _s_ is SimplexLike with dimension _p_, then _p_ is returned. 
38
  - if _s_ is ComplexLike, then the largest dimension _p_ of any face in _s_ is returned.
39
  - if _s_ is none of the above but is Sized, len(_s_) - 1 is returned. 
40
  """
41
  if hasattr(s, "dim"):
3✔
42
    return s.dim(**kwargs)
3✔
43
  else:
44
    if is_complex_like(s):
3✔
UNCOV
45
      return max((dim(s, **kwargs) for s in s))
×
46
    else: 
47
      return len(s) - 1
3✔
48

49
def boundary(s: Union[SimplexConvertible, ComplexLike], p: int = None, oriented: bool = False, **kwargs) -> Iterable['SimplexConvertible']:
3✔
50
  """
51
  Returns the boundary of a simplicial object, optionally signed.
52

53
  If _s_ has an existing method _s.boundary(p, oriented)_, then that method is called with additional keyword args _kwargs_.
54

55
  Otherwise, the behavior of this function depends on the type-class of _s_. Namely, 
56
  - if _s_ is SimplexLike with dimension _p_, then a generator enumerating _(p-1)_-faces of _s_ is created. 
57
  - if _s_ is ComplexLike, then a sparse boundary matrix whose columns represent boundary chains is returned. 
58
  - if _s_ is FiltrationLike, then a sparse boundary matrix whose columns represent boundary chains in filtration order is returned.
59
  - if _s_ is none of the above but is Sized and Iterable, all len(s)-1 combinations are returned of _s_ are returned. 
60

61
  TODO: finish this
62
  """
UNCOV
63
  if hasattr(s, "boundary"):
×
64
    kwargs |= dict(p=p, oriented=oriented)
×
65
    return s.boundary(**kwargs)
×
66
  return combinations(s, len(s)-1)
×
67

68
## NOTE: Returning an *Iterable* instead of an *Iterator* is preferred, as it allows containers to 
69
## return lazy- or eager-proxy objects, e.g. tree iterators or entire numpy arrays 
70
def faces(s: Union[SimplexConvertible, ComplexLike], p: int = None, data: bool = False, **kwargs) -> Iterable[Union[SimplexConvertible, PropertySimplexConvertible]]:
3✔
71
  """
72
  Returns the faces of a simplicial object, optionally restricted by dimension.
73

74
  If _s_ has an existing method _s.faces(p)_, then that method is called with additional keyword arguments _kwargs_. 
75
  
76
  Otherwise, the behavior of this function depends on the type-class of _s_. Namely, 
77
  - if _s_ is SimplexLike, then a generator enumerating _p_-combinations of _s_ is returned. 
78
  - if _s_ is ComplexLike, then a generator enumerating _p_-faces of _s_ (in any order) is returned. 
79
  - if _s_ is FiltrationLike, then a generator enumerating _p_-faces of _s_ in filtration order is returned.
80
  - if _s_ is none of the above but is Sized and Iterable, all combinations of _s_ of length _p+1_ are chained and returned. 
81
  """
82
  kwargs |= dict(p=p, data=data)
3✔
83
  if hasattr(s, "faces"):
3✔
84
    return s.faces(**kwargs)
3✔
85
  elif is_complex_like(s):
3✔
86
    return unique_everseen(it.chain.from_iterable([faces(f, **kwargs) for f in s])) # handles data implicitly, though faces may not store data
3✔
87
    # if p is None:
88
    #   g = iter(sset)
89
    # else: 
90
    #   g = iter(filter(lambda s: len(s) == p+1, iter(sset)))
91
  elif is_filtration_like(s):
3✔
UNCOV
92
    if not data:
×
93
      return (f for i,f in s)
×
94
    else:
UNCOV
95
      return ((f, dict(index=i)) for i,f in s)
×
96
  elif is_simplex_like(s): # is simplex convertible
3✔
97
    k = len(s)
3✔
98
    if p is None:
3✔
99
      g = it.chain.from_iterable([combinations(s, k-i) for i in reversed(range(0, k))])
3✔
100
    else:
UNCOV
101
      assert isinstance(p, Integral), f"Invalid type {type(p)}; dimension 'p' must be integral type"
×
102
      g = iter(combinations(s, p+1))
×
103
    return zip_data(g, data)
3✔
104
  else:
UNCOV
105
    raise ValueError(f"Unknown type supplied '{type(s)}'")
×
106

107
def card(s: Union[SimplexConvertible, ComplexLike, FiltrationLike], p: int = None, **kwargs):
3✔
108
  """Counts the number of _p_-dimensional simplices of a simplicial object _s_. 
109
  
110
  If _s_ has an existing method _s.card(p)_, then that method is called with additional keyword arguments _kwargs_. 
111

112
  Otherwise, the behavior of this function depends on the type-class of _s_ and whether _p_ is specified. Namely, 
113
   - If _s_ is _complex like_, then card(s) returns a tuple containing the number of simplices in _s_ in each dimension, and _card(s, p)_ the number of simplices in _s_ with dimension p.
114
  """
115
  if hasattr(s, "card"):
3✔
116
    kwargs |= dict(p=p)
3✔
117
    return s.card(**kwargs)
3✔
118
  else:
119
    if p is None: 
3✔
120
      from collections import Counter
3✔
121
      cc = Counter([dim(s, **kwargs) for s in faces(s, p, **kwargs)])
3✔
122
      return tuple(cc.values())
3✔
123
    else: 
UNCOV
124
      assert isinstance(p, int)
×
UNCOV
125
      return int(sum([1 for s in faces(s, p, **kwargs) if dim(s, **kwargs) == p]))
×
126
    
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

© 2026 Coveralls, Inc