Skip to content

Commit 4cdb2eb

Browse files
authored
Merge pull request smartFCA#30 from EgorDudyrev/fix/bitarray3.0
Update code for the new version of bitarray package (v3.0.0)
2 parents 6b86c4a + 27e726a commit 4cdb2eb

File tree

7 files changed

+35
-37
lines changed

7 files changed

+35
-37
lines changed

caspailleur/base_functions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def extension(description: Union[Iterable[int], bitarray], crosses_per_columns:
4949
-> Union[set[int], bitarray]:
5050
"""Select the indices of rows described by `description`"""
5151
column_type = type(crosses_per_columns[0])
52-
description = description.itersearch(True) if isinstance(description, bitarray) else description
52+
description = description.search(True) if isinstance(description, bitarray) else description
5353

5454
total_extent = column_type(maximal_extent(crosses_per_columns))
5555
extent = reduce(column_type.__and__, (crosses_per_columns[attr_i] for attr_i in description), total_extent)
@@ -89,7 +89,7 @@ def closure(
8989
"""
9090
result = intention(extension(description, crosses_per_columns), crosses_per_columns)
9191
if not isinstance(description, bitarray) and isinstance(result, bitarray):
92-
return (i for i in result.itersearch(True))
92+
return (i for i in result.search(True)) # convert ba.searchiterator into an interator
9393
return result
9494

9595

@@ -105,4 +105,4 @@ def isets2bas(itemsets: Iterable[Iterable[int]], length: int) -> Iterator[fbarra
105105
--------
106106
isets2bas([ [0, 1], [1,3,4] ], 5) --> [bitarray('01000'), bitarray('01011')]
107107
"""
108-
return io.isets2bas(itemsets, )
108+
return io.isets2bas(itemsets, length)

caspailleur/indices.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def delta_stability_by_extents(extents: list[fbarray]) -> Iterator[int]:
3232
def delta_stability_by_description(
3333
description: Union[Iterable[int], bitarray], crosses_per_columns: list[fbarray], extent: fbarray = None
3434
) -> int:
35-
description = set(description.itersearch(True)) if isinstance(description, bitarray) else set(description)
35+
description = set(description.search(True)) if isinstance(description, bitarray) else set(description)
3636

3737
extent = extension(description, crosses_per_columns) if extent is None else extent
3838
out_attrs = [(m_i, m_ext) for m_i, m_ext in enumerate(crosses_per_columns) if m_i not in description]
@@ -115,14 +115,13 @@ def distributivity_index(
115115

116116
n_distr = n_trans_parents
117117

118-
for intent_idx, intent in tqdm(
119-
enumerate(intents), total=len(intents),
120-
disable=not use_tqdm, desc='enumerate intents'
121-
):
118+
intents_iterator = tqdm(enumerate(intents), total=len(intents), disable=not use_tqdm, desc='enumerate intents')
119+
for intent_idx, intent in intents_iterator:
122120
distr_ancestors = deque([
123121
(mother, father)
124-
for mother in parents[intent_idx].itersearch(True) for father in parents[intent_idx].itersearch(True)
125-
if mother < father and intents[mother] | intents[father] == intent
122+
for mother in parents[intent_idx].search(True)
123+
for father in parents[intent_idx].search(True, mother+1)
124+
if intents[mother] | intents[father] == intent
126125
])
127126

128127
visited_pairs = set()
@@ -138,11 +137,11 @@ def distributivity_index(
138137
mother_intent, father_intent = intents[mother], intents[father]
139138

140139
distr_ancestors.extend([
141-
(mother, gfather) for gfather in parents[father].itersearch(True)
140+
(mother, gfather) for gfather in parents[father].search(True)
142141
if mother_intent | intents[gfather] == intent
143142
])
144143
distr_ancestors.extend([
145-
(gmother, father) for gmother in parents[mother].itersearch(True)
144+
(gmother, father) for gmother in parents[mother].search(True)
146145
if intents[gmother] | father_intent == intent
147146
])
148147

caspailleur/io.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def bas2isets(bitarrays: Iterable[fbarray]) -> Iterator[FrozenSet[int]]:
7474
bas2isets([ bitarray('01000'), bitarray('01011') ]) --> [ [0,1], [1,3,4] ]
7575
"""
7676
for bar in bitarrays:
77-
yield frozenset(bar.itersearch(True))
77+
yield frozenset(bar.search(True))
7878

7979

8080
def to_named_itemsets(data: ContextType) -> NamedItemsetContextType:
@@ -428,7 +428,7 @@ def transpose_context(data: ContextType) -> ContextType:
428428
n_objs, n_attrs = len(crosses_data), len(crosses_data[0])
429429
transposed = [bazeros(n_objs) for _ in range(n_attrs)]
430430
for obj_i, ba in enumerate(crosses_data):
431-
for attr_i in ba.itersearch(True):
431+
for attr_i in ba.search(True):
432432
transposed[attr_i][obj_i] = True
433433
transposed = list(map(bitarray, transposed))
434434

@@ -475,7 +475,7 @@ def identify_supported_context_type(context: ContextType) -> Optional[typing.Typ
475475
def verbalise(description: Union[bitarray, Iterable[int]], names: list[str]) -> Iterable[str]:
476476
"""Convert every index i (or every True i-th element of a bitarray) into a human-readable string `names[i]`"""
477477
if isinstance(description, bitarray):
478-
return {names[i] for i in description.itersearch(True)}
478+
return {names[i] for i in description.search(True)}
479479

480480
if isinstance(description, Generator):
481481
return (names[i] for i in description)

caspailleur/mine_equivalence_classes.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def list_attribute_concepts(intents: list[fbarray]) -> list[int]:
157157
attr_concepts = [-1] * len(intents[0])
158158
found_attrs = bazeros(len(intents[0]))
159159
for intent_i, intent in enumerate(intents):
160-
for m in intent.itersearch(True):
160+
for m in intent.search(True):
161161
if attr_concepts[m] == -1:
162162
attr_concepts[m] = intent_i
163163
found_attrs |= intent
@@ -193,7 +193,7 @@ def iter_equivalence_class(attribute_extents: list[fbarray], intent: fbarray = N
193193

194194
total_extent = extension(intent, attribute_extents)
195195

196-
stack = [[m] for m in intent.itersearch(True)][::-1]
196+
stack = [[m] for m in intent.search(True, right=True)]
197197

198198
yield intent
199199
while stack:
@@ -211,7 +211,7 @@ def iter_equivalence_class(attribute_extents: list[fbarray], intent: fbarray = N
211211

212212
# conj == total_extent
213213
yield attrs_to_eval
214-
stack += [attrs_to_remove+[m] for m in intent.itersearch(True) if m > last_attr][::-1]
214+
stack += [attrs_to_remove+[m] for m in intent.search(True, last_attr+1, right=True)]
215215

216216

217217
def iter_equivalence_class_levelwise(
@@ -320,7 +320,7 @@ def iter_keys_of_intent(intent: fbarray, attr_extents: list[fbarray], support_su
320320
support = extent.count()
321321

322322
def subdescriptions(description: fbarray) -> Iterator[fbarray]:
323-
return (description & ~single_attrs[m_i] for m_i in description.itersearch(True))
323+
return (description & ~single_attrs[m_i] for m_i in description.search(True))
324324

325325
key_candidates = deque([intent])
326326
while key_candidates:
@@ -385,7 +385,7 @@ def list_keys(intents: list[fbarray], only_passkeys: bool = False) -> dict[fbarr
385385
n_attrs, n_intents = len(intents[-1]), len(intents)
386386
attrs_descendants = [bazeros(n_intents) for _ in range(n_attrs)]
387387
for intent_i, intent in enumerate(intents):
388-
for m in intent.itersearch(True):
388+
for m in intent.search(True):
389389
attrs_descendants[m][intent_i] = True
390390

391391
# assuming that every subset of a key is a key => extending not-a-key cannot result in a key
@@ -400,13 +400,13 @@ def list_keys(intents: list[fbarray], only_passkeys: bool = False) -> dict[fbarr
400400
attrs_to_test = deque([m_ba for m_ba in single_attrs])
401401
while attrs_to_test:
402402
attrs = attrs_to_test.popleft()
403-
attrs_indices = list(attrs.itersearch(True))
403+
attrs_indices = list(attrs.search(True))
404404

405405
if any(attrs & (~single_attrs[m]) not in keys_dict for m in attrs_indices):
406406
continue
407407

408408
common_descendants = ~bazeros(n_intents)
409-
for m in attrs.itersearch(True):
409+
for m in attrs.search(True):
410410
common_descendants &= attrs_descendants[m]
411411
if not common_descendants.any():
412412
continue
@@ -464,7 +464,7 @@ def list_keys_for_extents(
464464
testing_stack = deque(single_attrs) # iteration order is inspired by Talky-G algorithm
465465
while testing_stack:
466466
attrs = testing_stack.pop()
467-
attrs_indices = list(attrs.itersearch(True))
467+
attrs_indices = list(attrs.search(True))
468468
sub_descriptions = [attrs & (~single_attrs[m]) for m in attrs_indices]
469469

470470
# check that every subset of attrs is a key

caspailleur/order.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def topological_sorting(elements: list[fbarray], ascending: bool = True) -> tupl
2323
THe mapping from the original indices to the indices of topologically sorted array.
2424
"""
2525
ascending = 1 if ascending else -1
26-
ars_topsort = sorted(elements, key=lambda el: (el.count() * ascending, tuple(el.itersearch(True))))
26+
ars_topsort = sorted(elements, key=lambda el: (el.count() * ascending, tuple(el.search(True))))
2727

2828
el_idx_map = {el: i for i, el in enumerate(ars_topsort)}
2929
orig_to_topsort_indices_map = [el_idx_map[el] for el in elements]
@@ -44,7 +44,7 @@ def inverse_order(order: List[fbarray]) -> List[fbarray]:
4444
"""Reverse the given order. So that new_order[i][j] = order[j][i]"""
4545
inversed = [bazeros(len(order[0])) for _ in order]
4646
for el_i, ordered in enumerate(order):
47-
for el_j in ordered.itersearch(True):
47+
for el_j in ordered.search(True):
4848
inversed[el_j][el_i] = True
4949

5050
return [fbarray(ordered) for ordered in inversed]
@@ -67,25 +67,25 @@ def sort_intents_inclusion(intents: List[fbarray], use_tqdm=False, return_transi
6767

6868
attrs_descendants = [bitarray(zero_intents) for _ in range(n_attrs)]
6969
for intent_i, intent in enumerate(intents):
70-
for m in intent.itersearch(True):
70+
for m in intent.search(True):
7171
attrs_descendants[m][intent_i] = True
7272

7373
for intent_i in tqdm(range(n_intents-1, -1, -1), disable=not use_tqdm, desc='Sorting intents'):
7474
intent = intents[intent_i]
7575

7676
common_descendants = bitarray(~zero_intents)
77-
for m in intent.itersearch(True):
77+
for m in intent.search(True):
7878
common_descendants &= attrs_descendants[m]
7979

8080
children = bitarray(zero_intents)
81-
for new_m in (all_attrs & ~intent).itersearch(True):
81+
for new_m in (all_attrs & ~intent).search(True):
8282
meet_idx = (common_descendants & attrs_descendants[new_m]).find(True)
8383
if meet_idx == -1: # no meet found
8484
continue
8585
children[meet_idx] = True
8686

8787
trans_children = bitarray(zero_intents)
88-
for child in children.itersearch(True):
88+
for child in children.search(True):
8989
trans_children |= trans_lattice[child]
9090
trans_lattice[intent_i] = fbarray(children | trans_children)
9191
lattice[intent_i] = fbarray(children & ~trans_children)
@@ -101,14 +101,14 @@ def close_transitive_subsumption(subsumption_list: List[fbarray]) -> List[fbarra
101101
So that in the returned list `trans_subsumption_list`,
102102
`trans_subsumption_list[i]` contains indices of all intents, smaller that intent with index `i`
103103
"""
104-
assert all([max(subsumed.itersearch(True)) < i for i, subsumed in enumerate(subsumption_list) if subsumed.any()]), \
104+
assert all([subsumed.index(True, right=True) < i for i, subsumed in enumerate(subsumption_list) if subsumed.any()]), \
105105
"`subsumption_list` relation should be defined on a list, topologically sorted by descending order." \
106106
" So all subsumption_list of element `i` should have smaller indices"
107107

108108
trans_subsumption_list = []
109109
for subsumed in subsumption_list:
110110
trans_subsumed = bitarray(subsumed)
111-
for el_i in subsumed.itersearch(True):
111+
for el_i in subsumed.search(True):
112112
trans_subsumed |= trans_subsumption_list[el_i]
113113
trans_subsumption_list.append(fbarray(trans_subsumed))
114114
return trans_subsumption_list
@@ -120,16 +120,15 @@ def open_transitive_subsumption(trans_subsumption_list: List[fbarray]) -> List[f
120120
So that in the returned list `subsumption_list`,
121121
`subsumption_list[i]` contains indices of the biggest intents, smaller that intent with index `i`
122122
"""
123-
assert all([
124-
max(subsumed.itersearch(True)) < i for i, subsumed in enumerate(trans_subsumption_list) if subsumed.any()
125-
]),\
123+
assert all([subsumed.index(True, right=True) < i for i, subsumed in enumerate(trans_subsumption_list)
124+
if subsumed.any()]), \
126125
"`trans_subsumption_list` relation should be defined on a list, topologically sorted by descending order." \
127126
" So all trans_subsumption_list of element `i` should have smaller indices"
128127

129128
subsumption_list = []
130129
for trans_subsumed in trans_subsumption_list:
131130
subsumed = bitarray(trans_subsumed)
132-
for el_i in list(subsumed.itersearch(True))[::-1]:
131+
for el_i in subsumed.search(True, right=True):
133132
if subsumed[el_i]:
134133
subsumed &= ~trans_subsumption_list[el_i]
135134
subsumption_list.append(fbarray(subsumed))

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ classifiers = [
2020
]
2121
keywords = ["python", "fca", "itemset-mining"]
2222
dependencies = [
23-
'numpy>=1.20', 'scikit-mine>=1', 'bitarray>=2.5.1, <3', 'tqdm', 'deprecation'
23+
'numpy>=1.20', 'scikit-mine>=1', 'bitarray>=3.0.0', 'tqdm', 'deprecation'
2424
]
2525
requires-python = ">=3.8"
2626

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
# pip-compile pyproject.toml
66
#
7-
bitarray==2.7.3
7+
bitarray==3.0.0
88
# via caspailleur (pyproject.toml)
99
contourpy==1.0.7
1010
# via matplotlib

0 commit comments

Comments
 (0)