-
Notifications
You must be signed in to change notification settings - Fork 36
/
environment.py
204 lines (162 loc) · 7.09 KB
/
environment.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#-*- coding: utf-8 -*-
import numpy as np
import math
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
class Environment(object):
"""
Implementation of the black-boxed environment
Attributes common to the environment:
numBins(int) -- Number of bins in the environment
numSlots(int) -- Number of available slots per bin in the environment
cells[numBins, numSlots] -- Stores environment occupancy
packet_properties{struct} -- Describes packet properties inside the environment
Attributes common to the service
serviceLength(int) -- Length of the service
service[serviceLength] -- Collects the service chain
placement[serviceLength] -- Collects the packet allocation for the service chain
first_slots[serviceLength] -- Stores the first slot occupied in the correspondent bin for each packet
reward(float) -- Stores the reward obtained placing the service on the environment
invalidPlacement(Bool) -- Invalid placement indicates that there is a resource overflow
"""
def __init__(self, numBins, numSlots, numDescriptors):
# Environment properties
self.numBins = numBins
self.numSlots = numSlots
self.numDescriptors = numDescriptors
self.cells = np.empty((numBins, numSlots))
self.cells[:] = np.nan
self.service_properties = [{"size": 1} for _ in range(numDescriptors)]
# Placement properties
self.serviceLength = 0
self.service = None
self.placement = None
self.first_slots = None
self.reward = 1
self.invalidPlacement = False
# Assign ns properties within the environment
self._get_service_propertieses()
def _get_service_propertieses(self):
""" Packet properties """
# By default the size of each package in that environment is 1, should be modified here.
self.service_properties[0]["size"] = 3
self.service_properties[1]["size"] = 2
self.service_properties[2]["size"] = 2
self.service_properties[3]["size"] = 1
self.service_properties[4]["size"] = 1
self.service_properties[5]["size"] = 1
self.service_properties[6]["size"] = 1
self.service_properties[7]["size"] = 1
def _placeSubPakcet(self, bin, pkt):
""" Place subPacket """
occupied_slot = None
for slot in range(len(self.cells[bin])):
if np.isnan(self.cells[bin][slot]):
self.cells[bin][slot] = pkt
occupied_slot = slot
break
elif slot == len(self.cells[bin])-1:
self.invalidPlacement = True
occupied_slot = -1 # No space available
break
else:
pass # Look for next slot
return occupied_slot
def _placePacket(self, i, bin, pkt):
""" Place Packet """
for slot in range(self.service_properties[pkt]["size"]):
occupied_slot = self._placeSubPakcet(bin, pkt)
# Anotate first slot used by the Packet
if slot == 0:
self.first_slots[i] = occupied_slot
def _computeReward(self):
""" Compute reward """
occupancy = np.empty(self.numBins)
for bin in range(self.numBins):
occupied = 0
for slot in range(len(self.cells[bin])):
if not math.isnan(self.cells[bin][slot]):
occupied += 1
occupancy[bin] = occupied / len(self.cells[bin])
reward = np.sum(np.power(100, occupancy))
return reward
def step(self, placement, service, length):
""" Place service """
self.placement = placement
self.service = service
self.serviceLength = length
self.first_slots = np.zeros(length, dtype='int32')
for i in range(length):
self._placePacket(i, placement[i], service[i])
""" Compute reward """
if self.invalidPlacement == True:
self.reward = 1
else:
self.reward = self._computeReward()
def clear(self):
""" Clean environment """
self.cells = np.empty((self.numBins, self.numSlots))
self.cells[:] = np.nan
self.serviceLength = 0
self.service = None
self.placement = None
self.first_slots = None
self.reward = 1
self.invalidPlacement = False
def render(self, epoch=0):
""" Render environment using Matplotlib """
# Creates just a figure and only one subplot
fig, ax = plt.subplots()
ax.set_title(f'Environment {epoch}\nreward: {self.reward}')
margin = 3
margin_ext = 6
xlim = 100
ylim = 80
# Set drawing limits
plt.xlim(0, xlim)
plt.ylim(-ylim, 0)
# Set hight and width for the box
high = np.floor((ylim - 2 * margin_ext - margin * (self.numBins - 1)) / self.numBins)
wide = np.floor((xlim - 2 * margin_ext - margin * (self.numSlots - 1)) / self.numSlots)
# Plot slot labels
for slot in range(self.numSlots):
x = wide * slot + slot * margin + margin_ext
plt.text(x + 0.5 * wide, -3, "slot{}".format(slot), ha="center", family='sans-serif', size=8)
# Plot bin labels & place empty boxes
for bin in range(self.numBins):
y = -high * (bin + 1) - (bin) * margin - margin_ext
plt.text(0, y + 0.5 * high, "bin{}".format(bin), ha="center", family='sans-serif', size=8)
for slot in range(self.numSlots):
x = wide * slot + slot * margin + margin_ext
rectangle = mpatches.Rectangle((x, y), wide, high, linewidth=1, edgecolor='black', facecolor='none')
ax.add_patch(rectangle)
# Select serviceLength colors from a colormap
cmap = plt.cm.get_cmap('hot')
colormap = [cmap(np.float32(i+1)/(self.serviceLength+1)) for i in range(self.serviceLength)]
# Plot service boxes
for idx in range(self.serviceLength):
pkt = self.service[idx]
bin = self.placement[idx]
first_slot = self.first_slots[idx]
for k in range(self.service_properties[pkt]["size"]):
slot = first_slot + k
x = wide * slot + slot * margin + margin_ext
y = -high * (bin + 1) - bin * margin - margin_ext
rectangle = mpatches.Rectangle((x, y), wide, high, linewidth=0, facecolor=colormap[idx], alpha=.9)
ax.add_patch(rectangle)
plt.text(x + 0.5 * wide, y + 0.5 * high, "pkt{}".format(pkt), ha="center", family='sans-serif', size=8)
plt.axis('off')
plt.show()
if __name__ == "__main__":
# Define environment
numBins = 5
numSlots = 5
numDescriptors = 8
env = Environment(numBins, numSlots, numDescriptors)
# Allocate service in the environment
servicelength = 5
ns = [0, 6, 6, 7, 5, 0]
placement = [0, 1, 1, 0, 0]
env.step(placement, ns, servicelength)
env.render()
env.clear()