Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/assign/costMemory.py
193697 views
1
# -*- coding: utf-8 -*-
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2012-2026 German Aerospace Center (DLR) and others.
4
# This program and the accompanying materials are made available under the
5
# terms of the Eclipse Public License 2.0 which is available at
6
# https://www.eclipse.org/legal/epl-2.0/
7
# This Source Code may also be made available under the following Secondary
8
# Licenses when the conditions for such availability set forth in the Eclipse
9
# Public License 2.0 are satisfied: GNU General Public License, version 2
10
# or later which is available at
11
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
14
# @file costMemory.py
15
# @author Jakob Erdmann
16
# @author Michael Behrisch
17
# @date 2012-03-14
18
19
from __future__ import print_function
20
from __future__ import absolute_import
21
import os
22
import sys
23
from collections import defaultdict
24
from xml.sax import make_parser, handler
25
26
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
27
from sumolib import openz # noqa
28
from sumolib.net import readNet # noqa
29
30
31
class EdgeMemory:
32
33
def __init__(self, cost):
34
self.cost = cost
35
self.seen = True
36
37
def update(self, cost, memory_weight, new_weight, pessimism):
38
p = (cost / self.cost) ** pessimism if self.cost > 0 else 1
39
memory_factor = memory_weight / (memory_weight + new_weight * p)
40
self.cost = self.cost * memory_factor + cost * (1 - memory_factor)
41
self.seen = True
42
43
44
class CostMemory(handler.ContentHandler):
45
# memorize the weighted average of edge costs
46
47
def __init__(self, cost_attribute, pessimism=0, network_file=None):
48
# the cost attribute to parse (i.e. 'traveltime')
49
self.cost_attribute = cost_attribute.encode('utf8')
50
# the duaIterate iteration index
51
self.iteration = None
52
# the main data store: for every interval and edge id we store costs and
53
# whether data was seen in the last call of load_costs()
54
# start -> (edge_id -> EdgeMemory)
55
self.intervals = defaultdict(dict)
56
# the interval length (only known for certain if multiple intervals
57
# have been seen)
58
self.interval_length = 214748 # SUMOTIME_MAXSTRING
59
# the interval currently being parsed
60
self.current_interval = None
61
# the combined weight of all previously loaded costs
62
self.memory_weight = 0.0
63
# update is done according to: memory * memory_factor + new * (1 -
64
# memory_factor)
65
self.memory_factor = None
66
# differences between the previously loaded costs and the memorized
67
# costs
68
self.errors = None
69
# some statistics
70
self.num_loaded = 0
71
self.num_decayed = 0
72
# travel times without obstructing traffic
73
# XXX could use the minimum known traveltime
74
self.traveltime_free = defaultdict(lambda: 0)
75
if network_file is not None:
76
# build a map of default weights for decaying edges assuming the
77
# attribute is traveltime
78
self.traveltime_free = dict([(e.getID(), e.getLength() / e.getSpeed())
79
for e in readNet(network_file).getEdges()])
80
self.pessimism = pessimism
81
82
def startElement(self, name, attrs):
83
if name == 'interval':
84
self.current_interval = self.intervals[float(attrs['begin'])]
85
if name == 'edge':
86
id = attrs['id']
87
# may be missing for some
88
if self.cost_attribute.decode('utf-8') in attrs:
89
self.num_loaded += 1
90
cost = float(attrs[self.cost_attribute.decode('utf-8')])
91
if id in self.current_interval:
92
edgeMemory = self.current_interval[id]
93
self.errors.append(edgeMemory.cost - cost)
94
edgeMemory.update(
95
cost, self.memory_weight, self.new_weight, self.pessimism)
96
# if id == "4.3to4.4":
97
# with open('debuglog', 'a') as f:
98
# print(self.memory_factor, edgeMemory.cost, file=f)
99
else:
100
self.errors.append(0)
101
self.current_interval[id] = EdgeMemory(cost)
102
103
def load_costs(self, dumpfile, iteration, weight):
104
# load costs from dumpfile and update memory according to weight and
105
# iteration
106
if weight <= 0:
107
sys.stderr.write(
108
"Skipped loading of costs because the weight was %s but should have been > 0\n" % weight)
109
return
110
assert weight > 0
111
if self.iteration is None and iteration != 0:
112
print("Warning: continuing with empty memory")
113
# update memory weights. memory is a weighted average across all runs
114
self.new_weight = float(weight)
115
self.iteration = iteration
116
self.errors = []
117
# mark all edges as unseen
118
for edges in self.intervals.values():
119
for edgeMemory in edges.values():
120
edgeMemory.seen = False
121
# parse costs
122
self.num_loaded = 0
123
parser = make_parser()
124
parser.setContentHandler(self)
125
parser.parse(openz(dumpfile))
126
# decay costs of unseen edges
127
self.num_decayed = 0
128
for edges in self.intervals.values():
129
for id, edgeMemory in edges.items():
130
if not edgeMemory.seen:
131
edgeMemory.update(
132
self.traveltime_free[id], self.memory_weight, self.new_weight, self.pessimism)
133
self.num_decayed += 1
134
# if id == "4.3to4.4":
135
# with open('debuglog', 'a') as f:
136
# print(self.memory_factor, 'decay', edgeMemory.cost, file=f)
137
# figure out the interval length
138
if len(self.intervals.keys()) > 1:
139
sorted_begin_times = sorted(self.intervals.keys())
140
self.interval_length = sorted_begin_times[
141
1] - sorted_begin_times[0]
142
self.memory_weight += self.new_weight
143
144
def write_costs(self, weight_file):
145
with open(weight_file, 'w') as f:
146
f.write('<netstats>\n')
147
for start, edge_costs in self.intervals.items():
148
f.write(' <interval begin="%d" end="%d">\n' %
149
(start, start + self.interval_length))
150
for id, edgeMemory in edge_costs.items():
151
f.write(' <edge id="%s" %s="%s"/>\n' %
152
(id, self.cost_attribute.decode('utf-8'), edgeMemory.cost))
153
f.write(' </interval>\n')
154
f.write('</netstats>\n')
155
156
def avg_error(self, values=None):
157
if not values:
158
values = self.errors
159
length = len(list(values))
160
if length > 0:
161
return (sum(list(values)) / length)
162
return 0
163
164
def avg_abs_error(self):
165
return self.avg_error(list(map(abs, self.errors)))
166
167
def mean_error(self, values=None):
168
if not values:
169
values = self.errors
170
values.sort()
171
if values:
172
return values[len(values) // 2]
173
return 0
174
175
def mean_abs_error(self):
176
return self.mean_error(list(map(abs, self.errors)))
177
178
def loaded(self):
179
return self.num_loaded
180
181
def decayed(self):
182
return self.num_decayed
183
184