Skip to content

Commit 20b143d

Browse files
committed
fix bugs in read_nodes() and README
- bypass the check on b > 0 if b is None with respect to the setup of is_activity_node; - optimize the setup for bin_index; - separate _output_equity() from evaluate_accessibility(); - change version number to v0.8.6a1; - fix misleading code snippet on finding shortest path where the keyword 'seq_type' shall have been provided; - polish README on Synthesize Zones and OD Demand; - change version info in README.
1 parent c8099e4 commit 20b143d

File tree

7 files changed

+106
-104
lines changed

7 files changed

+106
-104
lines changed

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Path4GMNS
22
[![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-red)](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-red)
3-
[![Downloads](https://pepy.tech/badge/path4gmns)](https://pepy.tech/project/path4gmns) [![GitHub release](https://img.shields.io/badge/release-v0.8.5-brightgreen)](https://img.shields.io/badge/release-v0.8.2-brightgreen)
3+
[![Downloads](https://pepy.tech/badge/path4gmns)](https://pepy.tech/project/path4gmns) [![GitHub release](https://img.shields.io/badge/release-v0.8.6a1-brightgreen)](https://img.shields.io/badge/release-v0.8.2-brightgreen)
44

55
Path4GMNS is an open-source, cross-platform, lightweight, and fast Python path engine for networks encoded in [GMNS](https://github.com/zephyr-data-specs/GMNS). Besides finding static shortest paths for simple analyses, its main functionality is to provide an efficient and flexible framework for column-based (path-based) modeling and applications in transportation (e.g., activity-based demand modeling). Path4GMNS supports, in short,
66

@@ -17,13 +17,13 @@ Path4GMNS also serves as an API to the C++-based [DTALite](https://github.com/jd
1717
* OD Matrix Estimation (ODME).
1818

1919
## Installation
20-
Path4GMNS has been published on [PyPI](https://pypi.org/project/path4gmns/0.8.5/), and can be installed using
20+
Path4GMNS has been published on [PyPI](https://pypi.org/project/path4gmns/0.8.6a1/), and can be installed using
2121
```
2222
$ pip install path4gmns
2323
```
24-
If you need a specific version of Path4GMNS, say, 0.8.5,
24+
If you need a specific version of Path4GMNS, say, 0.8.6a1,
2525
```
26-
$ pip install path4gmns==0.8.5
26+
$ pip install path4gmns==0.8.6a1
2727
```
2828

2929
v0.8.5 now supports _Apple Silicon_, and _synthesizing zones and demand_. Path4GMNS has evolved dramatically since its early releases with bug fixes, performance improvement, new functionalities. Please **discard all old versions** and **update to or install the latest version**.
@@ -53,7 +53,7 @@ network = pg.read_network(load_demand=False)
5353
print('\nshortest path (node id) from node 1 to node 2, '
5454
+network.find_shortest_path(1, 2))
5555
print('\nshortest path (link id) from node 1 to node 2, '
56-
+network.find_shortest_path(1, 2, 'link'))
56+
+network.find_shortest_path(1, 2, seq_type='link'))
5757
```
5858

5959
You can specify the absolute path or the relative path from your cwd in read_network() to use a particular network from the downloaded sample data set.
@@ -66,7 +66,7 @@ network = pg.read_network(load_demand=False, input_dir='data/Chicago_Sketch')
6666
print('\nshortest path (node id) from node 1 to node 2, '
6767
+network.find_shortest_path(1, 2))
6868
print('\nshortest path (link id) from node 1 to node 2, '
69-
+network.find_shortest_path(1, 2, 'link'))
69+
+network.find_shortest_path(1, 2, seq_type='link'))
7070
```
7171

7272
Retrieving the shortest path between any two (different) nodes under a specific mode is now available under v0.7.2 or higher.
@@ -427,13 +427,13 @@ print(f'processing time of equity evaluation: {time()-st:.2f} s')
427427

428428
Zone information is crucial in conducting traffic assignment, evaluating accessibility and equity. When no zone information is provided along node.csv, Path4GMNS can automatically synthesize a total number of $d * d$ grids (rectangles) as zones given the dimension $d$.
429429

430-
Activity nodes are randomly sampled for each zone according to a hardcoded sample rate $r$, where $r = max(10, N / 100)$ and $N$ is the total number of nodes in the network. The total demand, as an input argument, will be allocated to each zone proportionally with respect to the number of its activity nodes.
430+
Activity nodes are randomly sampled for each zone according to a hardcoded sample rate $r$, where $r = max(10, N / 100)$ and $N$ is the total number of nodes in the network. The total demand, as an input argument, will be allocated to each zone proportionally with respect to the number of its activity nodes, as its synthesized production volume and also attraction volume.
431431

432432
$$prod_i = attr_i = demand \times \frac{N_i^a}{N^a}$$
433433

434434
Where, $prod_i$ is the production volume of zone $i$, $attr_i$ is the production volume of zone $j$, $demand$ is the total demand, $N^a$ is the total number of activity nodes, $N_i^a$ is the number of activity nodes in zone $i$.
435435

436-
In other words, the allocated demand to each zone serves as its synthesized production volume and also attraction volume. Denote the minimum travel time from zone $i$ to zone $j$ under a specific mode as $mintt_{ij}$ and introduce the following definition on the set of connected zones from zone $i$, which is cut off by a predefined time budget $b$.
436+
Denote the minimum travel time from zone $i$ to zone $j$ under a specific mode as $mintt_{ij}$ and introduce the following definition on the set of connected zones from zone $i$, which is cut off by a predefined time budget $b$.
437437

438438
$$ D(i) = \lbrace j: mintt_{ij}\leq b \rbrace $$
439439

@@ -452,7 +452,8 @@ network = pg.read_network(load_demand=False)
452452

453453
print('\nstart zone synthesis')
454454
st = time()
455-
# by default, grid_dimension is 8, total_demand is 10,000, time_budget is 120 min, mode is 'p'
455+
# by default, grid_dimension is 8, total_demand is 10,000,
456+
# time_budget is 120 min, mode is 'p'
456457
pg.network_to_zones(network)
457458
pg.output_zones(network)
458459
pg.output_synthesized_demand(network)
@@ -473,7 +474,6 @@ pg.load_demand(network, 'p', 'AM', filename='syn_demand.csv')
473474
# perform some other functionalities from Path4GMNS, e.g., traffic assignment
474475
column_gen_num = 20
475476
column_update_num = 20
476-
477477
pg.perform_column_generation(column_gen_num, column_update_num, network)
478478

479479
pg.output_columns(network)
@@ -506,11 +506,11 @@ As **CMAKE_BUILD_TYPE** will be **IGNORED** for IDE (Integrated Development Envi
506506
# from the root directory of PATH4GMNS
507507
$ python setup.py sdist bdist_wheel
508508
$ cd dist
509-
# or python -m pip install path4gmns-0.8.5.tar.gz
510-
$ python -m pip instal pypath4gmns-0.8.5-py3-none-any.whl
509+
# or python -m pip install path4gmns-0.8.6a1.tar.gz
510+
$ python -m pip instal pypath4gmns-0.8.6a1-py3-none-any.whl
511511
```
512512

513-
Here, 0.8.5 is the version number. Replace it with the one specified in setup.py.
513+
Here, 0.8.6a1 is the version number. Replace it with the one specified in setup.py.
514514

515515
## Implementation Notes
516516

@@ -562,7 +562,7 @@ You are encouraged to join our [Gmail group](https://groups.google.com/g/path4gm
562562

563563
## How to Cite
564564

565-
Li, P. and Zhou, X. (2022, September 5). *Path4GMNS*. Retrieved from https://github.com/jdlph/Path4GMNS
565+
Li, P. and Zhou, X. (2022, September 10). *Path4GMNS*. Retrieved from https://github.com/jdlph/Path4GMNS
566566

567567
## References
568568
Lu, C. C., Mahmassani, H. S., Zhou, X. (2009). Equivalent gap function-based reformulation and solution algorithm for the dynamic user equilibrium problem. Transportation Research Part B: Methodological, 43, 345-364.

path4gmns/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .zonesyn import *
66

77

8-
__version__ = '0.8.5'
8+
__version__ = '0.8.6a1'
99

1010

1111
# print out the current version

path4gmns/accessibility.py

Lines changed: 85 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,39 @@ def _output_accessibility_aggregated(min_travel_times, interval_num,
148148
+' for aggregated accessibility matrix')
149149

150150

151+
def _output_equity(output_dir, time_budget, equity_metrics, equity_zones):
152+
with open(output_dir+'/equity_'+str(time_budget)+'min.csv', 'w', newline='') as f:
153+
headers = ['bin_index', 'mode', 'zones',
154+
'min_accessibility', 'zone_id',
155+
'max_accessibility', 'zone_id',
156+
'mean_accessibility']
157+
writer = csv.writer(f)
158+
writer.writerow(headers)
159+
160+
for k, v in sorted(equity_metrics.items()):
161+
try:
162+
avg = round(v[4] / len(equity_zones[k]), 2)
163+
zones = ', '.join(str(x) for x in equity_zones[k])
164+
line = [k[0], k[1], zones, v[0], v[1], v[2], v[3], avg]
165+
except ZeroDivisionError:
166+
continue
167+
168+
writer.writerow(line)
169+
170+
if output_dir == '.':
171+
print('\ncheck equity_'
172+
+str(time_budget)
173+
+'min.csv in '
174+
+os.getcwd()
175+
+' for equity evaluation')
176+
else:
177+
print('\ncheck equity_'
178+
+str(time_budget)
179+
+'min.csv in '
180+
+os.path.join(os.getcwd(), output_dir)
181+
+' for equity evaluation')
182+
183+
151184
def evaluate_accessibility(ui,
152185
multimodal=True,
153186
mode='p',
@@ -326,99 +359,70 @@ def evaluate_equity(ui, multimodal=True, mode='p', time_dependent=False,
326359
str in the file name refers to the time budget. For example, the file name
327360
will be equity_60min.csv if the time budget is 60 min.
328361
"""
329-
with open(output_dir+'/equity_'+str(time_budget)+'min.csv', 'w', newline='') as f:
330-
headers = ['bin_index', 'mode', 'zones',
331-
'min_accessibility', 'zone_id',
332-
'max_accessibility', 'zone_id',
333-
'mean_accessibility']
334-
writer = csv.writer(f)
335-
writer.writerow(headers)
362+
base = ui._base_assignment
363+
an = AccessNetwork(base.network)
364+
zones = an.base.zones
365+
ats = None
336366

337-
base = ui._base_assignment
338-
an = AccessNetwork(base.network)
339-
zones = an.base.zones
340-
ats = None
341-
342-
min_travel_times = {}
343-
equity_metrics = {}
344-
equity_zones = {}
345-
346-
if multimodal:
347-
ats = base.get_agent_types()
348-
for at in ats:
349-
an.set_target_mode(at.get_name())
350-
_update_min_travel_time(an,
351-
at,
352-
min_travel_times,
353-
time_dependent,
354-
demand_period_id)
355-
else:
356-
at_name, at_str = base._convert_mode(mode)
357-
an.set_target_mode(at_name)
358-
at = base.get_agent_type(at_str)
367+
min_travel_times = {}
368+
equity_metrics = {}
369+
equity_zones = {}
370+
371+
if multimodal:
372+
ats = base.get_agent_types()
373+
for at in ats:
374+
an.set_target_mode(at.get_name())
359375
_update_min_travel_time(an,
360376
at,
361377
min_travel_times,
362378
time_dependent,
363379
demand_period_id)
364-
ats = [at]
365-
366-
# v is zone object
367-
for oz, v in zones.items():
368-
if oz == -1:
369-
continue
370-
371-
bin_index = v.get_bin_index()
372-
for at in ats:
373-
at_str = at.get_type_str()
380+
else:
381+
at_name, at_str = base._convert_mode(mode)
382+
an.set_target_mode(at_name)
383+
at = base.get_agent_type(at_str)
384+
_update_min_travel_time(an,
385+
at,
386+
min_travel_times,
387+
time_dependent,
388+
demand_period_id)
389+
ats = [at]
374390

375-
count = 0
376-
for dz in zones.keys():
377-
if (oz, dz, at_str) not in min_travel_times.keys():
378-
continue
391+
# v is zone object
392+
for oz, v in zones.items():
393+
if oz == -1:
394+
continue
379395

380-
min_tt = min_travel_times[(oz, dz, at_str)][0]
381-
if min_tt > time_budget:
382-
continue
396+
bin_index = v.get_bin_index()
397+
for at in ats:
398+
at_str = at.get_type_str()
383399

384-
count += 1
400+
count = 0
401+
for dz in zones.keys():
402+
if (oz, dz, at_str) not in min_travel_times.keys():
403+
continue
385404

386-
if (bin_index, at_str) not in equity_metrics.keys():
387-
equity_metrics[(bin_index, at_str)] = [count, oz, count, oz, 0]
388-
equity_zones[(bin_index, at_str)] = []
389-
equity_zones[(bin_index, at_str)].append(oz)
405+
min_tt = min_travel_times[(oz, dz, at_str)][0]
406+
if min_tt > time_budget:
407+
continue
390408

391-
# 0: min_accessibility, 1: zone_id, 2: max_accessibility,
392-
# 3: zone_id, 4: cumulative count,
393-
# where 0 to 4 are indices of each element of equity_metrics.
394-
if count < equity_metrics[(bin_index, at_str)][0]:
395-
equity_metrics[(bin_index, at_str)][0] = count
396-
equity_metrics[(bin_index, at_str)][1] = oz
397-
elif count > equity_metrics[(bin_index, at_str)][2]:
398-
equity_metrics[(bin_index, at_str)][2] = count
399-
equity_metrics[(bin_index, at_str)][3] = oz
409+
count += 1
400410

401-
equity_metrics[(bin_index, at_str)][4] += count
411+
if (bin_index, at_str) not in equity_metrics.keys():
412+
equity_metrics[(bin_index, at_str)] = [count, oz, count, oz, 0]
413+
equity_zones[(bin_index, at_str)] = []
414+
equity_zones[(bin_index, at_str)].append(oz)
402415

403-
for k, v in sorted(equity_metrics.items()):
404-
try:
405-
avg = round(v[4] / len(equity_zones[k]), 2)
406-
zones = ', '.join(str(x) for x in equity_zones[k])
407-
line = [k[0], k[1], zones, v[0], v[1], v[2], v[3], avg]
408-
except ZeroDivisionError:
409-
continue
416+
# 0: min_accessibility, 1: zone_id, 2: max_accessibility,
417+
# 3: zone_id, 4: cumulative count,
418+
# where 0 to 4 are indices of each element of equity_metrics.
419+
if count < equity_metrics[(bin_index, at_str)][0]:
420+
equity_metrics[(bin_index, at_str)][0] = count
421+
equity_metrics[(bin_index, at_str)][1] = oz
422+
elif count > equity_metrics[(bin_index, at_str)][2]:
423+
equity_metrics[(bin_index, at_str)][2] = count
424+
equity_metrics[(bin_index, at_str)][3] = oz
410425

411-
writer.writerow(line)
426+
equity_metrics[(bin_index, at_str)][4] += count
412427

413-
if output_dir == '.':
414-
print('\ncheck equity_'
415-
+str(time_budget)
416-
+'min.csv in '
417-
+os.getcwd()
418-
+' for equity evaluation')
419-
else:
420-
print('\ncheck equity_'
421-
+str(time_budget)
422-
+'min.csv in '
423-
+os.path.join(os.getcwd(), output_dir)
424-
+' for equity evaluation')
428+
_output_equity(output_dir, time_budget, equity_metrics, equity_zones)

path4gmns/utils.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,7 @@ def read_nodes(input_dir,
250250
b = _convert_str_to_int(line['is_boundary'])
251251
if b is None:
252252
pass
253-
254-
if b > 0:
253+
elif b > 0:
255254
is_activity_node = True
256255
except KeyError:
257256
pass
@@ -267,9 +266,9 @@ def read_nodes(input_dir,
267266
# bin_index for equity evaluation
268267
bin_index = 0
269268
try:
270-
bin_index = _convert_str_to_int(line['bin_index'])
271-
if bin_index is None:
272-
bin_index = 0
269+
bi = _convert_str_to_int(line['bin_index'])
270+
if bi is not None:
271+
bin_index = bi
273272
except KeyError:
274273
pass
275274

path4gmns/zonesyn.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ def network_to_zones(ui, grid_dimension=8, total_demand=10000, time_budget=120,
226226
It is used along with time_budget to check if the minimum travel time under
227227
the given mode exceeds the time budget or not.
228228
229-
230229
Outputs
231230
-------
232231
zone.csv.csv

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="path4gmns",
8-
version="0.8.5",
8+
version="0.8.6a1",
99
author="Dr. Xuesong Zhou, Dr. Peiheng Li",
1010
author_email="xzhou74@asu.edu, jdlph@hotmail.com",
1111
description="An open-source, cross-platform, lightweight, and fast Python\

tests/demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,4 @@ def demo_mode(mode):
254254

255255
if __name__=="__main__":
256256

257-
demo_mode(6)
257+
demo_mode(7)

0 commit comments

Comments
 (0)