edsger 0.1.1__cp312-cp312-macosx_11_0_arm64.whl

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,368 @@
1
+ """
2
+ An implementation of Spiess and Florian's hyperpath generating algorithm.
3
+
4
+ reference: Spiess, H. and Florian, M. (1989). Optimal strategies: A new
5
+ assignment model for transit networks. Transportation Research Part B 23(2),
6
+ 83-102.
7
+
8
+ cpdef function:
9
+
10
+ - compute_SF_in
11
+ Compute Spiess & Florian optimal strategy (all-to-one).
12
+
13
+ cdef functions:
14
+
15
+ - _SF_in_first_pass_full
16
+ - _SF_in_second_pass
17
+
18
+ """
19
+
20
+ import numpy as np
21
+ cimport numpy as cnp
22
+
23
+ from edsger.commons import DTYPE_PY, DTYPE_INF_PY
24
+ from edsger.commons cimport (
25
+ DTYPE_INF, UNLABELED, SCANNED, DTYPE_t, ElementState)
26
+ cimport edsger.pq_4ary_dec_0b as pq # priority queue
27
+
28
+
29
+ cpdef void compute_SF_in(
30
+ cnp.uint32_t[::1] csc_indptr,
31
+ cnp.uint32_t[::1] csc_edge_idx,
32
+ DTYPE_t[::1] c_a_vec,
33
+ DTYPE_t[::1] f_a_vec,
34
+ cnp.uint32_t[::1] tail_indices,
35
+ cnp.uint32_t[::1] head_indices,
36
+ cnp.uint32_t[::1] demand_indices,
37
+ DTYPE_t[::1] demand_values,
38
+ DTYPE_t[::1] v_a_vec,
39
+ DTYPE_t[::1] u_i_vec,
40
+ int vertex_count,
41
+ int dest_vert_index,
42
+ ):
43
+ """Compute Spiess & Florian optimal strategy (all-to-one).
44
+ """
45
+
46
+ cdef:
47
+ int edge_count = tail_indices.shape[0]
48
+
49
+ # initialization
50
+ u_i_vec[<size_t>dest_vert_index] = 0.0
51
+
52
+ # vertex properties
53
+ # vertex frequency (inverse of the maximum delay)
54
+ f_i_vec = np.zeros(vertex_count, dtype=DTYPE_PY)
55
+ u_j_c_a_vec = DTYPE_INF_PY * np.ones(edge_count, dtype=DTYPE_PY)
56
+ v_i_vec = np.zeros(vertex_count, dtype=DTYPE_PY) # vertex volume
57
+
58
+ # edge properties
59
+ h_a_vec = np.zeros(edge_count, dtype=bool) # edge belonging to hyperpath
60
+
61
+ # first pass #
62
+ # ---------- #
63
+
64
+ _SF_in_first_pass_full(
65
+ csc_indptr,
66
+ csc_edge_idx,
67
+ c_a_vec,
68
+ f_a_vec,
69
+ tail_indices,
70
+ u_i_vec,
71
+ f_i_vec,
72
+ u_j_c_a_vec,
73
+ h_a_vec,
74
+ dest_vert_index
75
+ )
76
+
77
+ # second pass #
78
+ # ----------- #
79
+
80
+ cdef:
81
+ DTYPE_t u_r, _v_a_new, _v_i, u_i
82
+ size_t i, h_a_count
83
+ cnp.uint32_t vert_idx
84
+
85
+ v_i_vec = np.zeros(vertex_count, dtype=DTYPE_PY) # vertex volume
86
+
87
+ u_r = DTYPE_INF_PY
88
+ for i, vert_idx in enumerate(demand_indices):
89
+
90
+ v_i_vec[<size_t>vert_idx] = demand_values[i]
91
+ u_i = u_i_vec[<size_t>vert_idx]
92
+
93
+ if u_i < u_r:
94
+
95
+ u_r = u_i
96
+
97
+ # if the destination can be reached from any of the origins
98
+ if u_r < DTYPE_INF_PY:
99
+
100
+ # make sure f_i values are not zero
101
+ f_i_vec = np.where(
102
+ f_i_vec < MIN_FREQ_PY, MIN_FREQ_PY, f_i_vec
103
+ )
104
+
105
+ # sort the links with descreasing order of u_j + c_a
106
+ h_a_count = h_a_vec.sum()
107
+ masked_a = np.ma.array(-u_j_c_a_vec, mask=~h_a_vec)
108
+ edge_indices = np.argsort(masked_a).astype(np.uint32)
109
+
110
+ _SF_in_second_pass(
111
+ edge_indices,
112
+ tail_indices,
113
+ head_indices,
114
+ v_i_vec,
115
+ v_a_vec,
116
+ f_i_vec,
117
+ f_a_vec,
118
+ h_a_count
119
+ )
120
+
121
+
122
+ cdef void _SF_in_first_pass_full(
123
+ cnp.uint32_t[::1] csc_indptr,
124
+ cnp.uint32_t[::1] csc_edge_idx,
125
+ DTYPE_t[::1] c_a_vec,
126
+ DTYPE_t[::1] f_a_vec,
127
+ cnp.uint32_t[::1] tail_indices,
128
+ DTYPE_t[::1] u_i_vec,
129
+ DTYPE_t[::1] f_i_vec,
130
+ DTYPE_t[::1] u_j_c_a_vec,
131
+ cnp.uint8_t[::1] h_a_vec,
132
+ int dest_vert_index,
133
+ ) nogil:
134
+ """SF in first pass.
135
+
136
+ Note : all vertices are visited.
137
+ """
138
+
139
+ cdef:
140
+ int edge_count = tail_indices.shape[0]
141
+ pq.PriorityQueue pqueue
142
+ ElementState edge_state
143
+ size_t i, edge_idx, tail_vert_idx
144
+ DTYPE_t u_j_c_a, u_i, f_i, beta, u_i_new, f_a
145
+
146
+ # initialization of the heap elements
147
+ # all nodes have INFINITY key and UNLABELED state
148
+ pq.init_pqueue(&pqueue, <size_t>edge_count, <size_t>edge_count)
149
+
150
+ # only the incoming edges of the target vertex are inserted into the
151
+ # priority queue
152
+ for i in range(<size_t>csc_indptr[<size_t>dest_vert_index],
153
+ <size_t>csc_indptr[<size_t>(dest_vert_index + 1)]):
154
+ edge_idx = csc_edge_idx[i]
155
+ pq.insert(&pqueue, edge_idx, c_a_vec[edge_idx])
156
+ u_j_c_a_vec[edge_idx] = c_a_vec[edge_idx]
157
+
158
+ # first pass
159
+ while pqueue.size > 0:
160
+
161
+ edge_idx = pq.extract_min(&pqueue)
162
+ u_j_c_a = pqueue.Elements[edge_idx].key
163
+ tail_vert_idx = <size_t>tail_indices[edge_idx]
164
+ u_i = u_i_vec[tail_vert_idx]
165
+
166
+ if u_i >= u_j_c_a:
167
+
168
+ f_i = f_i_vec[tail_vert_idx]
169
+
170
+ # compute the beta coefficient
171
+ if (u_i < DTYPE_INF) | (f_i > 0.0):
172
+
173
+ beta = f_i * u_i
174
+
175
+ else:
176
+
177
+ beta = 1.0
178
+
179
+ # update u_i
180
+ f_a = f_a_vec[edge_idx]
181
+ u_i_new = (beta + f_a * u_j_c_a) / (f_i + f_a)
182
+ u_i_vec[tail_vert_idx] = u_i_new
183
+
184
+ # update f_i
185
+ f_i_vec[tail_vert_idx] = f_i + f_a
186
+
187
+ # add the edge to hyperpath
188
+ h_a_vec[edge_idx] = 1
189
+
190
+ else:
191
+
192
+ u_i_new = u_i
193
+
194
+ # loop on incoming edges
195
+ for i in range(<size_t>csc_indptr[tail_vert_idx],
196
+ <size_t>csc_indptr[tail_vert_idx + 1]):
197
+
198
+ edge_idx = csc_edge_idx[i]
199
+ edge_state = pqueue.Elements[edge_idx].state
200
+
201
+ if edge_state != SCANNED:
202
+
203
+ # u_j of current edge = u_i of outgoing edge
204
+ u_j_c_a = u_i_new + c_a_vec[edge_idx]
205
+
206
+ if edge_state == UNLABELED:
207
+
208
+ pq.insert(&pqueue, edge_idx, u_j_c_a)
209
+ u_j_c_a_vec[edge_idx] = u_j_c_a
210
+
211
+ elif (pqueue.Elements[edge_idx].key > u_j_c_a):
212
+
213
+ pq.decrease_key(&pqueue, edge_idx, u_j_c_a)
214
+ u_j_c_a_vec[edge_idx] = u_j_c_a
215
+
216
+ pq.free_pqueue(&pqueue)
217
+
218
+
219
+ cdef void _SF_in_second_pass(
220
+ cnp.uint32_t[::1] edge_indices,
221
+ cnp.uint32_t[::1] tail_indices,
222
+ cnp.uint32_t[::1] head_indices,
223
+ DTYPE_t[::1] v_i_vec,
224
+ DTYPE_t[::1] v_a_vec,
225
+ DTYPE_t[::1] f_i_vec,
226
+ DTYPE_t[::1] f_a_vec,
227
+ size_t h_a_count
228
+ ) nogil:
229
+ """SF in second_pass.
230
+ """
231
+
232
+ cdef:
233
+ size_t i, edge_idx, vert_idx
234
+ DTYPE_t v_i, f_i, f_a, v_a_new
235
+
236
+ for i in range(h_a_count):
237
+
238
+ edge_idx = <size_t>edge_indices[i]
239
+ vert_idx = <size_t>tail_indices[edge_idx]
240
+
241
+ v_i = v_i_vec[vert_idx]
242
+ f_i = f_i_vec[vert_idx]
243
+ f_a = f_a_vec[edge_idx]
244
+
245
+ # update v_a
246
+ v_a_new = v_i * f_a / f_i
247
+ v_a_vec[edge_idx] = v_a_new
248
+ v_i_vec[<size_t>head_indices[edge_idx]] += v_a_new
249
+
250
+
251
+ # ============================================================================ #
252
+ # tests #
253
+ # ============================================================================ #
254
+
255
+
256
+ from edsger.commons import MIN_FREQ_PY, INF_FREQ_PY
257
+
258
+ cpdef compute_SF_in_01():
259
+ """
260
+ Single edge network.
261
+
262
+ This network has 1 edge and 2 vertices.
263
+ """
264
+
265
+ volume = 1.0
266
+
267
+ f_a = MIN_FREQ_PY
268
+
269
+ csc_indptr = np.array([0, 0, 1], dtype=np.uint32)
270
+ csc_edge_idx = np.array([0], dtype=np.uint32)
271
+ c_a_vec = np.array([1.0], dtype=DTYPE_PY)
272
+ f_a_vec = np.array([f_a], dtype=DTYPE_PY)
273
+ v_a_vec = np.array([0.0], dtype=DTYPE_PY)
274
+ tail_indices = np.array([0], dtype=np.uint32)
275
+ head_indices = np.array([1], dtype=np.uint32)
276
+ demand_indices = np.array([0], dtype=np.uint32)
277
+ demand_values = np.array([volume], dtype=DTYPE_PY)
278
+ vertex_count = 2
279
+ u_i_vec = DTYPE_INF_PY * np.ones(vertex_count, dtype=DTYPE_PY)
280
+ dest_vert_index = 1
281
+
282
+ compute_SF_in(
283
+ csc_indptr,
284
+ csc_edge_idx,
285
+ c_a_vec,
286
+ f_a_vec,
287
+ tail_indices,
288
+ head_indices,
289
+ demand_indices,
290
+ demand_values,
291
+ v_a_vec,
292
+ u_i_vec,
293
+ vertex_count,
294
+ dest_vert_index,
295
+ )
296
+
297
+ assert v_a_vec[0] == volume
298
+ assert v_a_vec.shape[0] == 1
299
+
300
+ f_a = INF_FREQ_PY
301
+ f_a_vec = np.array([f_a], dtype=DTYPE_PY)
302
+ u_i_vec = DTYPE_INF_PY * np.ones(vertex_count, dtype=DTYPE_PY)
303
+
304
+ compute_SF_in(
305
+ csc_indptr,
306
+ csc_edge_idx,
307
+ c_a_vec,
308
+ f_a_vec,
309
+ tail_indices,
310
+ head_indices,
311
+ demand_indices,
312
+ demand_values,
313
+ v_a_vec,
314
+ u_i_vec,
315
+ vertex_count,
316
+ dest_vert_index,
317
+ )
318
+
319
+ assert v_a_vec[0] == volume
320
+ assert v_a_vec.shape[0] == 1
321
+
322
+
323
+ cpdef compute_SF_in_02():
324
+ """
325
+ Two edges network.
326
+
327
+ This network has 2 edges and 2 vertices.
328
+ """
329
+
330
+ volume = 1.0
331
+
332
+ csc_indptr = np.array([0, 0, 2], dtype=np.uint32)
333
+ csc_edge_idx = np.array([0, 1], dtype=np.uint32)
334
+ c_a_vec = np.array([1.0, 1.0], dtype=DTYPE_PY)
335
+ f_a_vec = np.array([2.0, 1.0], dtype=DTYPE_PY)
336
+ v_a_vec = np.array([0.0, 0.0], dtype=DTYPE_PY)
337
+ tail_indices = np.array([0, 0], dtype=np.uint32)
338
+ head_indices = np.array([1, 1], dtype=np.uint32)
339
+ demand_indices = np.array([0], dtype=np.uint32)
340
+ demand_values = np.array([volume], dtype=DTYPE_PY)
341
+ vertex_count = 2
342
+ u_i_vec = DTYPE_INF_PY * np.ones(vertex_count, dtype=DTYPE_PY)
343
+ dest_vert_index = 1
344
+
345
+ compute_SF_in(
346
+ csc_indptr,
347
+ csc_edge_idx,
348
+ c_a_vec,
349
+ f_a_vec,
350
+ tail_indices,
351
+ head_indices,
352
+ demand_indices,
353
+ demand_values,
354
+ v_a_vec,
355
+ u_i_vec,
356
+ vertex_count,
357
+ dest_vert_index,
358
+ )
359
+
360
+ assert v_a_vec[0] == 2. / 3.
361
+ assert v_a_vec[1] == 1. / 3.
362
+ assert v_a_vec.shape[0] == 2
363
+
364
+
365
+ # author : Francois Pacull
366
+ # copyright : Architecture & Performance
367
+ # email: francois.pacull@architecture-performance.fr
368
+ # license : MIT