pudo-sequences 0.1.0__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.
@@ -0,0 +1,11 @@
1
+ .ipynb_checkpoints/
2
+ .pytest_cache/
3
+ __pycache__/
4
+ notebooks/data/
5
+ *.egg-info/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ build/
10
+ dist/
11
+ archive/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Breno Beirigo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,336 @@
1
+ Metadata-Version: 2.4
2
+ Name: pudo-sequences
3
+ Version: 0.1.0
4
+ Summary: Generate, count, and index valid pickup/drop-off event sequences.
5
+ Project-URL: Homepage, https://github.com/brenobeirigo/pudo-sequences
6
+ Project-URL: Repository, https://github.com/brenobeirigo/pudo-sequences
7
+ Project-URL: Issues, https://github.com/brenobeirigo/pudo-sequences/issues
8
+ Author: Breno Beirigo
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: combinatorics,paired-events,pickup-and-delivery,pickup-dropoff,precedence-constraints,topological-sorts
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.10
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.2; extra == 'dev'
28
+ Requires-Dist: networkx>=3.0; extra == 'dev'
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Requires-Dist: twine>=5.0; extra == 'dev'
31
+ Provides-Extra: networkx
32
+ Requires-Dist: networkx>=3.0; extra == 'networkx'
33
+ Provides-Extra: test
34
+ Requires-Dist: networkx>=3.0; extra == 'test'
35
+ Requires-Dist: pytest>=8.0; extra == 'test'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # pudo-sequences
39
+
40
+ `pudo-sequences` is a small Python package for PU/DO constrained sequence
41
+ combinatorics: counting, generating, and indexing event orders where every
42
+ pickup must occur before its paired drop-off.
43
+
44
+ ```text
45
+ pickup_i before dropoff_i
46
+ ```
47
+
48
+ Use it when a routing, ridesharing, dispatching, simulation, optimization, or
49
+ learning system needs to reason about feasible local service orders before
50
+ evaluating distance, time windows, capacity, cost, or reward.
51
+
52
+ ## Why It Matters
53
+
54
+ A PU/DO route is not just a permutation of events. For two new requests, the
55
+ four events are:
56
+
57
+ ```text
58
+ 0 = pickup request 0
59
+ 1 = pickup request 1
60
+ 2 = drop-off request 0
61
+ 3 = drop-off request 1
62
+ ```
63
+
64
+ `itertools.permutations` returns all `24` event orders, including impossible
65
+ orders such as `(2, 0, 1, 3)`, where request `0` is dropped off before it is
66
+ picked up.
67
+
68
+ ```python
69
+ from itertools import permutations
70
+
71
+ from pudo_sequences import pudo_sequences
72
+
73
+ all_orders = list(permutations([0, 1, 2, 3]))
74
+ valid_orders = pudo_sequences([0, 1])
75
+
76
+ print(len(all_orders))
77
+ print(len(valid_orders))
78
+ ```
79
+
80
+ ```python
81
+ 24
82
+ 6
83
+ ```
84
+
85
+ The valid orders are:
86
+
87
+ ```python
88
+ [
89
+ (0, 1, 2, 3),
90
+ (0, 1, 3, 2),
91
+ (0, 2, 1, 3),
92
+ (1, 0, 2, 3),
93
+ (1, 0, 3, 2),
94
+ (1, 3, 0, 2),
95
+ ]
96
+ ```
97
+
98
+ You can filter normal permutations yourself:
99
+
100
+ ```python
101
+ def is_valid(route):
102
+ return route.index(0) < route.index(2) and route.index(1) < route.index(3)
103
+
104
+
105
+ filtered = [route for route in all_orders if is_valid(route)]
106
+
107
+ assert filtered == valid_orders
108
+ ```
109
+
110
+ For small teaching examples that is fine. In real local-search or learning
111
+ loops, generating impossible actions first adds noise and waste. The gap grows
112
+ quickly:
113
+
114
+ | Requests | Unconstrained permutations | Valid PU/DO sequences | Valid share |
115
+ | ---: | ---: | ---: | ---: |
116
+ | 1 | 2 | 1 | 50.0% |
117
+ | 2 | 24 | 6 | 25.0% |
118
+ | 3 | 720 | 90 | 12.5% |
119
+ | 4 | 40,320 | 2,520 | 6.25% |
120
+ | 5 | 3,628,800 | 113,400 | 3.125% |
121
+
122
+ The package focuses on this first layer:
123
+
124
+ ```text
125
+ PU/DO combinatorics: which event orders are logically possible?
126
+ Domain evaluation: which of those orders satisfy time, capacity, or cost rules?
127
+ Selection: which feasible order should the system choose?
128
+ ```
129
+
130
+ It is not a routing solver. It gives you the constrained local sequence space
131
+ that a solver, simulator, heuristic, or policy can evaluate.
132
+
133
+ ## Install
134
+
135
+ ```bash
136
+ pip install pudo-sequences
137
+ ```
138
+
139
+ For local development:
140
+
141
+ ```bash
142
+ pip install -e ".[dev]"
143
+ pytest
144
+ ```
145
+
146
+ The core package has no runtime dependency beyond Python. The optional
147
+ `topological` strategy requires NetworkX:
148
+
149
+ ```bash
150
+ pip install "pudo-sequences[networkx]"
151
+ ```
152
+
153
+ ## Quick Start
154
+
155
+ ```python
156
+ from pudo_sequences import count_pudo_sequences, pudo_sequences
157
+
158
+ print(count_pudo_sequences(2))
159
+ print(pudo_sequences([0, 1]))
160
+ ```
161
+
162
+ With the default integer convention, pickup `i` maps to drop-off `i + n`, where
163
+ `n` is the number of requests. For `requests=[0, 1]`, drop-offs are `2` and
164
+ `3`.
165
+
166
+ To stream instead of materializing every route:
167
+
168
+ ```python
169
+ from pudo_sequences import iter_pudo_sequences
170
+
171
+ best_route = None
172
+ best_score = float("inf")
173
+
174
+ for route in iter_pudo_sequences([0, 1, 2]):
175
+ score = sum(route) # Replace with distance, time, reward, or feasibility logic.
176
+ if score < best_score:
177
+ best_route = route
178
+ best_score = score
179
+ ```
180
+
181
+ To check whether a candidate from a heuristic is PU/DO-valid, compare it with
182
+ the generated local action set for small neighborhoods:
183
+
184
+ ```python
185
+ from pudo_sequences import pudo_sequences
186
+
187
+ valid_routes = set(pudo_sequences([0, 1, 2]))
188
+
189
+ assert (0, 3, 1, 4, 2, 5) in valid_routes
190
+ assert (3, 0, 1, 4, 2, 5) not in valid_routes
191
+ ```
192
+
193
+ ## Custom Labels
194
+
195
+ Use `dropoff_for` when events are not integers:
196
+
197
+ ```python
198
+ from pudo_sequences import pudo_sequences
199
+
200
+ routes = pudo_sequences(
201
+ ["alice", "bob"],
202
+ dropoff_for=lambda pickup: ("dropoff", pickup),
203
+ )
204
+
205
+ assert (
206
+ "alice",
207
+ ("dropoff", "alice"),
208
+ "bob",
209
+ ("dropoff", "bob"),
210
+ ) in routes
211
+ ```
212
+
213
+ By requiring a mapping from pickup labels to drop-off labels, the package can
214
+ use the same combinatorics for rider IDs, job IDs, task names, tuples, or other
215
+ hashable labels.
216
+
217
+ ## Already-Open Drop-Offs
218
+
219
+ Sometimes a planning horizon starts with requests already onboard. Their
220
+ drop-offs have no pickup inside the local sequence, so they may appear anywhere.
221
+
222
+ ```python
223
+ from pudo_sequences import count_pudo_sequences, pudo_sequences
224
+
225
+ routes = pudo_sequences([0, 1], open_dropoffs=[4])
226
+
227
+ assert len(routes) == count_pudo_sequences(2, n_open_dropoffs=1)
228
+ assert len(routes) == 30
229
+ assert (4, 0, 1, 2, 3) in routes
230
+ assert (0, 1, 2, 3, 4) in routes
231
+ ```
232
+
233
+ This represents two new requests plus one already-open request whose drop-off
234
+ is `4`.
235
+
236
+ ## Counts
237
+
238
+ For `n` labeled pickup/drop-off pairs, the number of valid sequences is:
239
+
240
+ ```text
241
+ (2n)! / 2^n
242
+ ```
243
+
244
+ Equivalently:
245
+
246
+ ```text
247
+ n! * (1 * 3 * 5 * ... * (2n - 1))
248
+ ```
249
+
250
+ If there are also `m` already-open drop-offs, the count becomes:
251
+
252
+ ```text
253
+ (2n + m)! / 2^n
254
+ ```
255
+
256
+ API:
257
+
258
+ ```python
259
+ from pudo_sequences import (
260
+ count_fixed_pickup_order_dropoff_insertions,
261
+ count_open_dropoff_insertions,
262
+ count_pudo_sequences,
263
+ )
264
+
265
+ assert count_pudo_sequences(3) == 90
266
+ assert count_pudo_sequences(3, n_open_dropoffs=2) == 5040
267
+ assert count_fixed_pickup_order_dropoff_insertions(3) == 15
268
+ assert count_open_dropoff_insertions(3, 2) == 56
269
+ ```
270
+
271
+ The constructive count is useful for explaining the structure:
272
+
273
+ ```text
274
+ 1. choose a pickup order;
275
+ 2. insert each paired drop-off only after its pickup;
276
+ 3. optionally insert already-open drop-offs anywhere.
277
+ ```
278
+
279
+ ## Generation Strategies
280
+
281
+ The default strategy is dependency-free DFS over the PU/DO state space:
282
+
283
+ ```python
284
+ from pudo_sequences import iter_pudo_sequences
285
+
286
+ for route in iter_pudo_sequences([0, 1, 2], strategy="dfs"):
287
+ pass
288
+ ```
289
+
290
+ Other strategies are available for comparison:
291
+
292
+ - `strategy="insertion"`: generate pickup orders, then insert each paired
293
+ drop-off only after its pickup.
294
+ - `strategy="topological"`: use NetworkX to enumerate all topological orders of
295
+ a precedence graph with edges `pickup_i -> dropoff_i`.
296
+
297
+ For `n=3`, all strategies represent the same constrained sequence set:
298
+
299
+ ```python
300
+ from pudo_sequences import pudo_sequences
301
+
302
+ dfs_routes = set(pudo_sequences([0, 1, 2], strategy="dfs"))
303
+ insertion_routes = set(pudo_sequences([0, 1, 2], strategy="insertion"))
304
+
305
+ assert dfs_routes == insertion_routes
306
+ ```
307
+
308
+ The generator is intended for small local neighborhoods. PU/DO sequence counts
309
+ grow factorially, so large request sets should usually be handled by heuristics,
310
+ optimization models, or sampling rather than full enumeration.
311
+
312
+ ## Prefix Indexing
313
+
314
+ When a caller repeatedly asks for valid continuations after a partial route,
315
+ build a prefix index:
316
+
317
+ ```python
318
+ from pudo_sequences import build_pudo_index
319
+
320
+ index = build_pudo_index([0, 1])
321
+
322
+ assert index.continuations([0, 1]) == {(2, 3), (3, 2)}
323
+ assert index.continuations([0, 2]) == {(1, 3)}
324
+ ```
325
+
326
+ The index is a secondary feature. Its purpose is fast continuation lookup, not
327
+ the definition of the package.
328
+
329
+ ## Development
330
+
331
+ ```bash
332
+ pip install -e ".[dev]"
333
+ pytest -q
334
+ python -m build --sdist --wheel
335
+ python -m twine check dist/*
336
+ ```
@@ -0,0 +1,299 @@
1
+ # pudo-sequences
2
+
3
+ `pudo-sequences` is a small Python package for PU/DO constrained sequence
4
+ combinatorics: counting, generating, and indexing event orders where every
5
+ pickup must occur before its paired drop-off.
6
+
7
+ ```text
8
+ pickup_i before dropoff_i
9
+ ```
10
+
11
+ Use it when a routing, ridesharing, dispatching, simulation, optimization, or
12
+ learning system needs to reason about feasible local service orders before
13
+ evaluating distance, time windows, capacity, cost, or reward.
14
+
15
+ ## Why It Matters
16
+
17
+ A PU/DO route is not just a permutation of events. For two new requests, the
18
+ four events are:
19
+
20
+ ```text
21
+ 0 = pickup request 0
22
+ 1 = pickup request 1
23
+ 2 = drop-off request 0
24
+ 3 = drop-off request 1
25
+ ```
26
+
27
+ `itertools.permutations` returns all `24` event orders, including impossible
28
+ orders such as `(2, 0, 1, 3)`, where request `0` is dropped off before it is
29
+ picked up.
30
+
31
+ ```python
32
+ from itertools import permutations
33
+
34
+ from pudo_sequences import pudo_sequences
35
+
36
+ all_orders = list(permutations([0, 1, 2, 3]))
37
+ valid_orders = pudo_sequences([0, 1])
38
+
39
+ print(len(all_orders))
40
+ print(len(valid_orders))
41
+ ```
42
+
43
+ ```python
44
+ 24
45
+ 6
46
+ ```
47
+
48
+ The valid orders are:
49
+
50
+ ```python
51
+ [
52
+ (0, 1, 2, 3),
53
+ (0, 1, 3, 2),
54
+ (0, 2, 1, 3),
55
+ (1, 0, 2, 3),
56
+ (1, 0, 3, 2),
57
+ (1, 3, 0, 2),
58
+ ]
59
+ ```
60
+
61
+ You can filter normal permutations yourself:
62
+
63
+ ```python
64
+ def is_valid(route):
65
+ return route.index(0) < route.index(2) and route.index(1) < route.index(3)
66
+
67
+
68
+ filtered = [route for route in all_orders if is_valid(route)]
69
+
70
+ assert filtered == valid_orders
71
+ ```
72
+
73
+ For small teaching examples that is fine. In real local-search or learning
74
+ loops, generating impossible actions first adds noise and waste. The gap grows
75
+ quickly:
76
+
77
+ | Requests | Unconstrained permutations | Valid PU/DO sequences | Valid share |
78
+ | ---: | ---: | ---: | ---: |
79
+ | 1 | 2 | 1 | 50.0% |
80
+ | 2 | 24 | 6 | 25.0% |
81
+ | 3 | 720 | 90 | 12.5% |
82
+ | 4 | 40,320 | 2,520 | 6.25% |
83
+ | 5 | 3,628,800 | 113,400 | 3.125% |
84
+
85
+ The package focuses on this first layer:
86
+
87
+ ```text
88
+ PU/DO combinatorics: which event orders are logically possible?
89
+ Domain evaluation: which of those orders satisfy time, capacity, or cost rules?
90
+ Selection: which feasible order should the system choose?
91
+ ```
92
+
93
+ It is not a routing solver. It gives you the constrained local sequence space
94
+ that a solver, simulator, heuristic, or policy can evaluate.
95
+
96
+ ## Install
97
+
98
+ ```bash
99
+ pip install pudo-sequences
100
+ ```
101
+
102
+ For local development:
103
+
104
+ ```bash
105
+ pip install -e ".[dev]"
106
+ pytest
107
+ ```
108
+
109
+ The core package has no runtime dependency beyond Python. The optional
110
+ `topological` strategy requires NetworkX:
111
+
112
+ ```bash
113
+ pip install "pudo-sequences[networkx]"
114
+ ```
115
+
116
+ ## Quick Start
117
+
118
+ ```python
119
+ from pudo_sequences import count_pudo_sequences, pudo_sequences
120
+
121
+ print(count_pudo_sequences(2))
122
+ print(pudo_sequences([0, 1]))
123
+ ```
124
+
125
+ With the default integer convention, pickup `i` maps to drop-off `i + n`, where
126
+ `n` is the number of requests. For `requests=[0, 1]`, drop-offs are `2` and
127
+ `3`.
128
+
129
+ To stream instead of materializing every route:
130
+
131
+ ```python
132
+ from pudo_sequences import iter_pudo_sequences
133
+
134
+ best_route = None
135
+ best_score = float("inf")
136
+
137
+ for route in iter_pudo_sequences([0, 1, 2]):
138
+ score = sum(route) # Replace with distance, time, reward, or feasibility logic.
139
+ if score < best_score:
140
+ best_route = route
141
+ best_score = score
142
+ ```
143
+
144
+ To check whether a candidate from a heuristic is PU/DO-valid, compare it with
145
+ the generated local action set for small neighborhoods:
146
+
147
+ ```python
148
+ from pudo_sequences import pudo_sequences
149
+
150
+ valid_routes = set(pudo_sequences([0, 1, 2]))
151
+
152
+ assert (0, 3, 1, 4, 2, 5) in valid_routes
153
+ assert (3, 0, 1, 4, 2, 5) not in valid_routes
154
+ ```
155
+
156
+ ## Custom Labels
157
+
158
+ Use `dropoff_for` when events are not integers:
159
+
160
+ ```python
161
+ from pudo_sequences import pudo_sequences
162
+
163
+ routes = pudo_sequences(
164
+ ["alice", "bob"],
165
+ dropoff_for=lambda pickup: ("dropoff", pickup),
166
+ )
167
+
168
+ assert (
169
+ "alice",
170
+ ("dropoff", "alice"),
171
+ "bob",
172
+ ("dropoff", "bob"),
173
+ ) in routes
174
+ ```
175
+
176
+ By requiring a mapping from pickup labels to drop-off labels, the package can
177
+ use the same combinatorics for rider IDs, job IDs, task names, tuples, or other
178
+ hashable labels.
179
+
180
+ ## Already-Open Drop-Offs
181
+
182
+ Sometimes a planning horizon starts with requests already onboard. Their
183
+ drop-offs have no pickup inside the local sequence, so they may appear anywhere.
184
+
185
+ ```python
186
+ from pudo_sequences import count_pudo_sequences, pudo_sequences
187
+
188
+ routes = pudo_sequences([0, 1], open_dropoffs=[4])
189
+
190
+ assert len(routes) == count_pudo_sequences(2, n_open_dropoffs=1)
191
+ assert len(routes) == 30
192
+ assert (4, 0, 1, 2, 3) in routes
193
+ assert (0, 1, 2, 3, 4) in routes
194
+ ```
195
+
196
+ This represents two new requests plus one already-open request whose drop-off
197
+ is `4`.
198
+
199
+ ## Counts
200
+
201
+ For `n` labeled pickup/drop-off pairs, the number of valid sequences is:
202
+
203
+ ```text
204
+ (2n)! / 2^n
205
+ ```
206
+
207
+ Equivalently:
208
+
209
+ ```text
210
+ n! * (1 * 3 * 5 * ... * (2n - 1))
211
+ ```
212
+
213
+ If there are also `m` already-open drop-offs, the count becomes:
214
+
215
+ ```text
216
+ (2n + m)! / 2^n
217
+ ```
218
+
219
+ API:
220
+
221
+ ```python
222
+ from pudo_sequences import (
223
+ count_fixed_pickup_order_dropoff_insertions,
224
+ count_open_dropoff_insertions,
225
+ count_pudo_sequences,
226
+ )
227
+
228
+ assert count_pudo_sequences(3) == 90
229
+ assert count_pudo_sequences(3, n_open_dropoffs=2) == 5040
230
+ assert count_fixed_pickup_order_dropoff_insertions(3) == 15
231
+ assert count_open_dropoff_insertions(3, 2) == 56
232
+ ```
233
+
234
+ The constructive count is useful for explaining the structure:
235
+
236
+ ```text
237
+ 1. choose a pickup order;
238
+ 2. insert each paired drop-off only after its pickup;
239
+ 3. optionally insert already-open drop-offs anywhere.
240
+ ```
241
+
242
+ ## Generation Strategies
243
+
244
+ The default strategy is dependency-free DFS over the PU/DO state space:
245
+
246
+ ```python
247
+ from pudo_sequences import iter_pudo_sequences
248
+
249
+ for route in iter_pudo_sequences([0, 1, 2], strategy="dfs"):
250
+ pass
251
+ ```
252
+
253
+ Other strategies are available for comparison:
254
+
255
+ - `strategy="insertion"`: generate pickup orders, then insert each paired
256
+ drop-off only after its pickup.
257
+ - `strategy="topological"`: use NetworkX to enumerate all topological orders of
258
+ a precedence graph with edges `pickup_i -> dropoff_i`.
259
+
260
+ For `n=3`, all strategies represent the same constrained sequence set:
261
+
262
+ ```python
263
+ from pudo_sequences import pudo_sequences
264
+
265
+ dfs_routes = set(pudo_sequences([0, 1, 2], strategy="dfs"))
266
+ insertion_routes = set(pudo_sequences([0, 1, 2], strategy="insertion"))
267
+
268
+ assert dfs_routes == insertion_routes
269
+ ```
270
+
271
+ The generator is intended for small local neighborhoods. PU/DO sequence counts
272
+ grow factorially, so large request sets should usually be handled by heuristics,
273
+ optimization models, or sampling rather than full enumeration.
274
+
275
+ ## Prefix Indexing
276
+
277
+ When a caller repeatedly asks for valid continuations after a partial route,
278
+ build a prefix index:
279
+
280
+ ```python
281
+ from pudo_sequences import build_pudo_index
282
+
283
+ index = build_pudo_index([0, 1])
284
+
285
+ assert index.continuations([0, 1]) == {(2, 3), (3, 2)}
286
+ assert index.continuations([0, 2]) == {(1, 3)}
287
+ ```
288
+
289
+ The index is a secondary feature. Its purpose is fast continuation lookup, not
290
+ the definition of the package.
291
+
292
+ ## Development
293
+
294
+ ```bash
295
+ pip install -e ".[dev]"
296
+ pytest -q
297
+ python -m build --sdist --wheel
298
+ python -m twine check dist/*
299
+ ```
@@ -0,0 +1,22 @@
1
+ """PU/DO constrained sequence combinatorics."""
2
+
3
+ from .core import (
4
+ count_fixed_pickup_order_dropoff_insertions,
5
+ count_open_dropoff_insertions,
6
+ count_pudo_sequences,
7
+ iter_pudo_sequences,
8
+ pudo_sequences,
9
+ )
10
+ from .index import PudoSequenceIndex, build_pudo_index
11
+
12
+ __all__ = [
13
+ "PudoSequenceIndex",
14
+ "build_pudo_index",
15
+ "count_fixed_pickup_order_dropoff_insertions",
16
+ "count_open_dropoff_insertions",
17
+ "count_pudo_sequences",
18
+ "iter_pudo_sequences",
19
+ "pudo_sequences",
20
+ ]
21
+
22
+ __version__ = "0.1.0"