cbrkit 0.26.3__tar.gz → 0.26.4__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.
Files changed (67) hide show
  1. {cbrkit-0.26.3 → cbrkit-0.26.4}/PKG-INFO +1 -1
  2. {cbrkit-0.26.3 → cbrkit-0.26.4}/pyproject.toml +1 -1
  3. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/astar.py +74 -46
  4. {cbrkit-0.26.3 → cbrkit-0.26.4}/README.md +0 -0
  5. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/__init__.py +0 -0
  6. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/__main__.py +0 -0
  7. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/adapt/__init__.py +0 -0
  8. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/adapt/attribute_value.py +0 -0
  9. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/adapt/generic.py +0 -0
  10. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/adapt/numbers.py +0 -0
  11. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/adapt/strings.py +0 -0
  12. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/api.py +0 -0
  13. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/cli.py +0 -0
  14. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/constants.py +0 -0
  15. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/cycle.py +0 -0
  16. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/dumpers.py +0 -0
  17. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/eval/__init__.py +0 -0
  18. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/eval/common.py +0 -0
  19. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/eval/retrieval.py +0 -0
  20. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/helpers.py +0 -0
  21. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/loaders.py +0 -0
  22. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/model/__init__.py +0 -0
  23. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/model/graph.py +0 -0
  24. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/model/result.py +0 -0
  25. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/py.typed +0 -0
  26. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/retrieval/__init__.py +0 -0
  27. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/retrieval/apply.py +0 -0
  28. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/retrieval/build.py +0 -0
  29. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/retrieval/rerank.py +0 -0
  30. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/reuse/__init__.py +0 -0
  31. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/reuse/apply.py +0 -0
  32. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/reuse/build.py +0 -0
  33. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/__init__.py +0 -0
  34. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/aggregator.py +0 -0
  35. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/attribute_value.py +0 -0
  36. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/collections.py +0 -0
  37. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/embed.py +0 -0
  38. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/generic.py +0 -0
  39. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/__init__.py +0 -0
  40. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/alignment.py +0 -0
  41. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/brute_force.py +0 -0
  42. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/common.py +0 -0
  43. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/dfs.py +0 -0
  44. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/greedy.py +0 -0
  45. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/lap.py +0 -0
  46. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/precompute.py +0 -0
  47. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/qap.py +0 -0
  48. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/graphs/vf2.py +0 -0
  49. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/numbers.py +0 -0
  50. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/strings.py +0 -0
  51. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/taxonomy.py +0 -0
  52. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/sim/wrappers.py +0 -0
  53. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/__init__.py +0 -0
  54. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/apply.py +0 -0
  55. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/build.py +0 -0
  56. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/model.py +0 -0
  57. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/prompts.py +0 -0
  58. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/__init__.py +0 -0
  59. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/anthropic.py +0 -0
  60. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/cohere.py +0 -0
  61. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/google.py +0 -0
  62. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/instructor.py +0 -0
  63. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/model.py +0 -0
  64. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/ollama.py +0 -0
  65. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/openai.py +0 -0
  66. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/synthesis/providers/wrappers.py +0 -0
  67. {cbrkit-0.26.3 → cbrkit-0.26.4}/src/cbrkit/typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: cbrkit
3
- Version: 0.26.3
3
+ Version: 0.26.4
4
4
  Summary: Customizable Case-Based Reasoning (CBR) toolkit for Python with a built-in API and CLI
5
5
  Keywords: cbr,case-based reasoning,api,similarity,nlp,retrieval,cli,tool,library
6
6
  Author: Mirko Lenz
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cbrkit"
3
- version = "0.26.3"
3
+ version = "0.26.4"
4
4
  description = "Customizable Case-Based Reasoning (CBR) toolkit for Python with a built-in API and CLI"
5
5
  authors = [{ name = "Mirko Lenz", email = "mirko@mirkolenz.com" }]
6
6
  readme = "README.md"
@@ -1,7 +1,7 @@
1
1
  import heapq
2
- from collections.abc import Mapping
2
+ from collections.abc import Collection, Mapping
3
3
  from dataclasses import dataclass, field
4
- from typing import Protocol
4
+ from typing import Any, Protocol, cast
5
5
 
6
6
  from ...helpers import (
7
7
  get_logger,
@@ -34,6 +34,33 @@ __all__ = [
34
34
  logger = get_logger(__name__)
35
35
 
36
36
 
37
+ def next_elem[K](elements: Collection[K]) -> K:
38
+ """Select the next element from a set deterministically.
39
+
40
+ If elements are sortable, returns the smallest one.
41
+ Otherwise, returns the first element from iteration.
42
+
43
+ Args:
44
+ elements: Set of elements to choose from
45
+
46
+ Returns:
47
+ A single element from the set
48
+
49
+ Raises:
50
+ ValueError: If the set is empty
51
+ """
52
+ if not elements:
53
+ raise ValueError("Cannot select from empty set")
54
+
55
+ if len(elements) == 1:
56
+ return next(iter(elements))
57
+
58
+ try:
59
+ return min(cast(Collection[Any], elements))
60
+ except TypeError:
61
+ return next(iter(elements))
62
+
63
+
37
64
  @dataclass(slots=True, frozen=True, order=True)
38
65
  class PriorityState[K]:
39
66
  priority: float
@@ -157,15 +184,11 @@ class select1[K, N, E, G](SelectionFunc[K, N, E, G]):
157
184
  ) -> None | tuple[K, GraphElementType]:
158
185
  """Select the next node or edge to be mapped"""
159
186
 
160
- try:
161
- return next(iter(s.open_y_nodes)), "node"
162
- except StopIteration:
163
- pass
187
+ if s.open_y_nodes:
188
+ return next_elem(s.open_y_nodes), "node"
164
189
 
165
- try:
166
- return next(iter(s.open_y_edges)), "edge"
167
- except StopIteration:
168
- pass
190
+ if s.open_y_edges:
191
+ return next_elem(s.open_y_edges), "edge"
169
192
 
170
193
  return None
171
194
 
@@ -182,20 +205,18 @@ class select2[K, N, E, G](SelectionFunc[K, N, E, G]):
182
205
  ) -> None | tuple[K, GraphElementType]:
183
206
  """Select the next node or edge to be mapped"""
184
207
 
185
- try:
186
- return next(
187
- key
188
- for key in s.open_y_edges
189
- if y.edges[key].source.key not in s.open_y_nodes
190
- and y.edges[key].target.key not in s.open_y_nodes
191
- ), "edge"
192
- except StopIteration:
193
- pass
208
+ edge_candidates = {
209
+ key
210
+ for key in s.open_y_edges
211
+ if y.edges[key].source.key not in s.open_y_nodes
212
+ and y.edges[key].target.key not in s.open_y_nodes
213
+ }
194
214
 
195
- try:
196
- return next(iter(s.open_y_nodes)), "node"
197
- except StopIteration:
198
- pass
215
+ if edge_candidates:
216
+ return next_elem(edge_candidates), "edge"
217
+
218
+ if s.open_y_nodes:
219
+ return next_elem(s.open_y_nodes), "node"
199
220
 
200
221
  return None
201
222
 
@@ -246,19 +267,36 @@ class select3[K, N, E, G](SelectionFunc[K, N, E, G]):
246
267
 
247
268
  if not heuristic_scores:
248
269
  # Fallback: select any remaining node or edge for null mapping
270
+ # Use sorted to ensure deterministic selection
249
271
  if s.open_y_nodes:
250
- return next(iter(s.open_y_nodes)), "node"
272
+ return next_elem(s.open_y_nodes), "node"
251
273
  elif s.open_y_edges:
252
- return next(iter(s.open_y_edges)), "edge"
274
+ return next_elem(s.open_y_edges), "edge"
253
275
  return None
254
276
 
277
+ # Find the maximum heuristic score
255
278
  max_score = max(heuristic_scores.values())
256
- best_selections = [
279
+ best_selections = {
257
280
  key for key, value in heuristic_scores.items() if value == max_score
258
- ]
281
+ }
259
282
 
260
283
  # if multiple selections have the same score, select the one with the lowest number of possible mappings
261
- best_selection = min(best_selections, key=lambda key: mapping_options[key])
284
+ if len(best_selections) > 1:
285
+ min_mapping_options = min(mapping_options[key] for key in best_selections)
286
+ best_selections = {
287
+ key
288
+ for key in best_selections
289
+ if mapping_options[key] == min_mapping_options
290
+ }
291
+
292
+ # select the one with the lowest key
293
+ try:
294
+ best_selection = min(
295
+ best_selections,
296
+ key=lambda item: cast(Any, item[0]),
297
+ )
298
+ except TypeError:
299
+ best_selection = next(iter(best_selections))
262
300
 
263
301
  selection_key, selection_type = best_selection
264
302
 
@@ -290,7 +328,7 @@ class build[K, N, E, G](
290
328
  beam_width: Limits the queue size which prunes the search space.
291
329
  This leads to a faster search and less memory usage but also introduces a similarity error.
292
330
  Disabled by default. Based on [Neuhaus et al. (2006)](https://doi.org/10.1007/11815921_17).
293
- pathlength_weight: Add a penalty for states with few mapped elements that already have a low similarity.
331
+ pathlength_weight: Favor long partial edit paths over shorter ones.
294
332
  Disabled by default. Based on [Neuhaus et al. (2006)](https://doi.org/10.1007/11815921_17).
295
333
 
296
334
  Returns:
@@ -356,22 +394,11 @@ class build[K, N, E, G](
356
394
  prio = 1 - (past_sim + future_sim)
357
395
 
358
396
  if self.pathlength_weight > 0:
359
- node_null_mapping = (
360
- set(y.nodes.keys())
361
- - set(state.node_mapping.keys())
362
- - set(state.open_y_nodes)
363
- )
364
- edge_null_mapping = (
365
- set(y.edges.keys())
366
- - set(state.edge_mapping.keys())
367
- - set(state.open_y_edges)
368
- )
369
- num_paths = (
370
- len(state.node_mapping)
371
- + len(state.edge_mapping)
372
- + len(node_null_mapping)
373
- + len(edge_null_mapping)
374
- )
397
+ # Calculate the number of mapping decisions made so far (partial edit path length)
398
+ # This includes actual mappings plus null mappings (elements processed but not mapped)
399
+ total_y_elements = len(y.nodes) + len(y.edges)
400
+ open_y_elements = len(state.open_y_nodes) + len(state.open_y_edges)
401
+ num_paths = total_y_elements - open_y_elements
375
402
  return prio / (self.pathlength_weight**num_paths)
376
403
 
377
404
  return prio
@@ -432,7 +459,8 @@ class build[K, N, E, G](
432
459
  heapq.heappush(open_set, PriorityState(next_prio, next_state))
433
460
 
434
461
  if self.beam_width > 0 and len(open_set) > self.beam_width:
435
- open_set = open_set[: self.beam_width]
462
+ open_set = heapq.nsmallest(self.beam_width, open_set)
463
+ heapq.heapify(open_set)
436
464
 
437
465
  return self.similarity(
438
466
  x,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes