├── .gitignore ├── README.md ├── car_racing.py ├── generate-rollouts.ipynb ├── generate-rollouts.py ├── pushover.py ├── utils.py ├── vae-cnn.ipynb ├── vae.ipynb └── vae.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | __pycache__/ 3 | 4 | data/ 5 | 6 | rollouts/ 7 | 8 | *.zip 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A CNN Variational Autoencoder in PyTorch 2 | 3 | Implemetation of David Ha's research on [Word Model](https://arxiv.org/pdf/1803.10122.pdf). 4 | 5 | -------------------------------------------------------------------------------- /car_racing.py: -------------------------------------------------------------------------------- 1 | import sys, math 2 | import numpy as np 3 | 4 | import Box2D 5 | from Box2D.b2 import (edgeShape, circleShape, fixtureDef, polygonShape, revoluteJointDef, contactListener) 6 | 7 | import gym 8 | from gym import spaces 9 | from gym.envs.box2d.car_dynamics import Car 10 | from gym.utils import colorize, seeding 11 | 12 | import pyglet 13 | from pyglet import gl 14 | 15 | # Easiest continuous control task to learn from pixels, a top-down racing environment. 16 | # Discreet control is reasonable in this environment as well, on/off discretisation is 17 | # fine. 18 | # 19 | # State consists of STATE_W x STATE_H pixels. 20 | # 21 | # Reward is -0.1 every frame and +1000/N for every track tile visited, where N is 22 | # the total number of tiles in track. For example, if you have finished in 732 frames, 23 | # your reward is 1000 - 0.1*732 = 926.8 points. 24 | # 25 | # Game is solved when agent consistently gets 900+ points. Track is random every episode. 26 | # 27 | # Episode finishes when all tiles are visited. Car also can go outside of PLAYFIELD, that 28 | # is far off the track, then it will get -100 and die. 29 | # 30 | # Some indicators shown at the bottom of the window and the state RGB buffer. From 31 | # left to right: true speed, four ABS sensors, steering wheel position, gyroscope. 32 | # 33 | # To play yourself (it's rather fast for humans), type: 34 | # 35 | # python gym/envs/box2d/car_racing.py 36 | # 37 | # Remember it's powerful rear-wheel drive car, don't press accelerator and turn at the 38 | # same time. 39 | # 40 | # Created by Oleg Klimov. Licensed on the same terms as the rest of OpenAI Gym. 41 | 42 | STATE_W = 512 # less than Atari 160x192 43 | STATE_H = 512 44 | VIDEO_W = 600 45 | VIDEO_H = 400 46 | WINDOW_W = 1200 47 | WINDOW_H = 1000 48 | 49 | SCALE = 6.0 # Track scale 50 | TRACK_RAD = 900/SCALE # Track is heavily morphed circle with this radius 51 | PLAYFIELD = 2000/SCALE # Game over boundary 52 | FPS = 50 53 | ZOOM = 2.7 # Camera zoom 54 | ZOOM_FOLLOW = True # Set to False for fixed view (don't use zoom) 55 | 56 | 57 | TRACK_DETAIL_STEP = 21/SCALE 58 | TRACK_TURN_RATE = 0.31 59 | TRACK_WIDTH = 40/SCALE 60 | BORDER = 8/SCALE 61 | BORDER_MIN_COUNT = 4 62 | 63 | ROAD_COLOR = [0.4, 0.4, 0.4] 64 | 65 | class FrictionDetector(contactListener): 66 | def __init__(self, env): 67 | contactListener.__init__(self) 68 | self.env = env 69 | def BeginContact(self, contact): 70 | self._contact(contact, True) 71 | def EndContact(self, contact): 72 | self._contact(contact, False) 73 | def _contact(self, contact, begin): 74 | tile = None 75 | obj = None 76 | u1 = contact.fixtureA.body.userData 77 | u2 = contact.fixtureB.body.userData 78 | if u1 and "road_friction" in u1.__dict__: 79 | tile = u1 80 | obj = u2 81 | if u2 and "road_friction" in u2.__dict__: 82 | tile = u2 83 | obj = u1 84 | if not tile: return 85 | 86 | tile.color[0] = ROAD_COLOR[0] 87 | tile.color[1] = ROAD_COLOR[1] 88 | tile.color[2] = ROAD_COLOR[2] 89 | if not obj or "tiles" not in obj.__dict__: return 90 | if begin: 91 | obj.tiles.add(tile) 92 | #print tile.road_friction, "ADD", len(obj.tiles) 93 | if not tile.road_visited: 94 | tile.road_visited = True 95 | self.env.reward += 1000.0/len(self.env.track) 96 | self.env.tile_visited_count += 1 97 | else: 98 | obj.tiles.remove(tile) 99 | #print tile.road_friction, "DEL", len(obj.tiles) -- should delete to zero when on grass (this works) 100 | 101 | class CarRacing(gym.Env): 102 | metadata = { 103 | 'render.modes': ['human', 'rgb_array', 'state_pixels'], 104 | 'video.frames_per_second' : FPS 105 | } 106 | 107 | def __init__(self): 108 | self.seed() 109 | self.contactListener_keepref = FrictionDetector(self) 110 | self.world = Box2D.b2World((0,0), contactListener=self.contactListener_keepref) 111 | self.viewer = None 112 | self.invisible_state_window = None 113 | self.invisible_video_window = None 114 | self.road = None 115 | self.car = None 116 | self.reward = 0.0 117 | self.prev_reward = 0.0 118 | 119 | self.action_space = spaces.Box( np.array([-1,0,0]), np.array([+1,+1,+1])) # steer, gas, brake 120 | self.observation_space = spaces.Box(low=0, high=255, shape=(STATE_H, STATE_W, 3), dtype=np.uint8) 121 | 122 | def seed(self, seed=None): 123 | self.np_random, seed = seeding.np_random(seed) 124 | return [seed] 125 | 126 | def _destroy(self): 127 | if not self.road: return 128 | for t in self.road: 129 | self.world.DestroyBody(t) 130 | self.road = [] 131 | self.car.destroy() 132 | 133 | def _create_track(self): 134 | CHECKPOINTS = 12 135 | 136 | # Create checkpoints 137 | checkpoints = [] 138 | for c in range(CHECKPOINTS): 139 | alpha = 2*math.pi*c/CHECKPOINTS + self.np_random.uniform(0, 2*math.pi*1/CHECKPOINTS) 140 | rad = self.np_random.uniform(TRACK_RAD/3, TRACK_RAD) 141 | if c==0: 142 | alpha = 0 143 | rad = 1.5*TRACK_RAD 144 | if c==CHECKPOINTS-1: 145 | alpha = 2*math.pi*c/CHECKPOINTS 146 | self.start_alpha = 2*math.pi*(-0.5)/CHECKPOINTS 147 | rad = 1.5*TRACK_RAD 148 | checkpoints.append( (alpha, rad*math.cos(alpha), rad*math.sin(alpha)) ) 149 | 150 | #print "\n".join(str(h) for h in checkpoints) 151 | #self.road_poly = [ ( # uncomment this to see checkpoints 152 | # [ (tx,ty) for a,tx,ty in checkpoints ], 153 | # (0.7,0.7,0.9) ) ] 154 | self.road = [] 155 | 156 | # Go from one checkpoint to another to create track 157 | x, y, beta = 1.5*TRACK_RAD, 0, 0 158 | dest_i = 0 159 | laps = 0 160 | track = [] 161 | no_freeze = 2500 162 | visited_other_side = False 163 | while 1: 164 | alpha = math.atan2(y, x) 165 | if visited_other_side and alpha > 0: 166 | laps += 1 167 | visited_other_side = False 168 | if alpha < 0: 169 | visited_other_side = True 170 | alpha += 2*math.pi 171 | while True: # Find destination from checkpoints 172 | failed = True 173 | while True: 174 | dest_alpha, dest_x, dest_y = checkpoints[dest_i % len(checkpoints)] 175 | if alpha <= dest_alpha: 176 | failed = False 177 | break 178 | dest_i += 1 179 | if dest_i % len(checkpoints) == 0: break 180 | if not failed: break 181 | alpha -= 2*math.pi 182 | continue 183 | r1x = math.cos(beta) 184 | r1y = math.sin(beta) 185 | p1x = -r1y 186 | p1y = r1x 187 | dest_dx = dest_x - x # vector towards destination 188 | dest_dy = dest_y - y 189 | proj = r1x*dest_dx + r1y*dest_dy # destination vector projected on rad 190 | while beta - alpha > 1.5*math.pi: beta -= 2*math.pi 191 | while beta - alpha < -1.5*math.pi: beta += 2*math.pi 192 | prev_beta = beta 193 | proj *= SCALE 194 | if proj > 0.3: beta -= min(TRACK_TURN_RATE, abs(0.001*proj)) 195 | if proj < -0.3: beta += min(TRACK_TURN_RATE, abs(0.001*proj)) 196 | x += p1x*TRACK_DETAIL_STEP 197 | y += p1y*TRACK_DETAIL_STEP 198 | track.append( (alpha,prev_beta*0.5 + beta*0.5,x,y) ) 199 | if laps > 4: break 200 | no_freeze -= 1 201 | if no_freeze==0: break 202 | #print "\n".join([str(t) for t in enumerate(track)]) 203 | 204 | # Find closed loop range i1..i2, first loop should be ignored, second is OK 205 | i1, i2 = -1, -1 206 | i = len(track) 207 | while True: 208 | i -= 1 209 | if i==0: return False # Failed 210 | pass_through_start = track[i][0] > self.start_alpha and track[i-1][0] <= self.start_alpha 211 | if pass_through_start and i2==-1: 212 | i2 = i 213 | elif pass_through_start and i1==-1: 214 | i1 = i 215 | break 216 | # print("Track generation: %i..%i -> %i-tiles track" % (i1, i2, i2-i1)) 217 | assert i1!=-1 218 | assert i2!=-1 219 | 220 | track = track[i1:i2-1] 221 | 222 | first_beta = track[0][1] 223 | first_perp_x = math.cos(first_beta) 224 | first_perp_y = math.sin(first_beta) 225 | # Length of perpendicular jump to put together head and tail 226 | well_glued_together = np.sqrt( 227 | np.square( first_perp_x*(track[0][2] - track[-1][2]) ) + 228 | np.square( first_perp_y*(track[0][3] - track[-1][3]) )) 229 | if well_glued_together > TRACK_DETAIL_STEP: 230 | return False 231 | 232 | # Red-white border on hard turns 233 | border = [False]*len(track) 234 | for i in range(len(track)): 235 | good = True 236 | oneside = 0 237 | for neg in range(BORDER_MIN_COUNT): 238 | beta1 = track[i-neg-0][1] 239 | beta2 = track[i-neg-1][1] 240 | good &= abs(beta1 - beta2) > TRACK_TURN_RATE*0.2 241 | oneside += np.sign(beta1 - beta2) 242 | good &= abs(oneside) == BORDER_MIN_COUNT 243 | border[i] = good 244 | for i in range(len(track)): 245 | for neg in range(BORDER_MIN_COUNT): 246 | border[i-neg] |= border[i] 247 | 248 | # Create tiles 249 | for i in range(len(track)): 250 | alpha1, beta1, x1, y1 = track[i] 251 | alpha2, beta2, x2, y2 = track[i-1] 252 | road1_l = (x1 - TRACK_WIDTH*math.cos(beta1), y1 - TRACK_WIDTH*math.sin(beta1)) 253 | road1_r = (x1 + TRACK_WIDTH*math.cos(beta1), y1 + TRACK_WIDTH*math.sin(beta1)) 254 | road2_l = (x2 - TRACK_WIDTH*math.cos(beta2), y2 - TRACK_WIDTH*math.sin(beta2)) 255 | road2_r = (x2 + TRACK_WIDTH*math.cos(beta2), y2 + TRACK_WIDTH*math.sin(beta2)) 256 | t = self.world.CreateStaticBody( fixtures = fixtureDef( 257 | shape=polygonShape(vertices=[road1_l, road1_r, road2_r, road2_l]) 258 | )) 259 | t.userData = t 260 | c = 0.01*(i%3) 261 | t.color = [ROAD_COLOR[0] + c, ROAD_COLOR[1] + c, ROAD_COLOR[2] + c] 262 | t.road_visited = False 263 | t.road_friction = 1.0 264 | t.fixtures[0].sensor = True 265 | self.road_poly.append(( [road1_l, road1_r, road2_r, road2_l], t.color )) 266 | self.road.append(t) 267 | if border[i]: 268 | side = np.sign(beta2 - beta1) 269 | b1_l = (x1 + side* TRACK_WIDTH *math.cos(beta1), y1 + side* TRACK_WIDTH *math.sin(beta1)) 270 | b1_r = (x1 + side*(TRACK_WIDTH+BORDER)*math.cos(beta1), y1 + side*(TRACK_WIDTH+BORDER)*math.sin(beta1)) 271 | b2_l = (x2 + side* TRACK_WIDTH *math.cos(beta2), y2 + side* TRACK_WIDTH *math.sin(beta2)) 272 | b2_r = (x2 + side*(TRACK_WIDTH+BORDER)*math.cos(beta2), y2 + side*(TRACK_WIDTH+BORDER)*math.sin(beta2)) 273 | self.road_poly.append(( [b1_l, b1_r, b2_r, b2_l], (1,1,1) if i%2==0 else (1,0,0) )) 274 | self.track = track 275 | return True 276 | 277 | def reset(self): 278 | self._destroy() 279 | self.reward = 0.0 280 | self.prev_reward = 0.0 281 | self.tile_visited_count = 0 282 | self.t = 0.0 283 | self.road_poly = [] 284 | self.human_render = False 285 | 286 | while True: 287 | success = self._create_track() 288 | if success: break 289 | print("retry to generate track (normal if there are not many of this messages)") 290 | self.car = Car(self.world, *self.track[0][1:4]) 291 | 292 | return self.step(None)[0] 293 | 294 | def step(self, action): 295 | if action is not None: 296 | self.car.steer(-action[0]) 297 | self.car.gas(action[1]) 298 | self.car.brake(action[2]) 299 | 300 | self.car.step(1.0/FPS) 301 | self.world.Step(1.0/FPS, 6*30, 2*30) 302 | self.t += 1.0/FPS 303 | 304 | self.state = self.render("state_pixels") 305 | 306 | step_reward = 0 307 | done = False 308 | if action is not None: # First step without action, called from reset() 309 | self.reward -= 0.1 310 | # We actually don't want to count fuel spent, we want car to be faster. 311 | #self.reward -= 10 * self.car.fuel_spent / ENGINE_POWER 312 | self.car.fuel_spent = 0.0 313 | step_reward = self.reward - self.prev_reward 314 | self.prev_reward = self.reward 315 | if self.tile_visited_count==len(self.track): 316 | done = True 317 | x, y = self.car.hull.position 318 | if abs(x) > PLAYFIELD or abs(y) > PLAYFIELD: 319 | done = True 320 | step_reward = -100 321 | 322 | return self.state, step_reward, done, {} 323 | 324 | def render(self, mode='human'): 325 | if self.viewer is None: 326 | from gym.envs.classic_control import rendering 327 | self.viewer = rendering.Viewer(WINDOW_W, WINDOW_H) 328 | self.score_label = pyglet.text.Label('0000', font_size=36, 329 | x=20, y=WINDOW_H*2.5/40.00, anchor_x='left', anchor_y='center', 330 | color=(255,255,255,255)) 331 | self.transform = rendering.Transform() 332 | 333 | if "t" not in self.__dict__: return # reset() not called yet 334 | 335 | zoom = ZOOM*SCALE # Animate zoom first second 336 | zoom_state = ZOOM*SCALE*STATE_W/WINDOW_W 337 | zoom_video = ZOOM*SCALE*VIDEO_W/WINDOW_W 338 | scroll_x = self.car.hull.position[0] 339 | scroll_y = self.car.hull.position[1] 340 | angle = -self.car.hull.angle 341 | vel = self.car.hull.linearVelocity 342 | if np.linalg.norm(vel) > 0.5: 343 | angle = math.atan2(vel[0], vel[1]) 344 | self.transform.set_scale(zoom, zoom) 345 | self.transform.set_translation( 346 | WINDOW_W/2 - (scroll_x*zoom*math.cos(angle) - scroll_y*zoom*math.sin(angle)), 347 | WINDOW_H/4 - (scroll_x*zoom*math.sin(angle) + scroll_y*zoom*math.cos(angle)) ) 348 | self.transform.set_rotation(angle) 349 | 350 | self.car.draw(self.viewer, mode!="state_pixels") 351 | 352 | arr = None 353 | win = self.viewer.window 354 | if mode != 'state_pixels': 355 | win.switch_to() 356 | win.dispatch_events() 357 | if mode=="rgb_array" or mode=="state_pixels": 358 | win.clear() 359 | t = self.transform 360 | if mode=='rgb_array': 361 | VP_W = VIDEO_W 362 | VP_H = VIDEO_H 363 | else: 364 | VP_W = STATE_W 365 | VP_H = STATE_H 366 | gl.glViewport(0, 0, VP_W, VP_H) 367 | t.enable() 368 | self.render_road() 369 | for geom in self.viewer.onetime_geoms: 370 | geom.render() 371 | t.disable() 372 | self.render_indicators(WINDOW_W, WINDOW_H) # TODO: find why 2x needed, wtf 373 | image_data = pyglet.image.get_buffer_manager().get_color_buffer().get_image_data() 374 | arr = np.fromstring(image_data.data, dtype=np.uint8, sep='') 375 | arr = arr.reshape(VP_H, VP_W, 4) 376 | arr = arr[::-1, :, 0:3] 377 | 378 | if mode=="rgb_array" and not self.human_render: # agent can call or not call env.render() itself when recording video. 379 | win.flip() 380 | 381 | if mode=='human': 382 | self.human_render = True 383 | win.clear() 384 | t = self.transform 385 | gl.glViewport(0, 0, WINDOW_W, WINDOW_H) 386 | t.enable() 387 | self.render_road() 388 | for geom in self.viewer.onetime_geoms: 389 | geom.render() 390 | t.disable() 391 | self.render_indicators(WINDOW_W, WINDOW_H) 392 | win.flip() 393 | 394 | self.viewer.onetime_geoms = [] 395 | return arr 396 | 397 | def close(self): 398 | if self.viewer is not None: 399 | self.viewer.close() 400 | self.viewer = None 401 | 402 | def render_road(self): 403 | gl.glBegin(gl.GL_QUADS) 404 | gl.glColor4f(0.4, 0.8, 0.4, 1.0) 405 | gl.glVertex3f(-PLAYFIELD, +PLAYFIELD, 0) 406 | gl.glVertex3f(+PLAYFIELD, +PLAYFIELD, 0) 407 | gl.glVertex3f(+PLAYFIELD, -PLAYFIELD, 0) 408 | gl.glVertex3f(-PLAYFIELD, -PLAYFIELD, 0) 409 | gl.glColor4f(0.4, 0.9, 0.4, 1.0) 410 | k = PLAYFIELD/20.0 411 | for x in range(-20, 20, 2): 412 | for y in range(-20, 20, 2): 413 | gl.glVertex3f(k*x + k, k*y + 0, 0) 414 | gl.glVertex3f(k*x + 0, k*y + 0, 0) 415 | gl.glVertex3f(k*x + 0, k*y + k, 0) 416 | gl.glVertex3f(k*x + k, k*y + k, 0) 417 | for poly, color in self.road_poly: 418 | gl.glColor4f(color[0], color[1], color[2], 1) 419 | for p in poly: 420 | gl.glVertex3f(p[0], p[1], 0) 421 | gl.glEnd() 422 | 423 | def render_indicators(self, W, H): 424 | gl.glBegin(gl.GL_QUADS) 425 | s = W/40.0 426 | h = H/40.0 427 | gl.glColor4f(0,0,0,1) 428 | gl.glVertex3f(W, 0, 0) 429 | gl.glVertex3f(W, 5*h, 0) 430 | gl.glVertex3f(0, 5*h, 0) 431 | gl.glVertex3f(0, 0, 0) 432 | def vertical_ind(place, val, color): 433 | gl.glColor4f(color[0], color[1], color[2], 1) 434 | gl.glVertex3f((place+0)*s, h + h*val, 0) 435 | gl.glVertex3f((place+1)*s, h + h*val, 0) 436 | gl.glVertex3f((place+1)*s, h, 0) 437 | gl.glVertex3f((place+0)*s, h, 0) 438 | def horiz_ind(place, val, color): 439 | gl.glColor4f(color[0], color[1], color[2], 1) 440 | gl.glVertex3f((place+0)*s, 4*h , 0) 441 | gl.glVertex3f((place+val)*s, 4*h, 0) 442 | gl.glVertex3f((place+val)*s, 2*h, 0) 443 | gl.glVertex3f((place+0)*s, 2*h, 0) 444 | true_speed = np.sqrt(np.square(self.car.hull.linearVelocity[0]) + np.square(self.car.hull.linearVelocity[1])) 445 | vertical_ind(5, 0.02*true_speed, (1,1,1)) 446 | vertical_ind(7, 0.01*self.car.wheels[0].omega, (0.0,0,1)) # ABS sensors 447 | vertical_ind(8, 0.01*self.car.wheels[1].omega, (0.0,0,1)) 448 | vertical_ind(9, 0.01*self.car.wheels[2].omega, (0.2,0,1)) 449 | vertical_ind(10,0.01*self.car.wheels[3].omega, (0.2,0,1)) 450 | horiz_ind(20, -10.0*self.car.wheels[0].joint.angle, (0,1,0)) 451 | horiz_ind(30, -0.8*self.car.hull.angularVelocity, (1,0,0)) 452 | gl.glEnd() 453 | self.score_label.text = "%04i" % self.reward 454 | self.score_label.draw() 455 | 456 | 457 | if __name__=="__main__": 458 | from pyglet.window import key 459 | a = np.array( [0.0, 0.0, 0.0] ) 460 | def key_press(k, mod): 461 | global restart 462 | if k==0xff0d: restart = True 463 | if k==key.LEFT: a[0] = -1.0 464 | if k==key.RIGHT: a[0] = +1.0 465 | if k==key.UP: a[1] = +1.0 466 | if k==key.DOWN: a[2] = +0.8 # set 1.0 for wheels to block to zero rotation 467 | def key_release(k, mod): 468 | if k==key.LEFT and a[0]==-1.0: a[0] = 0 469 | if k==key.RIGHT and a[0]==+1.0: a[0] = 0 470 | if k==key.UP: a[1] = 0 471 | if k==key.DOWN: a[2] = 0 472 | env = CarRacing() 473 | env.render() 474 | record_video = False 475 | if record_video: 476 | env.monitor.start('/tmp/video-test', force=True) 477 | env.viewer.window.on_key_press = key_press 478 | env.viewer.window.on_key_release = key_release 479 | while True: 480 | env.reset() 481 | total_reward = 0.0 482 | steps = 0 483 | restart = False 484 | while True: 485 | s, r, done, info = env.step(a) 486 | total_reward += r 487 | if steps % 200 == 0 or done: 488 | print("\naction " + str(["{:+0.2f}".format(x) for x in a])) 489 | print("step {} total_reward {:+0.2f}".format(steps, total_reward)) 490 | #import matplotlib.pyplot as plt 491 | #plt.imshow(s) 492 | #plt.savefig("test.jpeg") 493 | steps += 1 494 | if not record_video: # Faster, but you can as well call env.render() every time to play full window. 495 | env.render() 496 | if done or restart: break 497 | env.close() 498 | -------------------------------------------------------------------------------- /generate-rollouts.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import gym\n", 10 | "from car_racing import CarRacing" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 65, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "from random import random, randint, choice" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 2, 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "name": "stdout", 29 | "output_type": "stream", 30 | "text": [ 31 | "\u001b[33mWARN: gym.spaces.Box autodetected dtype as . Please provide explicit dtype.\u001b[0m\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "env = gym.make('CarRacing-v0')" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 34, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "episodes = 1\n", 46 | "steps = 100" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 68, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "import numpy as np" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 4, 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "data": { 65 | "text/plain": [ 66 | "array([0.08976637, 0.4236548 , 0.6458941 ], dtype=float32)" 67 | ] 68 | }, 69 | "execution_count": 4, 70 | "metadata": {}, 71 | "output_type": "execute_result" 72 | } 73 | ], 74 | "source": [ 75 | "env.action_space.sample()" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 37, 81 | "metadata": { 82 | "collapsed": true 83 | }, 84 | "outputs": [ 85 | { 86 | "name": "stdout", 87 | "output_type": "stream", 88 | "text": [ 89 | "Track generation: 1094..1380 -> 286-tiles track\n" 90 | ] 91 | }, 92 | { 93 | "ename": "NotImplementedError", 94 | "evalue": "abstract", 95 | "output_type": "error", 96 | "traceback": [ 97 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 98 | "\u001b[0;31mNotImplementedError\u001b[0m Traceback (most recent call last)", 99 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0meps\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mepisodes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mobs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msteps\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0maction\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maction_space\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msample\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 100 | "\u001b[0;32m~/Documents/Github/gym/gym/wrappers/time_limit.py\u001b[0m in \u001b[0;36mreset\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 42\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_episode_started_at\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 43\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_elapsed_steps\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 44\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 101 | "\u001b[0;32m~/Documents/Github/gym/gym/envs/box2d/car_racing.py\u001b[0m in \u001b[0;36mreset\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 290\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mCar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mworld\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrack\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 291\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 292\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 293\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 294\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maction\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 102 | "\u001b[0;32m~/Documents/Github/gym/gym/envs/box2d/car_racing.py\u001b[0m in \u001b[0;36mstep\u001b[0;34m(self, action)\u001b[0m\n\u001b[1;32m 302\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mt\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1.0\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mFPS\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 304\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrender\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"state_pixels\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 305\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 306\u001b[0m \u001b[0mstep_reward\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 103 | "\u001b[0;32m~/Documents/Github/gym/gym/envs/box2d/car_racing.py\u001b[0m in \u001b[0;36mrender\u001b[0;34m(self, mode)\u001b[0m\n\u001b[1;32m 325\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mviewer\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 326\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mgym\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menvs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclassic_control\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mrendering\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 327\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mviewer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrendering\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mViewer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mWINDOW_W\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mWINDOW_H\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 328\u001b[0m self.score_label = pyglet.text.Label('0000', font_size=36,\n\u001b[1;32m 329\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mWINDOW_H\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;36m2.5\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;36m40.00\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0manchor_x\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'left'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0manchor_y\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'center'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 104 | "\u001b[0;32m~/Documents/Github/gym/gym/envs/classic_control/rendering.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, width, height, display)\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwidth\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwidth\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mheight\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mheight\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 51\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwindow\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpyglet\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwindow\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mWindow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwidth\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheight\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdisplay\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 52\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwindow\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mon_close\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwindow_closed_by_user\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 53\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misopen\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 105 | "\u001b[0;32m~/.pyenv/versions/anaconda3-4.4.0/lib/python3.6/site-packages/pyglet/window/__init__.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, width, height, caption, resizable, style, fullscreen, visible, vsync, display, screen, config, context, mode)\u001b[0m\n\u001b[1;32m 502\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 503\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mscreen\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 504\u001b[0;31m \u001b[0mscreen\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_default_screen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 505\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 506\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 106 | "\u001b[0;32m~/.pyenv/versions/anaconda3-4.4.0/lib/python3.6/site-packages/pyglet/canvas/base.py\u001b[0m in \u001b[0;36mget_default_screen\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 71\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0mrtype\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;32mclass\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mScreen\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 72\u001b[0m '''\n\u001b[0;32m---> 73\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_screens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 74\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget_windows\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 107 | "\u001b[0;32m~/.pyenv/versions/anaconda3-4.4.0/lib/python3.6/site-packages/pyglet/canvas/base.py\u001b[0m in \u001b[0;36mget_screens\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0mrtype\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mlist\u001b[0m \u001b[0mof\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;32mclass\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0mScreen\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 64\u001b[0m '''\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'abstract'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 66\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 67\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget_default_screen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 108 | "\u001b[0;31mNotImplementedError\u001b[0m: abstract" 109 | ] 110 | } 111 | ], 112 | "source": [ 113 | "for eps in range(episodes):\n", 114 | " obs = env.reset()\n", 115 | " print(obs)\n", 116 | " for t in range(steps):\n", 117 | " action = env.action_space.sample()\n", 118 | " a, b, xc, d = env.step(action)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [] 127 | } 128 | ], 129 | "metadata": { 130 | "kernelspec": { 131 | "display_name": "Python 3", 132 | "language": "python", 133 | "name": "python3" 134 | }, 135 | "language_info": { 136 | "codemirror_mode": { 137 | "name": "ipython", 138 | "version": 3 139 | }, 140 | "file_extension": ".py", 141 | "mimetype": "text/x-python", 142 | "name": "python", 143 | "nbconvert_exporter": "python", 144 | "pygments_lexer": "ipython3", 145 | "version": "3.6.1" 146 | } 147 | }, 148 | "nbformat": 4, 149 | "nbformat_minor": 2 150 | } 151 | -------------------------------------------------------------------------------- /generate-rollouts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author: Shubham Chandel 3 | # @Date: 2018-04-21 18:55:18 4 | # @Last Modified by: Shubham Chandel 5 | # @Last Modified time: 2018-04-22 03:02:35 6 | 7 | from car_racing import CarRacing 8 | 9 | import scipy.misc 10 | import numpy as np 11 | from random import choice, random, randint 12 | 13 | env = CarRacing() 14 | 15 | episodes = 100 16 | steps = 150 17 | 18 | def get_action(): 19 | # return choice([[1, 0, 0], [-1, 0, 0], [0, 1, 0]]) 20 | # return [2*random()-1.05, 1, 0] 21 | action = env.action_space.sample() 22 | action[0] -= 0.05 23 | action[1] = 1 24 | action[2] = 0 25 | return action 26 | 27 | for eps in range(episodes): 28 | obs = env.reset() 29 | env.render() 30 | r = 0 31 | for t in range(steps): 32 | action = get_action() 33 | obs, reward, done, _ = env.step(action) 34 | # env.render() 35 | r += reward 36 | if t%5 == 0: 37 | i = ('000' + str(t//5))[-3:] 38 | scipy.misc.imsave(f'rollouts/CarRacing/car_{eps}_{i}.jpg', obs) 39 | print("Episode [{}/{}]: CummReward {:.2f}".format(eps+1, episodes, r)) 40 | 41 | 42 | -------------------------------------------------------------------------------- /pushover.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Author: chanshub 3 | # @Date: 2018-01-29 23:03:52 4 | # @Last Modified by: Shubham Chandel 5 | # @Last Modified time: 2018-04-21 06:23:57 6 | 7 | import requests 8 | 9 | data = { 10 | 'token': "", 11 | 'user': "", 12 | } 13 | 14 | JUSTBECAUSE = "Just because I have to put something." 15 | 16 | def notify(t, s=JUSTBECAUSE, priority=-1, sound="classical"): 17 | data['message'] = str(s) 18 | data['title'] = str(t) 19 | data['priority'] = priority 20 | data['sound'] = sound 21 | r = requests.post('https://api.pushover.net/1/messages.json', data=data) 22 | print("Notifing", t, s, r) 23 | 24 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import imageio 2 | from pathlib import Path 3 | 4 | 5 | def makegif(folder='reconstructed', name='recon_image', ext='jpg'): 6 | images = [] 7 | for file in sorted([file for file in Path(folder).glob(f'*.{ext}')]): 8 | images.append(imageio.imread(str(file))) 9 | imageio.mimsave(f'{name}.gif', images) 10 | print("Done!") 11 | -------------------------------------------------------------------------------- /vae-cnn.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import torch\n", 10 | "import torch.nn as nn\n", 11 | "import torch.nn.functional as F\n", 12 | "from torch.autograd import Variable\n", 13 | "\n", 14 | "import torchvision\n", 15 | "from torchvision import datasets\n", 16 | "from torchvision import transforms\n", 17 | "from torchvision.utils import save_image\n", 18 | "from torchsummary import summary\n", 19 | "\n", 20 | "from pushover import notify\n", 21 | "from utils import makegif\n", 22 | "from random import randint\n", 23 | "\n", 24 | "from IPython.display import Image\n", 25 | "from IPython.core.display import Image, display\n", 26 | "\n", 27 | "%load_ext autoreload\n", 28 | "%autoreload 2" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 10, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "data": { 38 | "text/plain": [ 39 | "device(type='cpu')" 40 | ] 41 | }, 42 | "execution_count": 10, 43 | "metadata": {}, 44 | "output_type": "execute_result" 45 | } 46 | ], 47 | "source": [ 48 | "# Device configuration\n", 49 | "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", 50 | "device" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 2, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "bs = 32" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": {}, 66 | "outputs": [ 67 | { 68 | "data": { 69 | "text/plain": [ 70 | "(30000, 938)" 71 | ] 72 | }, 73 | "execution_count": 3, 74 | "metadata": {}, 75 | "output_type": "execute_result" 76 | } 77 | ], 78 | "source": [ 79 | "# Load Data\n", 80 | "dataset = datasets.ImageFolder(root='./rollouts', transform=transforms.Compose([\n", 81 | " transforms.Resize(64),\n", 82 | " transforms.ToTensor(), \n", 83 | "]))\n", 84 | "dataloader = torch.utils.data.DataLoader(dataset, batch_size=bs, shuffle=True)\n", 85 | "len(dataset.imgs), len(dataloader)" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 4, 91 | "metadata": {}, 92 | "outputs": [ 93 | { 94 | "data": { 95 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhIAAAEKCAIAAADIMiuDAADzKUlEQVR4nOz9aZB02XkeBj7Pe+7Nraq+tfcNDaAbW4OEAIIbBFIUadGA5JEUDGtGHMkTlkIiRUtkKGJmrBkrxjETnqFifjhkySPRhD1hijOj0GZbtrhBlAguEDcsBLEQ6A3obvT27V9tud173md+3MyszKxcTlZl1vd1s5/opary5r3n3LO8592eF3gLb+EtvIW38Bbewlt4C2/hLbyFTYDV/37ksz+ytltGZL+S8eWjmx+D9AjK7y8R1vbMT3z4EwD+6qt/zaK8DbVhUWu7+ySYSztknQC1Bxz6Cl/egW1Dc97MTz/801jvWMwHAc/c6oYWkS272oGbROEp77Qai/X2goDq4gWDHWvYvqsDaN5kO8nTeE4//a7192JD4A3pF4jOjI8+8YlPAPgrX/2rdmhA2oogAIMArDKx1wGn04gW2QAyYY84BNY6o7RD206+GMKBYf/oPfAGwi8E9TV3c6P8O4WndPxdH++FgnjJmM2/22RrcFvqpjb+lBCqPQLsiXvBXjd/TXYdP/V3fwpYvmGc4HlydzoWiQ2HIKa8rFXAjnQoFuBmRIYCrAk2qaxahEID6KyyvrrU1jypcaaQwc4Z6gASZq1BdWfB1K1n3XCDtTgtMwAZbcdgrsO1NU11sXkXjNAK4OK+W88qyZt0rxYUXF2wuBOj7eA+1XHUgGIDW0SPaOn4RJpzNVAXDo8WuDIpc/Tnf19kn5Kn7EHmhhLKmNRJQnVHN7HpJwABCKKIQtyH3YBfRXYz5wHRFyNGE2L9YuNO4rbWPtG9GlKCdXGLyKnxZZpDOdhLvl0p9cH69J8FasYBZYOQIMGSlyVzA9f/elOf3iDqM55NSAZsE6QOZb6GBoYatbm1uXZQKhgUEDCaQtNzqVSqBDCgSeZgE+hLHaEPeLKmcjrY4L2LJVEmfolusvRzW+HoQ00wQT2liAxeA7sDocsAWVggpQXQo8gkKS2pDzbS12CQab1KoCiTSUAE2grXTdeIa9LtGLswGUJ1wK8W/6Cpby6xsZm5zZzcEuvE8WMByYbQT360oA5ZmzzfC+xJ7bPdkgX0iDpTVbOalAnFhls1Cwxgi1rQToLbUKDvV2eiU8HbYNr5765AteAF4KjZJ26/MiEDQRjUBBpEAXZdXY6ecvdAAdaUGbW/aHZMfgfoAo30Z4A29jJJcK54lkTQ2w6lnsfYJxyJ2o8yKRP7J56c03YFlmSbvCXcsHDNcNu8Ey1SoKKzMtMNv0MeHW3XLzYG+9Cis7NUvf67H4HWIltzx5UQ6lSoDnRJYF+IVD74NgvoUOiJZ2tJJoA+4Ej1MJmYU8XZ7xxES8oXW9IIgE2RIR5EFlxitVmMKO3fZRvkIpDdtbkhrNIpq/tW5/FcyI1bQpfqOgqeuctjAtPaf0ZzxI5QpO4n3lcoTPniqwSAPfgh0B/7awZlpXy+/R1CDIR0bI+e/YwolECdKWJPJmYT7UnHUNwZXeoIu+AV4IrjNr2DDFYpSHRUfsKRkJB0/BSyCd8G4C5f8NYEZ5ob6Mww3RwZWCdbRA1LTlkBbEgHyY+KQBfIYSW8LXWEUx+QT4goFlDmSDPKqCF0zvjISdXBVuLJDWpEC9CeTra0jvAGkhoAI8mF57Q0iEL9+KokAASgJTSJgug6eoZ49hbLwSJlTtsS6oMTvQdng+l6MKOpJ+SLJhUL80OpN20REsBlWxd7glNZmgLkYJ+qLbtMQAS7hn6i0D5qJCE51XXsma47XgNviof0KGFwSFAYyIZxlWJwo1kT681lpJoHSoC5sS8Vo+lOBaAB2nRokwx2Dmgg1YBTJ9or2BzVFQl1gLUf3iudJdE2L3hfaiaqyGQGBChZr1oDTNYymafopqPTsW3RizvmhjlLVHsDeySMx+MFVpAkAkADM8zeE1XtKUANqBlKoUc/XINJcAVQCLBmmNL+CVNdaosx6XjhFHu0gWN80lgMwIm21J59mGMm1KbP3tVWO9x2AadX5+I0qJgTHkRBkICC6jh6pKdEDAqASLrUJ/ao6+TrrpvivlufqjypGDR2Hsa1jam/4M0nNob+ag5+FlFAB7QbxHXhuunwaENhjeF+6FH5vUAT1fFg8KGtYEVTLtSA9Ni4EtpfbMc7ITyAJNM9EH1ZPBbVOg8BqjnKs9MS1XA00oOAht/qvcHUhVNCc/buVTwcBKiaT9jxFyCD6DjEWZoMuGVoEtmsyIiMqjk6yY0pxZ6peUyV6FjsuvUWTTjLjIHjbqQpM44ciPCQfIoswDjVL0GkQ12i6yhAn9m1YzYSgQW4D9wQXgNvCPtQH9QgqGFer47LhokeHbNTbURsmBkDFgXgGuKSwzkBjuz9JEAharHzaHD4AlFCHWQ3qKvGa+Su1BM9DOIchu/IgnGP/AZwTnpEesJxHgTgpp5YS5UcJNig95JPuOve1EQoyBoM9QBBtx2z59mxL0agIGcFKc24GEDN2MYZneQz2haB1XIy1Kf33kiRUKcBgbrV0S1P724ggOQJD1AF4Wfr5diZ3zjCGkHdpKOYCRDUczUHeyEF9eXdyA5s0e4KGK1OWhi/ZmKHBSwqTjZjyUt1eSnLgMpOKLAAuu5dImre5BcAiiIERXCfugW+DlwF9oXeyAuEoWd7kW1tgWJ6RkYqVmJjWgGcbInBx8XGaPRImdMoE81ohAEBNCLKd32J37hH7glXXa8Yb1BdhoHDV3I/vtmRBIgSdpO4CbwEfhjlo5EQukBLyFKXkepigFKjBtcFglJGNBHqVOYA6EQwJWXmgW7sCccCguc8DKhBuae7H08OwluybMXQCQcOztZyckeRMf9f3f+nn6l/7UvlV6q9a2ShXvVWMmfOpCQeAAL7lS30rlHrasAqkX7qgQWRSyXQhnfBuDw4RAZkS84kck1JL2XkgtgtGXtAffBW1ZGqdztjvz5SgxjJg0y3xKviFWBX6laCnHNGcJl//phKcdwrPrrgThupamQdMtKA4DSKAdVWMal+gVq6FsIvme3J+1SEKgveLJV2JiTxJvDvFD5o/oREoEvb9rTUIMKAhuPgDBcSgRxowBpVNJSqdDgZUANSU/OEPuGmEFMi2WFAhrMJw6XZShFCAtB19HEHgvQ4aMCG8kznIWN4uPngi+FF1wxj+krCQxksJDVfFNzQ010kMwAZ2cAKkX4OdFx9Vi7G5OMhlTkVRlPs+GE89jFKOpGBDXBr8UYr9KADqE8WI9166rYkhCh1gNvgFcVr4i2iI5aDUSMqY4y0qlV3shcpXvF1iw1qoaPlGGrQjg+8ClWMwpz+VvFhS17GdWAQ2hsHrZkVPTZ2wyNIAojDwN+J4cD0LfLc1Uq2+0NoEB1Hml/uVKg8k02xPm60G9OU61QnzrGHTkPR2ReaCZc60McZ+TYE7okimon2c7GkDk8VeXsyuMG2CQIuRcJhsTpyDhfvxpqUHXb0T/+ng1dmR/KvlEDKGmWpGQRWyKPfXbGQlCYzupdCbQDJCR9HT3HHwqeo2voFg50H6gnvKQIHE/sRh2YoRLJL7JtdQ/m623WgLZWjRN2p5p98wi3QNsb/Uv2wstgQSFYuG8AEEiYYEUQjjIiMg7zN+Q6YIbkIHRQ1Oq0teC4pw+JYVRtI2tFjZojQ8XcxegtjVzoK+peghuv9Qn+hdjkBKhfyJS08DQTQgBrZEOrEfO8lcyE9vtupAmwuDJgW0JU6TE9sVAZE8jRuf5f2AQe3Fgd9VIuUaJ+pu34Eo1jnIEtOA0syRTjgYqRHZ6QEOFAq0e20HNTby8vZz/3GzUv3he1tCZJOyDVAWJ7KAUBIBVdwOOVCZl44Y3UWrvwHawYFZFRN7Cbvmqs3g5DMlkiaEipISBIIWVJOxqA1hOB0qgfskdfNrzhvWuiSBfOIMsYxQ+LKWsWix6/i3kgWGwbUxUDYQDzQCKveS3XFQGNAAQup/XFfxXu5TI8ZyYzFuSozvjj2XiSh7/hSxAMZGlI9Wdcn2IR6c71YpwKBOtQCaxBt8aFawZEzPXdBfVm0YyMhgIhQF+gQ5WpT1C5Q+0A67cpMOHggOLA9g5Bq/DrrBU8PpFkrOLAgVL8MFj+AykDqR8HxhIMH1OGplnqIYlSzvvPo13f/xM1zX/8Lf+nWp38DEjk4Bo2ER7oIoVE5E1UNunl/lS40iS2E0tSTukS5sYgKgk1gs3F04tLQPg1PryI6mOM4nDqlCSL7wCHseuZXxevUPrzrgJHOEIDRBsixm6wNi4OpBiGqQ5t/qthQJjt3FBs+rh1MmJI1+l+SwktBWLIJDu5oDlvirzuuUixYOfOkgVfxIfuG34+6SNum8jSbtQgOuVvWDUFsckhmteQBBFijt1NbwhJeijWMXq/gLM17QAcoT2Syz8ia65RiA5CINuDCjhhmzyhGi4dnnWY/gqxaz8fekY556Cz1SCEA5gwGCc7KTyfo8qu7f+K//+KF3bj1n/0/Gpk/90/+m1//lg8MGJDGlv3KLvFctFQ10iNVJpEMOgGD1UAAmZiRTbFnXrmgfNjTNaLGzVLgyJwaxGLNcQCoFMvBEVZ9VY73465aAyB4KR6C1w1Xkd0y7VNdhKhK+nu1h41997jhaEMg5EBlTNKWinui3QPcA/w9YAVto9qFU6+tYgnmG6k0ceXmGICWiNCp1oxkBgDRXqA/B50D8gTlyYFDxq6vwKq2CiiyRzQSl7YpJ5IpT+RmPUfNAEBQKXXpXWcc6derz9To3l0Tg6nAjhShc+LM5N6O2LtzdvYVor04FuEmcQahniiKBikz7hhc7NP78h6sDXvd8q/bbdSf/3df/mZj5xtPPtnv9bIsVKLixEYq1dNdkkSBytyUdLWRYfhFAAa1Bknd6gmDGNMTNHne46TGJhUaCHU4FuT2krFK4yUgRGNPmpy0LIm2eIu4xvA6dAvqOWXKQmX7c6iS/pXJ+7hRfd3gUEcd2GYUpDpxIdpl6T74ZWMraizQLFVs0FNPIxgIyoU+Iw2ixFglLKa9DYUleklirsro4tEF1Q/uPmGtKsEvi49DWwsUp4Hd39tgwU0YbY+eVIi+2FYzBhPqlfs6QZOjvE+LUAS6ji4ZTx2PdCiuL05XIPrQrnPHUD+KExXBAmhjJcHGamGI61FQTOKCPPbheYtQNe0JSCYiUu4sDaWjCr/pA12iS5bGPs2hLr0ndomee6Frxc4//8j3ioxXrknXnCZ5jAghAEdGqpWarwDmqyS69FOd4dQx1s5RMm4NrBEtqA90gGI9tFoC2YDaG3Mx0mGL4nQpOIQ4MltK3Qmq9vD5wOvSbUNbiAbIY6y+SfmU8J4ZArtebYOQYARl0hZ4Uf6wdIFquXbArNKYR4bWAdK1jUrhWLO4k9INIExkI568/wyv+MwLjp/UzMz2zV+NuI9ozloqAgqqLXQTvV6nQ0n0kxUOAnWqnRZWC6CEdoUC63LYagOeBhamPdo21Rgq7oLaSibZHsAD7Dwh4MBRrJoaqIEHfuSX5LhDwwerzIEIlWAB9InC1YUdUjcdB1KH3ie6kX2wkApDQXlktEqSMbNB5IkqVmvFCIIky2BDCiqZWYzR3UmaGU4mOQQrXGZJpJYR7Fuq85yOeli0wgPQNDSEAugQXV+D8AhAXWhv4mBOwJTDbJCZfzz0CKCB6h11Q6WsZ2oNM5d/f3CNu4/vZtKi7fV45sQp+jCK0GWVL4pLUfdTb4M/EP08WBd60q6guYEPK0RSURTX7O+tshxTYJCvcphf6hUfX2CSfEw9IhlCIAkHXqXe7dYY334FAFE8pK9Uo+mUkNTXKuz8RLAUO1Vlqz2t+3oKG5KjpeKe6GSLoNiVZlW1Www2qToAMJjvAf25jLnHzykUGSk5oyk6C+qAuCV1ga5Cx9gxHXo8jOiSPaoPFWJJllVULivvNaDRi+cgZnMYdUmOkpSoUQLXtL1i3I1XSY7RzrKSwYoO36MyoCGrE1nlg8GsTUyIc7lMZuDIQjX/6ZUXoAZkcBe7p8x8EgCzDfm5BNFyhFoY3wantxd6lDniyAXsXR9RtR/XSheoFDOJoU7aeA6MUZTl8IvE/cSjwoPgJaLOYYK2HGQGUphPR7uCtpGYeAwghMBsQWCGPMArNXKBB2T6+dWxbpk3eM4bJ4Cht5q0AXf7yIM/6U40s+HwyK7Q9+A7Y1Z1AR3qEKseck8P9lZg5/cQUQPPvJEbh6ADyWFNahUGyQrMyWalFEA57Ty0N1dk6ibYN/blXajn7Bo6DH2yNPXghbEwxVIF5BEKFgIFyTzKIR1Fl481oOrD0c9zOnkUZLmwO0MJEWMcnHWGf1/+LsaexkIooEOxTtZNdT8+zQiom8q1S0A5FFKPmXJngYG6djIMtf+NVk71GrIwq/TOsAkE0ONEL/rgUKldYG5aleEjAcNZRCCDn4+8D/aI6WHxMtUkbagMHF0PAghUDeyEebvzCtqGfGCdTcGAt2MObETaLyyQaRNPH91yxRcoCaKbg6pZfv7che3XXw6Hh913vevq9ZtdAEOWqiMlYxwHtKvwy1DuhLGHsuPWAzzcAeboKBRVccDlb8wE1KjOm40LtuLw9Gp3WNWETbF1VIaTADLwPHQAtmccSrJfDIqCm0V5ZOVkYVYRrokuwCGrPHmDdFVW0SAiTnhuXi1Nb8xF5+6VkwOD9bL6/utQR7EXmZENsD6i2COqlVqmbusCmKeaSEVnSZ3QQCoALKU2tVHtPxAthjpQm125HYP9yeiT89LhvaMT6gIOj6m/HPfRJo7pII4rg7XI88ADjvsYHgu4VMWFjzwWVYWNY/es8njm6/HJAbijtqwd6Wsk4fGV2XeYkwgCMvlO1D3WfLT+Q0/+0OMPvh3/z/8Cr1/V9/zlL/5Xf/9XHnz4gAQYglXW4an7qXS8Gvzt0er0LtiReQD9hFvC6XoHgb3Z5VFnoyaEO6AVnQUEj1o1BkEN6Hg+fAB3TCYcL0jeG7k2j99sZFMa/3h65a8LC244+mjCyQHq4kkDUQUUUJ+eudWMTSADDCxNRfKubMMArZTXIEPPlxjA5x0vHehQbWhz9QcNaJBNoibtVgyCs4dDqniJKqPj2AWdI+l+gucv9dEOzDGggtQALjjuY7iPfkloOUKgiLrPItmb+U6FHDKfxzSRHEkl0NcfMCwQnnYioRSYom0IIFw5eRl8kHi76yHHeS9rvFq/+nW88PJ/XI+9dzx84xe/77H7Dj70nb/+2c/CpsX4IByt+uUK2KZcHDCFrT+hj8Ag/nUZVHhyPBVhYO4oz8Jhf8Y4SdBaALfmmBdM3AZJP5ywei2I256JxPTS02CBBRxDyUESLeHbT/iIyZreUhfIgeYKh6XK0IG5Ro5j17tULLE6qC/UJ2MpK+baNqznm+FVNBCoia2KI0QAkEO5NCAYmeU0FbywqcMa49C8Ml+lGA+BPfrikmAqEoC56sR5+H3Oe42XwG0q11AOG6BosoJMNXETGZn5vJThVYxUyZFUUx7mGZ8PyUXSfRtI8W2Y0AIuKz4mPAK7x9CSQqgywXoqPtn/15RwH4js1XvafvndH/2fvvrlXu9W6+gIOu5LVCUvd6XbwH2bUrcqDOLwloEFUVY1B9MQzBfW4f7DAzaFBZGmhG+LZjrwU4ZvrlFypGgtU6aM6DHkWXyq9IfWZK9xoAf0EbmCeqeaUqt3ACjBctm9O0TtaAmqENrwbhVphk0oGsqdLaBJcUxp4mjnmvUVgICVHudvlilni1mej6OPAMCEBnhOfgl2n+ke57ZQMwwi/aqWTsBdFpE4KArOnDq92KDbOkpPTt4Toic5LCq9cPG+Hf8k9JB4DlUl1+E9dRQuOXD5SADqfKbz5Y/8wr954PEP3370bc6BxJi6pwj1pdek+zfIbyqAaXxKEtSX1Raey8Aq7ApdsnsXywwCDVaBRhuzLwwflQPLissSREsM9IW1xOct+zVapVbVWqZ2mfi2Uk+uW+3RKhoewYVTdPreRUJl4r6zJHLCgbbQBmKlDqz/MKcAa5BbwMhVNEIYFZSdpWpUvtT+3PPtzEmyIOxz+JFVn6EmnQfucXvE/BFwh+jAiyoM76h8yHGYIAf6ZK6U4y9F5ib6zI6sqm2sH6lKzEB+LlI49ETpWyk5dwPV7eC+xsvfem9A4Ygz3XEaAq8S74+YQ25x1ugRrfnKpoM9V5fsrxD8dmdQE87DCvneOnMDZ4DAFhCS7BisI4TNGppOg+NWjuHPDgA1xKdg34IVHGAbgAfZwmLdExDYS1i1bug4C3iVHrih/hFqeGgF5HNM0SblLp846EzujUJclMScEmg7vDlBoSaei7qfejTqIcclsGlVzrbDTaY0HxYF9qRWaiKvahHGmZr3Kgy4zkQPl6TKDjX/Cgy9+ZBX7CwaboQOgT5kDBUQIYERikt4h9SBNVLjUwH4VvYLf+3b+jcDfkWYJJ+ezBgXbwL7xIXUO28WpVQC9SlqCsGhnjDgqR2+4rsWJtuCG1Ejz2tQkmwDTSagOtBIVWiERcti8WnxToHV0jwnfAj2DiQl7m20PRllqd5wRSVRF0tqj9dt3gAIngeaYdETSBm0KOqL7Dri3PTJRVNFDhgB1QxN+X0lHzY+RFwmtuChygUhMAhQEsEc6el0XoqRzBYItaG8cqo/17q0mraRSAQiVaki897ORCcVxU6AnA53Z5RcEuGDYMbBe1JV3Xuh3CioHphSOqJ6dK7upbpT2Q5wY9DyWXlSVIe8GnThrqgbRwd6RG1k0h3y1Ha5wVPYjHYAAaxBXeIEak0TXh+SJ+fUeWGf6K6//R7A7TtQuuk0mIicwTBpaczlNvxkEBOMuuE+5wepy3OMFGcIEagjnTjBCvPE+bP5ng3N2POvsNEOs8B9kSQxp7ziyuRN4yXHA+ZPRlyW7xgyUKNAAI7aOIInZ/UCsGgqjgLQZ7UcjEAX6gnF3F6slCVeBZ7NedxwllSu/cQzvwD2oWKQAcvBDY6eMfa0BP+epJ6zEVL3CIoNoineR7+OSl5NXzLIAAy64nhiFZ/gJqGiOmEc8dRyRWLzU4FEXjHyCqSiVs4wz2StiZMjg+EcYI52enbQElQHQmsyiYzyCLOvTFEpFiRznRhHLrqqgKOGUfcBqjnuo98v3i+cqyjz7ob5WWkaR3UMFl3KqgbqmbRrKQR2ocaSw7Fb5SydcC5PJFo6vHTNsRNKEmQj+u+647xwWbyfvIi4LdQKXiJyhmWegYHHNxfKJOZtQSjE5oxMA7hUQF2wp6Fhau4NV9I2jp1jhkVpoCETl1OFFFdc9+ubNOybyjk8qbMe6zmZoXhI/Fo5pSmP0sUrLiy/TnWArRO0af2rggXQhcpT89SuCoteM2uQdcIq+mKwRvVWeTSBbXp2jJjSgB3KgMO11TKyjNaCr8pm302LUkwJcxro+ZM7zGDZjGbG5LmVAKEMDECAMqEG5cY61XQ1hfPkNrwFnQN2pH0fRnveFbsvRd8HurCGsV5V1Jl7MGc0LxYQQZ411AcLLIpUJLLtwAwDM/vM4AgsnG8U6/Id4LL8ftclaEfMAMAZKTM3FUKeNpyE6mR3WdbL6PKewTke3sAI9RxdokjlXFjFtyGggCLkolMuRMAFB9xZuSgkRQVlNnytM2+kgLghjsoI9KCac2FgRiXv2KMOnCVxGdpC2HVHAFQJjMnsP/JQuEVtV0Wbl40QHYDMrE4GxENwvd5pB3aryLKzWm8G1MBmbnUfnasG/MU1uQlKIgCuPA2sz5GlJLegIOytQXKYYDUoKGnIhpBT7QmCsuMkEOlRSmO16AUMc8dNJJS5soBcyIkarO6oQc2ApnReulR6g6o5amRGZI4AWlVC0J0a8FYV5MpSceNgBKrNKCMbUmMi23wMQrnBgpgnAB3qaXEYmOfKmC3yJzgYq0lzTF6+m7if5T3SjlDzagFVVhZg4K10SH23ZqrVhBmV6EgC5EIp1EEnCqAndam4mjd0FSNVlO/6cOILQFXyc7CPjqwN4jB5boHTKP2xK0NdhSa10CvIYsBOwYrJsgXdI+1mZjA7xi9SoQBfBR5JicYhCK9ZaJE1yMHuytRJy3FWG4UCWBeaViWOjj+aGBTjZEYVSQ3yAGxxVAp5Bgg0KQP31pDf7h0hkK3UlBsCOHT159KpjdmXp+7IwcozwMAcMNEMuVB35YaG0CLrYoNqmNeEGpi7MqdlyCQz2TALYQfewqgu2XGpMBYNobtNZhxBUCEVRNtRI5rVaxm/gupXJ867qQs9YmGtBDaWzaYIFpypY/F7XTRxSBc5YxYBAIsATw1tqPRRJDJxOdgbsMigX9n+V375K9USn00sacPiGXcJWFJ9oTndJFW2RKd3hDZYjjU6KDxUCy8NfYxTXxw6yXkF7GtJqh3BILRCaAoGAQpEvarc8kaCCAWwQTbEbLjNzzxgGVhz9tPCW5tEbcl0Icg6cB6+D/RP5+hwYF9wYZtutCU2JalPtTFWAv1o1zYgmkCqJuRBmZAJmVST6kAONqGWl01XDtSkHDAhB4KBgHEYAgMMCs5U1f5GudRH6aw6dHapSr3IRKMMrDyGxLDIpnTkPL+LFuAxCJHoAD0pE5pAPShzAhC9wAqVfDYPASilHtCcV93HUJNmxbYfxUNHuGZtJYBIYHkCrqJQghX52VKYmJu6qSdTHVb/S7x8BlYSG6nQkgPQhoMVBXXI+tR5QRDYMe9IffJYrBTuj2gEdSdGaSqqirvEruHe+cNjYBNsSePvlUJDaJ8hxfpa0PCwnSlLqdAn1untuQw2IzADmtDM9XT8pjXYecaDkp3TxZMKOiRctrMsUKOq3jHWi/iIUHPlzibKJpCb1YG6x7yPXMrMg0jSJLJy8I3CaOaYScdiCKffwtGvklTCChvILVbTFzKR9AAzIoCBLCt2ntVWFAO0AVq1JXCiDxRQEOtAgwRtGafIOrDi+xHQJRrzNFR5qBw4828QoX6VST7zLLW8v1yRfU41wZJ3mFOP+/rFhgaRAosukTAgF9kQCqCs1ILqEa6SOnR0AZ/5VPqOeIH2+qB9s3nK+uIV6d5ZTyTUgDWB2qhWwvATARlVE7t307FqGUgglfWazIBM8xhshleJW1SWGiBKQJnbVqa+M55uqghow1HaVq5sfqe6QnfiQ//+opI0w8k6HhfCgY9sLJN26ERJLho+ByNWqKFyMtB6KsHMYiByKim16qxiRlyQlZus6b0AAkuhhHdlBOKmU2jFDGgS3cqOmtbRUUb6LLABWhWoN/duhGIVNXJSeOFMc6JRVZUUnll6792R9rx2OI7Wv0OHplti2+ZLYyGnHigdPlUa9uiWrljCX9UUC4gI1cTzsvMV39ms2xNs3t2GhOPoh3T1yIMvJZNQA2osvmQaBNlTlRB6erAdfFfsz2lnhLeP7QMBo/yqQYuO33aGkXbdVJfzoJM8ymsROdR0XiQvUttQdidCmRweN7zNGbgFXhS2heYKlUQhQ3f2xRYVyiXR7nKge1puF5ZMJBwCKr7hs7NmbMRIdTfA+7KCiNKhUDCB+F96gAzTATxTCYC8Du5LFwlGVIk4LbOxWsFzUaPyZefxuwpRKoTAtB2FzCGbH/5Usc+mc9sBgNint091ZJsC+9KucA6oTwehqY25EuVNBSK3Izk3UdObKDQjzn4z2GwGFIEG2CJGNT/qwmFy1JaELrFVreuJWfH456584P/9jV+8/9s6IV/wdB6FRE9OqowomZaFBvUj81QLrWpE+6zGbgP3HFSpXIB0Mt2TN6KkbkO3gX5SaJpAXnCdG/vLsBcTykcbul5Vi6XntAvG7QSZAdCE5HqudwUEdldpcC1wPmcXm0hMphlvgLe1/ujMEtgFOxP2CusD7TeQBfHk0EAvnOxsVdP7InCRbNqsqgxvHBCoiRfA8xhnzEUgGisMsSJwbP4TCL3ykS/fYLnUjzfntvVkn5LAfnK4gGDrZF5egs25xBd090wiBqWVwjcJqU7eC9yQOwbEcFPXkEbDlRifAMAgogoISRCBAlmHwt0Vpb4YKsDIlChAiqCrBs1kJGSVN4yZIYlz0UuOKVwVEdqjHGypSr0rO5F+p4mczgTMaAHHvb1HNb1zIUI9QxcoFjLL3X1QJraMzVkFaQg0DJ3kUHjBu87mxF4sePPJD+gvPmVXdyt1dXZ4j0ENzLRwWC2onboBqgCrXLK5J1PCpRJVoNqZBd28SX0bJ4IM/pC7HMdkRlUvNssyM7NrGbogqFLsrfICwx3mJQUGEZyJYCSK5H2DQB2wWTNX8EPpeO28BXD44SaPFw7uAweqasOx+4dCZgBgjZo5RkdXABm4JVxy23rjqB0GbIkXya0ZVdAHyKV8hSnFgigmhEbY1zu/3Lnx1LcVjTwLWbUnhBDG6CRGX57bBmTpPkOwCHPvFYG245ZwS9qEXj4fG9E2OLCHz59zSwsu3SHoMrhF7aPa4QZ8VGYTE+LAeItqCoK6jmUMNkcg2CA66y5akgCRyGQNogbfB/tJLZCEvthIFY3M504oi8C+EMVt+jJuVMHVMWyUSh2AgEOqgHx56PCbBBRz+uwCh8eutVka990IqoHQpDc0jHmdcx3Jhqm3wq6NrqtmAtBX61r5/vjud/3Sb/6r+Ep572XjIBNnvDxf9V8Fl0Gzyv8pgBkSqc4hqO9qaqi4CAScKKEq6m9YB/eMp+/6xQaBEALCAnOEELiaCemMQG55vFdhn5ggpJqAF9KrAQ86KPXBYpVaezlQE3tnSFJKVGnSrHOQTVJfwTPPniGmZqt68EX1owS1AXfuzOWUrq5jQRymcuycCgJ6Mwla3lxSZDSCgZ5Md8ho3r+L2KJmg+C22KKHCNjyOVOTwoAbIgk9Y1/o4r2/8vL3/eMvX/7j937+B/70c5/5DJ2Cc4J/CEcslk36VnSazeBGddWJbmqEtgox2oAvwMHeeNj0HSPr31QkFXk8KvEIK+YmeWUdH6btzC7huw5IgeFBs5donHFmkeSuKPFV17fSakQCg80EjKivct45DSjkYNPQGDjtB62sEZZqBl0pW5UwLA6xFdV1l8KOzcmfEARvOzcezj8XVarenXr6JqBPg+8B7iXyikYxZbpKcRQOdDdDMNK4vDhghQDWDe3kBRjlt8WS2Y3i1sG5Tz/zwtM3D8phRqdGldsnQJyLamEmzyVlyCVzxKSzmEpaAQWqIo8q3c44zwYDy5EyaShuNx6AK7CKBvC0Ek/Db2Ekd7yiVQhOUkHWh1al6U4GQT7gbGbjztgqAHcUUkUIt2m78PtEcRmDzTHUVzzvnAAWvUZrkrUR1eXYzM6lbH76whSqeKpEl0yEx+UL0nqK0e3cbC2NXeOGPOFJIKhs9w42YP0Iz5KvQk/Av4O4MKAvWXYOIArR7e40Jh9BRAdqJEUzAgCBpqq03yQU4C3jN/j0/jue/cgT0ayMLonuFdVptTlMCQ/dC9Xmv7dAz83SdgBzaF9RYrxDzF0G1YCmLKfvDhqwiSxxuDsdAymlqogtSPPhu52RzJi5AkgqkCQDaJSJhsBwlHJlwD5Wru6wSvO1A1xwXTFqGEksQW7uBnOjQPSgq5H3GuAouZDB5hgC2AAPNzMFJnhq5+S4EsxNae4NACqdPis0pfqUgEiXulAHWpqgJANgBXBb2AloTixf+YikZEH+LZwQFDSMCKoCRtMq1SyDcCj/rTQzDoBBYzachXA6kIa2+CXaFdi3Mb5XaJID2qQ5ELw/f9SPX04Qc4vBbRQqgRJMNhQzAzJowk5L81FSqap+WId6Dfac4RrYZQRlRiAYSx9E549IsivhYWaAEMB7URXCmNMCWF5F9y5/XQJQVMyfAwaCjYMOkKJyoR7QELPqFC+rDTSkjWgb7o44OM5w/+CJZ54h+cx73h1bLQDAdK1dGexcqIKshzPVNaziMxHHJnDT7vQW9Rjwujy6uw97pO2XXnzH3u4Ljz22d+EyBL5ieI8UOGSwWWFIzTbQBYMa4iye2jFwYA/NXZwdIDjjOwVRAPXjnwigFVBX6joKW822E4G9CAeGxY0J4NBVLDlV5dfbb//1l54914p//G0Mgok7oJn2kMjCuwgl8AX4SylBx0CdAliRTo/C8RNjK6UzjLNH9Xazq6Zfhj0d8WHocSLHXLVjEMOzpDMCKCgAOwZJXePZx+xWlBB5MsmxUU2N18G88NyNW8/t+/c+EloBzrBnekl6nrgpRafZkFhGGMbIVLaHKQp9dyeNTZUXfeFSEGt020QgBiFAybrXnHt4jWzQ6pT5kEMHAkaJL6e0IBOVICREwKAgZoNiFtUVN/r9bwa8lIUb/fl+WA3qlBMkRIggNfhn6okyLaOPPCEUgG3yIvROOI9kBgB3XesX6vSvD1VL3YAOhxf0lUpw26f24IfrP5UxB88ZayAx9d6qxLbYLQ+/dKP52mHvlc7Nf/H1xs12qstZU0ez4S1LYF+6Je0LBQFRqx265dA+dDCg91Efk+yzs9H99dfe84++cv53X3MvCLFO1KmaeGE+uUtyg/Sc8PTyNqDq7TZ4AbxEu0ReIi/CzhHbYhNoADmQqYp4Hr3qag3Wb3Su/8vnele6tSvtg6/d9OIsArkG54QI+3rQ/0j/JefrwJB8capzKoX5zB9VL2q3uruffKl7rdO40j54+jpCtIviRaAhmNLCtdaEnuSeaHgShBoYBs1rXu88+b88d+Gbt+JugVfB3yZ/OYTPZ7wBRGnMQzH6YaRkHCMiEqT4kGxr8fwhsqR0qHSwFw9e2Ndhp/a519v/9Gk7PEEYIismVrsQ7EKwZrW1j+8kHGkZq2sblXgwHxqRTIEkGEADSEagRpNXj7t48dy7XlRUuX/u3KLbpp4VsJEzmgE1cpvI4RTvgS5KV0ZNUt3wbb0ushzb24PjWRu8FvycCKacd+j0ttD2zYRXExHmVJitYgDY+ebB+3/yc0/stl//tvu7riuP7Fy5mFqqkOP+GwEF0YF6Y0fsE0PgAeTO7Wn22XlofPCe7B/rg93Op8yUAVuDDcpz53n6gaNjJzlXCHjV+DmgSIs1IgdJMNXDMgFwaeRPpxMiqoJmnYjOoModgAuv7L3r6aut52/c+/tXvrHTfOH/9F2Hjy1cHWuFy70b9Xv0rwsfYvgA4nkN400G75+9RYULB714efe9n3+l+cXX7v3SlW/c13rx//rtB09eQB2okwXQdY1FiG4WUewRreTrA1EXSojYjfrlre0/87VrnRe/+NkH3qfmOZqAgUoxM5+v8mRUH1SmqmF9IYs7rvcKC2s4AVBw5kBiGO4yiDj//K2t37n+jl77PT//7MH51qe/58H9rTw1t5ZQTjQQauZ5dSiPi9u/urbRoi7RLgVeNDtv3IFtgS2wKjmQAQGiRrTCWREffO21h195NS8WvCSmuogpzU9/OQkI1IDz5AUwF6uqHFvgQ0eFhAjaweE7n37+nr3bjbw++HOEvxaP1Mye4Jq5RCSho3hLfrC5lBx5VCxmn7aqCZDn9thh0doti53stQcvlq0MlkSYKUBRKiFRPfoeNEovWseOIAht+E16gsuKQPno9tM/9O7rD+4c3uyhaSPOEsoQGHYCWytHjQrAnsXPiIdIOrTSpxUrEarU5cE/MCAAuVifKjuNHvn8+Va3lbX2eo8eFtnIdD3qJNPCnVbB6LjlPggW423oV93/meOLQu8oQFGCz5lLIwRHmYUX7m21t6xxWDxyuxvyAEBV0lYObBsvg+eg+ulsJikQvLtKOQYCDdJlV1F7YWu79sRe5/x7fu1rO7/12XjYHu2KUwkZmKVw4EjnoJvz3YaLSgnrYm1BqbLVQKEW47tfvlY/6Hbu3X7hkfNlXIW+qQm7CNuCchHTtoqZWFnboFUeyMQh4u7hwSsCy3hrb/fcvffNvmr2frtxeEZrgU0oTHrzDHqH+MUBEYiA8rBdUs+0WtFLhGzgdrkS0CVaAoAo9DB93hFUyttid3nR6dNCQI+oz/DEVX/Yv6/1C3/pA+eebB082jp8rbfzcIvdBZTAk3DwUE6ht6mqIUojQxVQuH7z/PZ3v3Sl//nXW0+9Y+oCN2IHNOlwhaZaj/pczK5WqyXRRp68LgVpokzA7tsv3szy9qOtr33P224+d/vc5dbRU3PZFl2ES5FwWIRcR2vkRPNofAccEeeoUoJfBX5O9vag7xYegQJYUstCb6Ph9uMXbuXZ7kOtr/3QE7efuX3uviaOQpdZmSXQAptEQXR9kJ62GQwyuuc7xo+2UYEd6EXp96hXYG3kqH/l4YfwzRfeW88/m2XSzLDayceNKRyoInEDdQ/1ZLl8VkSgTx2nWz4F9h4692+/5YHah+6/tB0O2rQHGssdU0NITq5mMhuIDQWlljSIsMUxGJNN2tre2XvnuyRt7cxVww2pyS8UU3J6Ft7CAYiGBmxLzIbhXpPX8EGLO87bg/io/Pz2M0+9d3drS1lmA0FO7gm3wJYEVuedEYMNQY/ONtSBxUqB3SwIDEo8zpkA3srCn334kCC4fb4uCM706KOpAlZrR7pTJAvcyUJ4oPH4hy7sHssqHXArbcHM4oEnaLGSoK/Ivr6CZYuydJoWAhaP+kfBt/OL770IoPct9269/544vkMFeLMSSRwUtBIowgEXIz06I90FJ8vU4KUxVeMYcY6EPvxpx8vgt5g+DDZgjsVRGxS8mZ1/4hJqKt7e2Pojl2bsHyKqGJwaUDO1xA7V9tNXiZ8BB7qOfN4RXgDlzpumZ6GvOq4QfQzeL3B44eJnn/rWnQceKLI8Gzq6zSzGCBxVjx/9UDnGq08Hz89LvN/QGoY6NERSnenNW3tCr7LdrfMc2b/cvPhnnnCyC2aXBsFEiWBh6QVoKwzEBi31ILDqqZm1/IX3vgtAWNgRd62gs51q1tHrDE2ovug8ofOOB8XbA82939p6+j3vEyaTAAviNeGhYSBQFXFUAxzqutpCcZZuQSCKBZT5XB15dNyq/tcQjk3rE0Fjd9+82pjRfuCRL7ce8cs21xpFqOUM0L6zWGAxICB8PcOXy5WZNJLHVsIU5fOkoJv8zcZSJSpBVn0+6TshaCXjbTHBPj6pahwfoGEDDqHfcT5PvZd6XGyk2Dmk+nSy9NxmZFDd0V7BfLIaKtU/m3jPg972wdfkXyGfAfbd4rTkyvMM73h7GyDGqiQcM1JhUnIceTia4IcMj7nTmDtbRMMQwT40eTLjIdYsMapWHTV39XtHsICCp/sshtpGdYxPeWQVhrZCo8aroc25JTCMn0pxRWKQar8inKDBthhaS/zqFJHJ3mb82tEU0rClE3gdKjWoAuZARxLVAbup4a2LYEADKJTKziR4X2omDj4HjqjTpTsMzOSgHDz07PrmK7jIYoM8v7g6JAGgDjPzPWcxz1cgXCc/F09CtbtSsFDy+bpKa5kx0zSts4rV/rPCNJtXtnLsCuCadBN8TvgW4mEp56JwFcLydHMI1ccG6b8c6IuZDUsfAi4ckM/D/4D+sqw7kN+zmkAMDVnjYmMkGKZibUefQtC224fNn5RyWBNs2WAFBqo+nU50J4zxyyB4ITZXaNxI27Bx2bzoES5uoJzrCkoMVzjozfhuc05932OX8jGhCbQX3cxvA3vGy8PbdYjuoiLDqQhgE2jIa8AeU06UA/Rl0VIPDQGqOdIriA0w0i0oiJHcN12XvUxcCehsXr8irAUm1IQwwHPaedP+bA52tYXfJXdPcgphesmpVVx36VQKK6yY+Raq2Yjw18yuC48ze4rlPZWAnPW8zJEn+7urAhKbY/fWMIPKgBK8AX1V+hrsOhST1sRAXx5LyDguNsbkh8wsnuvhu4IeI1uyFpWPndkpNIDOBnu8pDPJM0R9WbREfhYcucQtdWpTtgmxwbhCcXFZsm40BhMUsUJG90XoHvCluZcKYBd+1e3yUEtah2+f5wx1IavuRKsrMTIVgCJQkGlcIAJQM7aXWLGnv0JRhgg/dHvN8ErUFeJwWMd48wTkrBHNpCsH0iCjnaPLp8gFVAJfAF49SdlvJ5loVKUYU/JAgErltiqge/m9Ka7kJFiuaozuDFmVrPMs/FXw3cC7nVvQ8eNahnTjsmJyetPqEAC6CtoedA38MvQN4QBVYi1X2kQBdw8hABiG4U6dawXQGiwedryvhocit+CNyiM1SW44IyN9UxDdRMCUiXUql/YdDqaEdZVQnFHKcB4GYuPg1QN00uZUAA8Xkg5E4BZxEOdbyoTb9Nc07r1XLjtMnYDar1Lzp4X4/mv7y798nTifPNHrEfsVC2BlNpxlF/4Dxzmb6dIde0p1Wkt6w3u39oFhRiOBCNxODTQCyFvAnHwMktPz36HbQly0qCYMI6AV9Guyl+jXxEPIMU5TMQpM3Luxy/4G4i4J7BBtWuXMTHwrDu06y7GjhsgXic9IcZGqO39GkftYwts4uFDs03enmzrvjfPA0KiIsmd8PrF1FcCx287E/v4+gBhjqrYx8WXgCvBl4f3AA+MLnwCwU23NY+6EypY2c6V0qf2Ilfbv8YYsWd20Pv014QXoOqy3wmFoSIxydD3JLMsAQIoxVh8dFVCoQY9Cj7tfdtYMh1IHwAy7NEF1XAfpvTg5RCGQDbKGimHX92R9je3Ds2cUq6Pg3grlRwfX7Ty8s8JgLo4hEXCIIS06AwnS3SdGMcMMNo70CkLHssz2X90HsPPQTtLX0x/UHRg3Km79qFlhA0FoztaTbCgYCRgV5YsTJQa9eHSyF1pRyeU8ec1AIxQ12YploRA2FZwXge4grxuAa3owqk1qtRm1EgwgAg1STHyGYDqmB3XJYljd79jIDnqxYEalz6JZI1hFURzf2rIQYJBm08/4eDsTJsZgRu2krYuFUGU4HXbZQJpVScQ+Fq5rJI2YwYFI+ODg5TPX0XykrG4BjFBXrAjLVjGIZBYARp/wQpAMNBDywVgc3TKT6hhE6VVrbfF+6Km9OC04pvwJEChY1Ys5KvVgLCrhknDM2//mPjYV0jAEATPLn6z3Xu1q/270BiUib9bDHw293+7qIHUL3yH/gxC2h78e0H7B425ce+B6avwSwdrjDfs2dT/Z00HqWEz1YgoHwM/FuH/awJBJfSblCzusP9Qonu35EkE8wIJeRPK3ga8Wp7eerNALAkYLzVCW0fs+/vf35vl3AWHWK13T214PMgv5o3V9v3vHi58vRqu7bvbHQ/bo/Li0s+rFCuui8Z6GvsV7v9gfXxd3wVisvC6Ow8jvzWpPwmdaOk7ci6O4l+3t7RhjrVbr9/tZlrm7u3c6nZM1t0Jl6td3Cr8GbEo5m8CoF2VZhhBIdrvdXu/UlLmtqB+hnhMOll9b4bL0X5TxbcNfvxH4O8Bu2ndHvej1eu5er9d7vV5/mtSLYHZkPVO5eG4JsPeq/L+5PnfyXkzhReC3oHkDOz4WZlbNpbKcSiQzMh9zRhZJutUl6DulZ1MX07xeiCjM/gbDV+d/N21dWM0ez3EZQIEbfX9hcS8EiLQtY8+nSmZ9p/hfK+Y+gwtv8dtejKkZlef57NXN0ZlzRmnkcTjg7/Py/1X469Jva7S6A/ifCB8v5x6P1tKLw8PDEEKj0Zi9uge9qAyZS9YFAHy3/G85fhdT6+KOjkU1o+4LuFjwtU784skc6xT+PPQfRzfXcYfuiXsxEBt5nn/kIx/J85xkURR5njcajc9+9rMvvPDCCdo60e4jO/3GMeqFmb3yyisPPvjgjRs3er3eF77whanA+YAtsuL/LKKWb6JCahmYcRiOLCPpbBHjY3Ht2jUADzzwwKuvvvqZz3zm6GYAwJa9P8fDAAq83NYXly4PAVqd4Hu8F8c+movxXrzyyiv33HPP/v7+3t7eH/zBHwwvGUSqXLa/2MJ3ETjEb9+IP5MaR7RigPPMXkiI48aHhb1YsC4I3s8fv6T/DYCb/Kcv4/+QHsMy9Tur4jKzbAon9hRNzSh3f+CBB770pS8NezF6lNX4eIZ7AJS41tcLC7cqCpJNrwsCJi1I0lpLL15++eWHH3741VdfLcvyy1/+8tAtUXXE6vZ4wH0ZLpR6LXldzPj7nRiLo0ffzx+/7D9M1G/in7yEH08trHYMlIJkmCE2TtyLgdiovD2tVuvw8LAShhcvXnz44YdPKTYImCG8lpWd4gzqvYz3AkCe52VZXrx48Y/9sT928+bNL335afKdUACuP8C/vo3vM9lt/sJr8e8sNvATYGnhC8E6KwxdB/zVEO4bBp9csdCLScXSRr04ODiQVK/Xy7K85557Lly4UBTFYbtnPCcI6N2HH73k/1uIN+xnXsLfXPx+SYQbht9g7JTpYzHViylclTpxNjXV+Fjs7Oy4+zve8Y6XXnpp0BgYmIMgwja++7L/R5TR4g38TEqr2GF4LZMVnsbSvaAXTrxGjRNFzOvF4nUR1Mz8HgDBksK8CFhpU+VJSL5m+mXZzOKSC972YkzNqO3t7fPnzz/88MPVcLhooECw/oD9zUv+v4Z4g/+fl/GfzntSFTKU3zT713W/FYtOf2D9J0B9gdkC0qK19OKd73znxYsXDw4Obt269ef+3J977fXXP/3p3yLOAZeJd92Pv3RB3xPUuMl/tHRdgAyvZfgNlt2JVp3xWMycUUGXgIrm5KSbJ/G08ZNzAhxP3IvB8Pb7/a9//etlWW5tbe3v7zebza9+9av7+/t5nksaMywww0XDFgDHYYlbyyW5q/+pzilzyhIx3ouDg4NOp1MF0v3Mz/zMr/7qr/2Nv/G3iP/M8V3E9VwPbeuyYG38/tLbCtBBLP5LaJWksKvAX5c4KAgIyHtpcSzjvbh+/Xqz2TSzra2tn/3Zn/3MZz77kz/594j/i/Ae4CXqu4kaQKZwi0m9z5f6kp+4FzNuOTsdYroXh4eHMcbPfe5z9Xr9qaeeOjw8fOml64YfEe8HngfeOSyvkjpJdE39T3WY5NdY0gsCJedGps5cF+12+6GHHup2ezdv3gQNaFKXgNbIKJvWKO/v9TQ5JST9Six/fY56vuBtL8bUjGq1Wq+88kq9Xv/Jn/zJp59++h/97D+lPkp+C/FE0B8NOg8xcBGdrACp7H8u6ocpH6u2KfSEv4PeAgvDWnrx8ssvt1qtra2tVqv1kz/5k5/61Kd+8zd/n/rbrh8AHobquXIAKetCUvdX+vg1oaupv5/ZWHQ6nYceeqjX7d24eRMkVCNaYj70p59883Tgp2L8hDCz8tmJe7FI0c+y7PLly0VR3Lx5c3i1Ne29GR4CUOLVjn81QXU6hahcB3Z2dj7+8Y+//vrrn/70Z4HvAd8GYAtqyITYwzMH/umlvZgX97IKTvUednZ2Pvaxj71+5cq/G/WC2hLqMiD28cx+Qi9QRQTeueFotVof/ehHb9y48Xu/91Xie2SPEWoNeuHpvRjizkytZrP5wQ9+8Pbt21/92nOGB8gLwkMtXKprZ6WxuLMYn1HER8W3gdwS6mJKL4Ye52nP89z4281ge3v74x//+JUrV4ar+zGAW6iqGKfPqFT/+YYwNqOeDXwfcB7ot/BwXffo1DNqE0t+1UgqktnIaaYUd9PdAoIhgJNWjfBWL+4E3iy9AMFASMyoStl/A/bizTEWb4ZeGJgNI0QiBmHN9kbrxVt4C2/hLbyFt/AW3sJbeAtv4S2cGAMj1Y9+9q94ALcztpCmDUm7i2j+1oiKd2dx4OgnPvwJAD/y2R/ZRAPkyj6V2UucadNjVfH9ciw+FhdUiUlB1YsffeVHZzcDwE3h1Ckom8ZgLD7zV/hK0G/DblmViXpE2IAJfqSqaMHo09FHksbJMIwWQub3RP1g1CNEfvREASwYDwrrro0Sq+rFj/3Yj001skoGwhgt4KjoQpZl6R+NylCPPhp1fPRRCKEiazn+kZlVpEnHX9f4R//wH/5DAD/6yo+oQ+2eWST8ScHouVkNVqcyah9oCzPXRUXZcjPZ4H8OtoWkKkERupnK76mW2/k5U64ko1AgRrEQo376Q/8dNrZHnRmqsRiVaSLPkfUVvs8m1d28549gYDIj02aa4FxQs6FqmWobr3xpkR51x0MMEsGvBPtK4AHEAQNIta9V++B4wOu84NfxP5pZMAM93Azli66L4vmjdAv26fuyYvM0ipjLuH/6CvcrVbI5/rgFXyeA0tdfZnbtULA+0GfsOIMW793yZEcvYckxrBKUXITOzObeN1PMYXUZgGjpUX9vCIwxkwWA6cFeRA1MIyI9OQzYkl2k5cuv3RwYiZ5rDFOfA0C+flbgaUTXnHLls3FHd4nwuYADCAKNY8Tdx2vgTO2Ao7PzeN1mMxskBUh8LmgX6sEJAepBu87+BgPETlnQd6ZESbnnyUTRLIliSizZcldAjESfC46kFFKrkVZ3nJmzNxOORPkqCtmiUbRB7D2VAfkb6P0vx0BsyKV26vs6QkurFyNPhUzYIXbMc2Erhf13Yy0phX61kQ22Mx+HJLlqWqGu6YnghejJb6HpOCfUT5EJejrQj7avES3uuN1pntgYXTn67hHzaHX9dYVvBLRlEew4doWkNMpTIYW9+PQKx+BZxx51/C8rPYtR6dV/3yhwraBtpBdHkScSVYNYUm98+rZvIgzWM2XoAv1VDrMAMqIFX432Pw057YKxVXEVgHWgfkdMMwKAiCFl56wmVJKkuWLNw9UbohXY9ohGYMt4kbhEbBtqM2olbBoTe/2sSkHzLC0jVeO4zJCkknpG2JVuQ3vLGXxPA9WcASQMHNawFyBp4OhKrWBxZ01bZXoihSEfVjq6i+EAk88KCi5LPROzTK7Laa7UQ7MQ7+4XuiLG+u1QB6ytZtxgE9alr7H6CuGNmG3nEwogwRbRO/skKoJgQZWLmJgFeN0H128IDvaJtAI+Cs6MA7KlmpCLThRAT+gZ4tkl/I38FmY28g/HGCuf7fBTGpC5Z1lWlmUU3Af+5ymZ4V6dT5Rdt/JF4F2+aWGoP4dYyA6FA+CQ3kHoSl16v2TXUBCFUI6omzieJa4RQcBYbbx0Su81mra8iMmlWKWms2UsqSKyxwG35N13VpYrdbnNLzk/87bJV5NMDTK4s97ZtWNSXHaButBcZSkaWEPor2leEWgxbGczGPdqQpM4POO3T0jqO/rm0XHMtEJWu7NY32iUijTYm9J2nAw2JJMbHJ0MqIN1opT6QI8qnNEqwo3NWddmur4rx3j16kir12rvv3D5qd/4zfqP/8SL//Qf/+72znUGM1aipcJRvBAA0F16VnpcrG9Yh3o7ATkGOxQdZXREqSR7QL/kvnAV7AR03XsluoY+1RUKsu8sBp7b0Qs+XpXyDExbKlJtNKSYUQRyMTc0wejom3pCAQ5LM9wR6EAIxuAINCDGZFkQkn3cApNrJCvILUnGmKiVSy/f1ZgUG4LaYLpBXFCb6qzH7+AB3CabVc3ZY5OTYIvqnYUhewwCyB7hE0fI8R8IKpPqG/RAE2DffCGR9QSyMDfWMiMyoqlQUj2pB2w4/3SmwuHuFgIhXuL3/3t/4kOPv+05fnXvvlfezduP/eB/+C9+67d2x9iwj4chuDuuCi8T79xgy6uHV50Y/FaxcecEpZ3SRNxLXaw8B4qI8tKiwR2FeUH0iB7Z9dgFe1Y5ydhx9oguVIiRYyrK5LOOalZPt2dVSWN9T4zz8wDLxkomE8ioTGiRJVU4e0Rfd0b/2IfooCMQZPr+Xh1R0uKoKvtj2uFsBSVGb2ptA0BBdZxbs3MURhBhUToUDtfAeCJQudt2QGOuLk0RmdAC9s+Q8qZqXU8L4jAEEdImj70SVCTLDGJACT9nBAmBUA7kZAu8vZFckJmxtkeVmT0yyB8P/n26/tjr/wOff/ov3xP99+/7m+/5D//gGx/54pc/+a4nnTMExsiprgh+VXxEqq98aCGhGtTHiTXESocTBONQWSMAWkVNb6jjyFoFCA64NaFtoDQvZX2gZ+zAu5Ed6BDYJ9pS19URurQ+BTGSEQQqjUeojPSrTTYln+sYRJtaXUMZlYkZ0QBd7Jt6rj6wSjjT6UEBMqTRHlcQtUJ9+1UCtBR4XHecDTfGu52jbCUcExuC2mRDCItM+Szo+0IvUYgvAeuwHSr3pTkJ1mDsimdS0n0IsgcOCitOqxrVBcocNRDHC2GuCdFQJk87g/Jq7i9eANUlknxhafgTwt1Jjkx5A0N/ZdODI2f5QeGjjh18pvhsZIQRhqsXe5++/ss/+NUv/c6D99/Y2TmuZIwqK1DC1aBXxHeseIgwYRvWZNyPaJ8y1YPgVCTEonfuEjIiqzSLSvBg5K+qhkIOKwL68huOA6kPdkwdoAt0ZG2hB49u0VgScagsjtSD4SvWCly8Y8hsiT+YQKCaUhPYIw5XfsIZg6CRo2o7i/0h1TElUSqbJTs2pHRLwRsCM0IBWAJtYE7JW0HoUXu+HmMRHU3atikcLaMFUJA1qaIysp5RcIL1A8PRs8aFx+DnLFe2SUdz6fLEEyYRwPkifxJiiQ0Z/QYrcFJsoDJVnRM/Cn2rKq0oVibzykzT9Oe++4H7vr7be0B6TeN38wmOcZE0mJ71+Gh1nzQE8BxQp8jQCN49Wc20MRAKycf/SLodcyXZIDqLRBADkLtalNFbAOCMHMVwORDByFjGWET2TD1n30xEmzoUe1Kf6gGF0F9RFaCYV/tqwoEDhngiyXS2cECHYN8ZWFloPVSTx30QNDKAIJa2uKbh0cUUw7JD7lEjjqzcbw7MjiBTh2iIx1NUBHQc++sLuQ3GLSqs8k6b8p5Z74zEtwB0SXHkLTiecKAGYlauEEZVbQLUzPiW4wciFaAscYdT3TmrsMzsdhSihw0JvBjjKGNjIDkIPkz+AP1RkLMUfOLwoeYv/+UP6JaFfz0o0jmeJIhhhNXgzldNr0uPImkBZ+A5oo5B7GyNaAHJZdVnggAt9fii2TU/Jl0aY3cd1D4anf8JBByZXOiCC6bgugQEmAvRYlmwgAqqD/zdlTpDZWNRXwthUf4GyQVRKRasXqVMCqLRs4HbhkYYSIiQ6/jqmwliBRX9D4W2AQARaAPnJl6hHDgAOmtN04imfhUtmgiCsBbUT459OxUESd35HubqihwIpioy3ARW5udqOlJUFfNPoyhWFtEqmHZvhixQB8iFbBCiBacKkTEl8JwUs/TpvFIuyEkwHmuLTHof/KPQpcqcN/PRRA3YFiQ9LnyJ7hPu13FWKAAqaU9b+YAjX7jaCdTAnYrJamwXbkE9X8AcsxQCYBCSfK6cUZdzHtyNVamEuY+WsXpuVeMKpJlMCPQcjEBaWbDqZgMO+JDaQpVI1YDpwNnZBqYwodoJjGPeC0oUDMrdgiEH+8nqkwEhOVA34gwSYUQ3ceGUGV658r2tuv/o97n5KuoKjTGWqhI4cHRtzcw2gtpAHVzFyMwa0OCmiRSrjrI09eHycX12IpENVE3Khu+laWwN7d0c/COgmsA2xnnDYnYwAW9DQayRTSAXHIqe4KsAAA+ydBoDgf1kfwzBJqLLCkdMHS25BjacLek75R90NY5SGeZ+a8u8EJ4Un1VFZldhRsa4hFcRXoMem9sNEWi6bZsynyIbYAC3qN1T2VpoVKK5Qsk2MVam80U3HdqtABEdKFAF5GB0OZITNYY3E5X7CjFa0dOruXtuQabod1fxKlXHMrA0jb/PFCy15I3Bj1uobOLrJGUzOLYI0i25NCq9Rs5nyTq6TpADReJdgbrDYGO3nSs2KHlbViMI9qADRz/ZlrcCnAXQpnZWEUcEWkDvdBnCdBnAyqs5iIcZGByqQG+SFtAdpDuND+qUzURZZcWv7iDlE4mmQ3lz5AgdGrkE+qxjiBiJDtSTAsxgkak5rplx2XYzfAZQrpA+qUzcsUCqJHpQDyyWk745AFf2EOL3RbyNCEk6ooKsDlyAv038KnBMyRhcVvmWCvBr1IPCTA8HwRa5HWBzsn8bVA/sLG/V3NYaUteFlJ58I9PSCJHhpdABKjPqSCQLqQ8a3YXZCvkNKqo5mdI+C1tEjSyovtQXStA3njO0KlYMrABNIpYvTBElpoyTbELbE+ZHYk5uTQHcTjptEMYm0ETKuLML7aadGAluU7nGd6pF2fHsOToEqX0ybtAm5F0PDWqxnWGqbTnRkKYCOSpDUeISrht3hnEnQx26+vJwjOSMKGDFIhezJNSq6Z9oFx12oeLmW3CFc1iuLPW2zCCLaQYBsr8CVQ5zKDhlzKmah5Z5IXSHqfvHnRSkJAbo7UX5vQGPLOnrOKxL70Ra4LuJF2VdCzZhEZgOzH2duEI8MuNMxy1oa04m0KChsBbUO7lvnJZadlMA0gJ1hgG+qzTjdOtTBmbJKr/AAhrMzmXdMSgjQlQAGwrRVEo9R98QtVFumM2BAnvwjLSlmodm+MMNCIkyN7VJA8UzQV/UYmV/JiZPCPPFhowA9gdRHhs9E1gJb0eeD0qxzFUo6KXACbMDL1H7RC/Bylz9N4wbuo8W6nh0BfoDHsO5tEKAmtW5iwQQaWmvS4CnbCHpIJjP8rnOfLqqGKq0AyPBPIzSZSiTgXWyBrjQM/WE/sTOa8xio8D7paeouliStZRHCR3DvhgNEC+Tj4fs2UkD9ZTMEGLf+Yz4oDQIbiEABYRtU9OXKThEDrbkhyfdeU2y2dEN008SWQ6P6Ky0T0AwmSQ45UIJuVgAUWdGdU4AtvgMOX615ERMOGhXl2dCcAx4YCgDamSNdCiCPaovlYMRP3EXzhgqEG8LhDIhGHJZMM9EA0wEfBCPTwwyz98wXUvBsplyVrZIdmkN90aCbBXQE/ZlpaaWqmpSBq41eY19eF/0WC2uGbythDeSTQ9TN1/vthCgHInnE3N5mSy2COWzLiYQiJbQJEt4H+gOrvF7Iz9Af5vDYBE6EC4se5qANnFwlA6vIL3b/eXATnVSn5n9J0D+TeMV2YPy6p3mzh2qnjZ9Cd8C+kQfJ1neXCFtW06KkBgpF0soSqXLgSperHp+yil+fRDArDKTJClNLKqBSLO2BZPNYAuCgUbUZJGI8L57H9YX0mme7yzEylhnfahDp2QOgwItkDUpEAFwJgb1voGwMd7zVeFUW5ZLi+lzJBxCh4Kzihua/DSkRqkCcqeWBEMQQIco42iFzMj4M6A+FkojJdENohIa69walJGppmKhXME+QOP8VNtKmgI5LAeawwZ8v3QuUsMw0h7QFZrzT5TOinRg6nNddj0U+XyQfEpmTFTv6Du+Rt03pFTaydBIij2rYAY26eVJfLayijM/7VldeHS5M1bhEROGyrMTFMeRiUwsUEQVK4hXyxe+VFEGGJkzNKnbfvfXrxwHR17PKsM8ggUEoS0QCk5ugiH8DuMuEuzegxbPmAjtOQ60YBjWRQw3uh96ssV8AwTro1Pipu15CxvCdO2QKpBevUP19IJnw6+cixPboIBDYB7FQoT2nQc4ThtjRn/SvVaOv9gq+28iMAEMrxmu28D0U6wc8ammq3FSQ3tyOQc40AMLSy8HdAYQoTzVREdhRc6CaixSjHjCm4a7qYrRKgz99VBp3FXYiNgYVOBYcVEI9PYMySxKEIqqerktKtPFpYbs8Wtt6XAKbl1mrIo6G4eYuMikWjjBFiDzNceRdKWb0J6jp6qKzdx0E0GJ4XcAQNRWd8JoOlZbBcajaTHyq5So6tLPcyvrXvnDR/vUcZlhZlnIWJg9P2BiV1soVnJVEGTWzJYuiGo2VnUF4VAp9JhcOu4uBQlmSNQg5FQycRNNCBi8rqV3jnjzHcxPCRF3YQn4TRmpLKNtwQ+VGh0MAFAhtcntqb+CXYsHkUWCKpE+69I8weqQrOhnjuxUVZjQgMo7l9fGNjUXV2DsT25tCgSUQkm0oVyoBTQGaYPTcFjBxEgYBV8hhXDxrSbZBxxgId8D+nNHVgAy6N3Cy1CvslRNRD8PA3NJOb5p4Zr8fmckDsHzq71hrwEtzsgbr9zYDgkszaO8hNwZJQdTeV/uYgg6cDTIfDqlYAZKZ0x2bOSr6P+lm/PN5gc4HZgae3+m2IDYIEId2KEyJ6i9WEnMpd8bnLw7jgaQVXFNhKAO/MAtLvfXmeDJteZT0a2k/VH7RzxLFYeHalJ+VKCSMsqSg3I2MiOkysHraAM1qC7WgjJxFCdaQumRMAHJZCXTLutpROhQPDfUcrvQQZWkvSzY6V7qQdfzExqJmY3V5BBAtOXPQvcYTehJPap5jNF1EcR8Mnevgyq6yV1WAg4Nh5rjKThvdAjoEF0oADVHzZAD2SCMevraMt28JuZKT+X1krZCMOUfCqw52HJNWLeRimBLPA/lDgANeX1FK3NFpFjdzIV9cU+2ieBuJfghqtieRR9DuWt8XawyyJZuEz8ZHOoCu9RN522wbdVZjv3lBroj5FBIPQJab8lYswt2BYFt0x7SiD2EDP6kKz9qdAhhvI4ThnFW4UWzGyAABw5lq9jK1YX2J0SYdqE9og3rYlDFL9nL88ZDpa22Dbehm45bQFsqJ2yHFNhPfaUyIF1PdVgf4NxSHgxVCuRbQuXOY53ahgJsm2hCo2KJhDVNPa2keaojNgBDPIjWDekbnJODis9pX6kIChdxJLhwKHl1Sp9I3SAJSCCHVNijr3ianDzTMPUIdaSumNHrYg/pGRuWMzGbXBCWctqLOgQKeNe5QuEE4QHqIbcXDbQQjhUYH50AOsBzxGVHAAqiLWynJUC1qf1jJpI3utfiJBBQBQUJPcDca0DdmNMCJCK5PhIJhmR2cV+SQs/LpAsd8vDN52N+g2F9YiN32zHUpweUNaCB1fijnNqnJCtOWQ5hCZZPvkh0jzKAp4J5BjfIfYU6MBNgKt3ouiCoEEusEEhu8BxUUg8ZLaWclE7G1l4D3kdeCRY5wXd9LJmDL8DfDVwWRLWB+iwu54kGkYeuw6SUvT9cEBDJjrEDD+45GEzpqn9GD2mF9ehW2gzupjF4Bsh4styat7BWrMNIRaAhXgioc0aiPYkWVt5Y++IqvvRhQ3yFqAOBy+z7LMn+gnAkA6TaZKUe0dL2ZKeQeOl6sdKiy1KrdwhSlDZWslegPcDw0EQpzqmQqirIyvelZ4hoABDhh4s8LpK0Dx2cXWbrGw2VVi1WR6jDFVj2LRMS7QyiiiXyhQIFxWQz4XA3MgBVNdm7BjoWZ/jGwqnFBoGW4TwX6C3MyeZpn5MIW6HA73J46egO/KDHUT1HdZ+wtyaFGr5hoNwSc9kJqgTjpjKBCClHfDIiQ7XbTxZuGg/MFb8O3R5SxfTA7pyblsBtoL1KldG3kAYRnicXoxWUlguidDb4THYe2IG3pJp5OAHR16YwxgfwhsSpjFQ0YBtoLWfJUEvoVlbRDb4soQphSo4OHOSGzQ/+7Cv241Qs+Ti/CAE1jsd+zOrmgIOIAFjx2VVxvMmtvQMgrLaK9rbh6h0E4kOy+1yvTPM1TakdOISedX4HZZTohww1eDYYqAGDWCHt842Vk/xGgRMwMUPiIU5OxAR2BQERYBJZpwLVxGChiZAUYSVUVIQuHNQBwR3wsqezG9+dOIXYyICdgKmz9hwwEFvQ3gYLp44elbgJH+3is+8CJ9g3HUtsHucXIYAGxmWPBLnbqIrn8L90ShrEiYhyQPDibhUYAKrttYpuSKI7B3sbq6YOAPAAXGD8dtnrVXF1YqhkHGuM+AL5bvOLTlClvE3sHC1V6ynuCymZQG8kjO1F1Lopz1ZDVYAxdTZEMSZQmwjyVOMOjaxOhiMClwxer8q+0qKhlCLg8kLW050qJHX34/ggnlBsKIDnTbV0HyLRgDrOVesbrwKucnDngklKSaSArhbGjdDNrTYhfSio7d42YkBLTieEoVYqYAmzqZpgbxGBypmBDuxKNaIB5ljAvCSiyn3bEAQqV9gyNaV3Oh4mXiSOKxljbece9HXnB1k50AfheTVBQAe+rzcfLykyKBMzWDBlx+gMzhAmIMrbYp3MqOBDPXvO/CmwnMGQzmiWQO4wwLFS00PSYQ4+DZXvgyhNZTxJmMZJMSxjPifUeFD9gaiK76bJfw6qTlWWuEXvSKqqeCW9RwEspxOYTyg26EBcMRPFYFurkaBtGpIqtUBjakGlE0iCZPsh0JAdhetMqhqCScdYe9mt0jEWaDKLYOeJ29A8c/wZo5Si2IFyoEHUxGzWsAvo2ypVSFdETXZukAykJv1DwitCb+HyEPgs+Q7oAgBYhA6FAHWow2ibrB9zZ0Bxh6ybKB/Y4e5oFx04MG87MrFG5lAtTKbUD3dwQWVSimy1DJO2HcJC+s6oRBLHdYE94eZcJb4yRYwsqkzIdK7gbVVJUce53Y4ePdjlUqUvHdibnksn1TYAHJI1Hhfpi1AHGsApKqktaxVJyCR3S1E5O1BvIAwkqzicBozQ1cHkFkYZ4RMPGo1KprKm41E4Gv3nBHBsLh7pBBjM7T5UiOaxRtbJnMyGuTmA6MPqHet/vOqwHRtNVQp4XLhXennZV/fgXwc/OBzPrlxizziLxvsuQzV7NYhjSgLpK2rcG4foRB/oQxQzR42qg7kUqlPx0JZbmHNJFKREK5M55wgPVZxGwrW+AhcWc6gyJIh0jdfpWQGxqkya8tD0e4tH+8bcO2vq/yk4dho8hdgopc4x/qjFINg8VSW1JdiX+6xaWvNQjFmCKQwsV8M3TqA7mxblSIrkYM2xXg2qJOOd49GdDwqItGEUP2pEszJewaJ5sQGzGqGmYUdTEX/aEj9Ivb7M0CTwOeJJYaf6lezePVvqMVQ7vgGEsshAZoSSg4M1dLbdnRBVCCXQcWVgTtaIfFDrYGDOXXwHrlCMEgSTrVlD/2WSEsNtogY4GYUSKOguloCfXVmtO44Tio0x/qhFobfHQNSApuNwfe/Xxmo8HFTjxhME4R4/f0pQd8nEU0bkvl4FV/0qn+runIKD029V7RxdeE7UnTTFdZvSDWyB2zPi9Ajy3W6fJ15ZJF8FYA94AXj/HfYPT4DVOcMqzZgkMtKgKkUmAAaOpnCEt2VKc9gmE9PeMVT0wX2oD7XFANVBwFL23CqMKvE5JtqxejxzwMhEi57MPTMaaUBG1FmFb8KJKJbw0lkJEk+k/X1D4jQBuEKU2sadVXZpJSqOaQjgtqm1CeWFkKE7qB43+NM0uQiUS9mazQLsQ0pmHuEdPeMI7DsKAr5mu48B29TW3L75FvhB+pVxxXzmdeIz0Nuh7btIDnvN2AKMFlidi1G5/Y/PJJLGxO1yGGV093R0ITTgC6giDVOud09zbFS5AcmvQcm3BY8dYqrfMvcMrAMi3UxChA6J7l2UY7hGnCbmjJChAxRI3eOiaw9aiWhkPlQjLhCtdJry1W5PB7szqlhX8ApBtHUzoBTJET4BtkNsCblOomGtC1q38zWA58TWomBqwvAu4N7lN9Mt4Rt3j64ByAIRqkrsAbDBqZQzNzpKQUgjd0F8M3No0bmCcTskmwAElskH2cDZNXJEEykSlAkBqGnN1XTuJpw6VNmhdtKuoULaBTvr2GIItdzOE7VT32rBQyKxLFyYq7DDJiLdrcHgakHnwUumC1DjrqrWeFJkwHmquVwMalv81oQuO/AMcLim5k2hygkIUE2epc6EY/VtF8FSaeuxqlWE4a5k5Z4DzQg9mQumk0tXJc0Sbztbts+5cbon5o2GdVAZdsEG1Jj3sQCoKx2IxTrkrwFN2naAbfZkJY/qgz6RFj55CVmPd3KnzknCZDCwQdShUuzR+85ilaPZ3YMMuADkSUmbJPle+ReEK4svJm4BLwHvPaE5sbL8HCWOEQpiIDPQ6BkrY7cfeGLs/8DwnfZsD+KyKKPhbZV2IQQgc5wP5vC+1Acj5HZX1y9NrhAjChlSxlqEVTXAE5G+X7pxXgnkNz7WITYEb4O1Oec+QYdQm2uJlHeD7RDNqoDehqd4SfRnEayOhAeJ+igG46zXmwjUJl0gBGpUDooogK7QM0R/QwV4rOa79vPkB+T/VktWvgNPOx43nIAbLdAz0YBcNCqQRgTHwOU5qCUGmRkTVWmtUjZihdsO0kqXv0ADPCNziVITjIaC7IoFVd6lE0aeVIwSVRRV8u4gR+JLA0BLHrf0FJM3II6JDQMaZN+VzKoPAH2hK7TC+C4mwqLigbOdPPGXgVV9izNJ1VJhLGc86EiKSKobeScCJugKYBampvuwzAlRA2pEBAqgC+/75kgG14mSaAvnLPXYTNh7qd93XlnAFAMAuEG+CL1nxfYYeJ6sAUP9p0o0mNgOBllZUsCS8i0jOFDF36VcnJ4a5YY4Y01zlIk01mRmFXMzTYABdaEGydiXCqhflaWyqlTOXWGlj2Ii/4e5UstDCTGVjFYUM6bWOnC8iXn4J16uMuCc8Ry1teLWXBU2mOSwZB9+W2yvUkhuKVy+Fu9IAlhwyRmWOMnpdT0gg3Gp/TZADeoCeC4p/fGuQIfWQ7oZO14g3pfQuwh9TavVfRk4+3VEarTo0iruO93yndyIFaIdZlPE8yumg4nHyoDj9LQEQlQTPCe7SF6gbTtz3iUOs1VyblYgVlGyZ4NVVFsa5rLevCkwnBEUah7OAU2AFXvPiqKy4Cj9W4B3TbeF3potSQTYFXprj92Z9aye5MusnjXdmSOFwpAkagkIEpSwSkntOwpHPFSiblRt5nzKeHHpMFA3gJcS5o1hLImViWUaCChTarmXKnoncUAsQWhVbRBZznhv/Izhk8IfUF0QquJ9mM0SMAOyWIOBdXCHdknh3N0RapHM+6mgxHhwE7lK5rks1b/yxlDuT4ph35rkBfMRB7jRWiu7D9WBSoeAtri7MWqw5Nit06Lr8CPCwxkxFAY2dEe8iKIzT72YAnt4AxF8sgd0koNbAFyU3rt8ujKSzxp7C+9swja1c3Sv9HgYHiOhWQBJqdeuFL0zSxZ5jLhh+C3w34AvGPuOkFSwUaiE1l3tKT8OrqSfJROprTa+b94wKozEBs8RgRzz1Km+IDhqDiJ0KO079jZcK61PLsvfPjWEwsZ4IgUX5BOW6wDkYUYS8+ZBA1Ktt5AqGpU30DwW2kCZcrAjBHVdj0LnE0T4VccsJisnnGAGnidbR3sOJXjyFkSkk10pPcfCgGTbCOIMOhy5quRcez3Yr5v9eghXCE/iYXKYyoUELneh27cw7LoOI7tCKana2DhMqRyDG+MguGGpW9xX0ybvDofQZjAyUh37hLDWysopO4bDscUrqJ8eFZ0MwdubruJJjVEuxm7v0a98+ckvf0md/aO/Bin3dXn7V0MGJOeLKGry7PMGmM4q5+uUUXRhENktHQB75Dbw9uVr2iPwVaE3K68uB85TjeldUIsrXE/cAskpFmo8fav3wmHSUKx8yJ17U1IqpBfNPpnxl5m9tjxK2+bX3aMre2W/3OuvmSbh9HChbdij34ZuSjfdd50Hsi5UBaaPBbVUo9v93Svv+JfPNK4v8n3RVtD77oS2kSzVTo0F05zIiQZAX2GjGV5Yvd3m9Xb/nz/Tfbld2+21Xz/0cm2OABbVtr6p1yRA3SNuqHYRXy/7tzrd64edoy5kXGIp4uCfQZaQQUHKpFyoAQ2hIeQ8geXYckvcSQSgIMT8du/mp17t3+jUXz3c/8w1duLMwy5rYItIrsu2QXRgk6X3RDWvdjv/4hu9l/vZ7V7/xU68WeBQEETwncB2QpuvUi9Pr2ma7JypNuOVVIRFiVBa1FPzZvfJ/+FZfOWW9b06AC9WPDTLDzHjMoCREkQR07oXWTFj0CR2kH0+8F8E/IZ4a5GnUMK8kjP1290H/uWzna/csn5EHBB0KGg46e/46YQQEMk+rA3tK94Gbkg3XbelfVcX6gMuud33+Ssf/G9/f+vnX5pXM0agsuS4aQfj2Z3ORHg/7n7mWv7qYXGrd/2XX6pd72CTsnxh3gZhW/Q+F5Yqmo3qle28vv/B567plZv3Pnv7M+dbN//GB3uPnz9hS489QG1Hwxg2ctahwJ5BA2/kzlbzySwLsFcvXariLYGq+MSsenYEGmKgqsLmVQXI6sxIjRJNB76GQ6FYuXGeXiBLxn6EcPHFW4987qVzz77+9l978Zs7zef/j995+50XZ7S8SbWASPWhnrNvWOXYsE44/FDjkTwUzn1z7wO/8wq+8vrFP7j9lZ2d1//at3Tfdn7wRi9A7wC/tKi1BFDSnoE/JuRHWS90UynmM16qZnNFzb57olqQv7S7H+Of/FfPlr/yjX/7kYfCDzzGi4sOIMlRn4BkhXkJREypSWZDi/SokbuOX4f+APh24L3A1qxHl47oM082zZf377vVefKXnrF/+ZVffsc9/KF346Eaz5lKoecsoJjsbT4bVIk2TisAWDQQbi4gfvjKbsj86lMX5pkDWSWDrPKgs8SFV/c/9N997m0H3Ze+9+0HnWL3vtq1e4YMr8daPZjOK8TyTd9kSbqfMqJp2D/ROyA64u88+cD7er23/cZL39GLvxoWVW6uNsJ0gyBL4tCxY6eUGtW3Naz1zepPEeoLrHYWZf3iHS+8FLP8q0+pCAAgwAJnny4JbhlqI5WRw6dMnCmr47wDSMxRGEAwMEu2t8tVSiTMlOfsSz1to++BmjwROgGD5SKIADTBRlAp9ImuUJ56MQxe9Cpf6UtdoTVar2qbff6J+97V7j76jZc+cG+8Haw3qsYenE8SXxcPFk0iQbhCvQY+NnaVxAMgr5IlJ1u9iraBUNViXfKFG++4+FyhnZeuffjv/55/5fZn339v52I2VzTRcWyw5iLCb7uAKWPVcRZOVEGiDlyFPil+GfgO6QmgPkEcq9I027uja/fufOVDD//g1d0P/+zv/6mX9z/9g4/t5zXmYE40AiLQB3pSIYubrRa8EoYSWFatPaj9QnvvQv3an39f+MA989U+KSQdCgQwLjOh50AjKdJAojq+OBOAAoNdPChCP3qj3CPi+eAXOHjpnD52yITSsOdpNlLYNlXH0ZtLyBIXm0QXKlYfdWHvvff0Hjn3lVb+7Hsv5y8dtO9tLTi3MRC+4uzqEA33+sljhNRw5MbKyUzCOHjLPTBiVKO+3W/fAvaNB2W/XqteGlEX5oejpJ5QV45SMdViOk0RSmcpgTcev/iN78mbT2x98WPvvPXMrQv3z6iUYkaO94hgTuSOpqkEOkRfiCfaAQK4LfWIlaoWijgEa8CAZth233ehv7P1lSw89+4H9fJBeakx9pLJi9LbiS8tuiVB9MGvCg9McJqpBDozFoRUZW6khV3aglqfR8jP1e/7zgee/faLrz938PhWKN7WWiQSVJ1zk1/6rF3+OEHOOP0BS/qLtNfMHoe+y/koNHwPKv142yoZlj3YfOCBx549KJ7v6aFev/vgDvNhwi+BDMiApjFW+YNij3dMc12AiPpnXi4P/NPfds+5PMwdZgJRiiBtYUFpAIAP7KZzb5ZDO2kFF0osXTIC9u7d+p//ow9c+MCOv3P79lduX3r/BWuqUhK8Oiph/NwAclAAMgXKpRrGr04gFwnClrSblv46CebWuNRwoPOdD3Y+LMtmi2uSasq2oEOhvcpTHN6G5cP+NDS4XdeVEG8qknVjq3riSCcAIPTpZRz9odXYuvbkE52Q1fPKmECDMI86UCvw54DJJWJG38hXySorAIBC3Mm3PnQBoJ+rnXviwtD8doQgIp8pxgwG1oBccLFP7wr9JOXD6QYikDtAw5ALxQpVEwCghB9GnjdQEOhZ/VzwiM6H78WH7p14/yKMeAJ4bnkFSb5GvEY9PhEap45QJ2oTb5ciI5UlvXJZMk+F2LvY2Pvff+B2PYuNrGo+NKwt5JQEp7ujFHtpB8P5OG49m2LNMUh96RngZek94rcT9wEkihl6zrCkGYzW26kXf/6pQ3e2aJlPbE/VVRmQgQ1TFAqyRxVyrw77dx403vtH7u32yryqzzFv8Bzak0wWgCBkxgwKA5qZsVqMAIi4dLesVIG0mbK0C4Aa4fzH3qYdcJsXPnK/xr54/Bk6+jAN4lRbkzipWAdrWGRgWnoHAtmcE5VJW8YWYbIWvafVqs30xspuXyQARnrBJWUYlkHFIN6mEsllLXvm3e+r6o5zEMfnqqfashdg1RvI3GaZ4OdcXVXvCFNTaM73XTkXNYhABgWxSRT0g5LdJbLZAOWwHUNNqARe03WwWp/ZNdZNdechdKgj9f/YwwXoMv3tbl9dosSpT/ua9PDECqjqjTObzouWp1ieAIBmYNIWLwiO+qPbparydrBIuVRS0Rl1VOoHVajuyeXGHAvVxLZ9dPc2+XnqebdvNXyr4O4MM3e40V+yCxkABSwy4xDMqJxqiq5wSB3eHYYr8sZ7Lt9658Ud0LkoLpJOSohuMqdUWXuCLNAzWGbIBANZ1QVZYwNXuBab9ISPkCQ23MBWUOFrjHmtPBkhADtEfTArlRNN6RCpghgwV2yPdsPV3phJs2LXCTj6qrJSNbp2KPWOXOINm2/CrLTD5e0RV6/+XGUZLPMCiYIby9SKTzLO9AlP3XTwQ6i0cF+URUiwbtwxjAcCtcjeQAdKhTO23XpUAq8Mg9u7TC+IbWi+wYgAXgevCQ+OaRuoTiGO1pipTqAPK8wvhUmmFDIiiuo7ois6YpADsfLYDmYdj7aAU8kMLLNQHYcg7FKfln9NeHfAO2SNRVa0wck6aKkxrcqM94C1F6k5MSq1W5nFwW+LrzZUa76ya0RgyNDq9KqgL4IY1xensxJX+1khyUYuQg1XY81aJXPivE1HyreIbAVZ7QSLyYatEuc+o1XVvz0uyXIn2MTMeSZMB7GsEXT5rut2RFsol2yjVkjJ7M3KlBjoCUAuxYUXE2o5z0P55GXBsLXqQpD1gERqAFm81/W2ivRl0RfUg74GlJNtEXAIm5xRq5WwTu4aO4YDshNUGe6UbNicD8365bjY8ITUaBfsGvlbwL+mngf7I7/E7EYyt8TwPhNUnmGA6tmgMk2XYA+r8cC+AZGkbVTnWtZMnTV5tIhQB3ZmSAgGsEXfdwgphJezrlGiQ1oANW26rdin2aUWmeApOmuz9YmlDrOJi41YLWPQ6EAP6gMG1d3qYayU2LhlWSpm5AzPvW/GRL4L0VGQcSFJ5Q6tlc0ishUaRFfsbmjfEI18F/ENYBmVAL9JXCUemhzHkn44+cU5VtOjvg1tyazC0Vbs2frSiUmQkjry6wovzx3NFJq9geIQydfF69IjhvcD90vZLGMAxZxK1LBlKu5KcvZ14PjWo4Ht4c2DgdjY/+b+wsvEvsXDyJNI0eormvi9KW5RhyOdefxywRF3YYVW2X6BUS8E7EqJpVWb4va04BEdL5K7o4oKR5+MfvLMw+2g1+bYQPZpzbSZUkK3J56x/9risZh8UBWBncnqpsnILoral3rHbRya+joqZoUdYH8GN/XxHoqwfflCt/PBzQPcnPltgFCf2lUymd/IVjN7LCavJABFsQ5cW3I194nflX+H5qmng7GoCfuzeI5ESVZCUSjhEiJZJk28KuLq+HWLZ/xiS5Eg7hGvEC9Luz5y7+3v7wNHlEruHuNKYQkgoNvEs663K7wrxK1Bbt/gbUM0oGcIDhBa5giK8Ntp3CZjWGldLMS85w7jytJCxdNlPSHtDlZm1Qs2yL20/U3U7aTyXwSwR2yNNX5OE0WxYLXtzGlBFbE7mJ88oOqTcSLV/3Ye3VnSqJNGzhmNYJw6ug9IPRlII4/nK3tcTYfdf3Uf471IXxScY6jrI3QGGbUjyec6Gj1VoclztDUzY0BMYUnT0UFk0IuHlo3F2JeFYUYVj/VFgMNAmkEjFWiiF0Yjh7u3zV5QPlPtmj8fpsdidsNXO34FEBykTVbfH+/FCASCGSseqUohG7V3jvKoADaP1YbSYHkPx0KYGZkpSGPZOSsYtBlohJfHBIcN1M/ZmNeLUXvQAwsOlAkOBMbOTvqMWgqhAQx9YMEyANFLYDB/hgHsi16Ee1xp9FdfF4tgHGXhjmMwo1LaX2HJWEzeXAUPro31Yt62M+Orq6wUwkI2aJ0W0oQPb2uc4WUiYFQc8eqM7QzV0fzE2sPo4UuQN+vhj4beb3d1MN17AkZ7LIQ/DoSxWx0APxfj/h0l+iZgZrUP1f2Dg2ZrX8XPF0rIfKw2r28N2TNlcbDZXiwfi8xC/mhd3++V32K8F1U7/2jI3iXN+/qZjMWSXqSPRUb++yF/eKAinvGMWmFdEKw93rBvU/eTPR0cXdwg/2SWX8ZsorO7YV2MwxpW/1jd67H3C8UoIzjQ7g38QVp9TjvPaiw0+cMR8mY9+1jml+O4dB7NqKXtr3C3zajBF3aY/6ncaua/5PFa6cuccjvkfxDCjAQu4ID2Cx53l2ql29vbzWbz/PnzjUajXq+fP3++Xq9PNwu2Hb73HvvL99hf3g7fm1LAJb+c1f9ZA4/P8gEAgeHfD/keWeDon+fAx5fed1kvms3mzs7OuXPnGo1ViXwHMLP6f96wMjAao/F5Q1qzCNRC+Nt5/raThkBMjcXW1laeHyefqChba2ANnJtjbBbqH2+Ewxm9qNr53+Z5nzb+/tc+Fgt7YTV7x5Z91zn7eDP8kXlnsMSxaJI/H2o9yzbUi1qttrOzU6vVjl3FwIsZ78t4X+DFhNMYmx9v5F+uTfXiIvmbWa1nocCMEVnvWBxf3aO2gSGFlNEuWeN3mtmzYbwXgeE7QuOqbXxG1Wq17e3tWb0grWXcMe7MLP+QX87qv9tgHM6lyRm1tP3r7UWz2dza2mo2j5d7q2bUvWSLrKfqJo+Dz1t2LWt8e90SgtUeB56b08FnQv6OMPcOAyNLnucf+chH8jwnee3ataIoKgPol740lXHLS/gL9+AvAriO/+8h/t3SlglLCj2wCvge+8uJc77He1EURZ7n586de/rppz/3uc/NeOz888jRFZRGJSNXJQNe7fIjTI2Fu99///2f//znX3311fF7E7bDP1rDkwD6eGZfn56jk1KQbDgKM0u6aS7l+FrGYk4vqqfzfv74Zf9hon4T/+Ql/Lhm6eTpY0FUfZ2we66rFwAeeOCBV1999TOf+czEQxkesJ84pz8FYA8//6r+71pmJxXmsqzbQPk+Zr9aUy+KoogxSvriF79YNX90ZYZLAecDL5W42dcLy+0jBh1jajIgaMJ+sIleSLp06dLVq1c///nPDy8hADLcz/9km38MwgF/9Qr+q6mxqALWpjclm/hxQfvX1Qszc/fd3d1Lly598YtffOGFF4ZdICBjeMB+Ylt/osfnDvAbN+LPJOo1Rys9DYbZhVcWx9IMxIaZkWy1WgcHB5Kazaak1157jeRU0IUJ9ByAJTSOAEsLXwjWmTEBBZC6adknq2rNQ1yVOjGewJcy6sXh4eH29nZZlq1Wq9VqHW8XrVnVqRGjfEZ8GAEzhOdz/tKQTueqik4/MeZJwIsIHZYncAhNjUW9Xr969erDDz/cbreLomi3e8PSOY1L/N9d1A+b7Ab/+/1ZIpwAYflNs39d99yP90LAVxF+cX483enHYrwXDz744JTYABDUDLqEQSHPeUaqpLFw4HPGqMDJ2bnGXpRleenSpYl1QQNCpoea+iOUdez3lt6WRLhh+A3GzkQMdQn8jvEGjLNW1xp7sbu7u7e3V6/X3VWU0QbpBs0H8H8+jz+Taecm/38v4z9d8qSC9luW7eTeiUP2I5A6CPHfMNua08519WJ7e9vMhtpGRTRYFb9pNPDUjv6EyUq8MnWTakfKfjvjtUlD4FUVnT6gpe1fVy8ODw93d3cffPDBnZ2dp5566s/+2T/7wgsv/C//6t8Qf1a4h/h6Dd+1pe/Y9u+AdW/gZ1Juzg7zX8utlYU9LUsgAIAO+Ksh3DcrHuSKhV6ce4fBhtHv97/+9a+XZbm1tXX9+vVWqxVC2N3d3dnZKcuy3e5UhYGoLSEbRrgu99QI0EEs/suxRO5JROn3YvkXJ7cLLWdhmY3xXuzv77dareeff35vb++hhx7qdXs3bt4imkBOhnv5V3f43YAO8OtX8Pdmng3lKv5ZB//jMNrLobQ8eQHR/Z+r3z+R5fP4WGRZ1mw2f/Znf/Yzn/nsT/7k3wV+CHg/9ST0lC2MYxQglf3PRf3wjF5U7fyHKv6bY4eD8TucfixGvdjb2zt37tyxGZUPp9Pc95U4Fn3p78TyyMm/7l40m00z63Q673vf+w4PD1988SUhIy8Y305chDzVgyn1Pl/qSz61Lg6Ev13OjcRaVy8ajcbe3l6r1fqpn/qpL37xi3//v/4E9FHwOwM+lOn9DT0CMOD4YetYew68+Fs94KhZAqL8mVJ/af5xfI0zqrJ5/sRP/MQLL774cz/3K5xYF7Nn1GBH+lv9Ke/XaEYtbf8ae7G/v3/9+nUAly5d+vEf//FPfepT/+pffQr6c8IPAEVEzgG/WOo+oquIfy06YyyS5uFV4K9rRkAjAch78yN6Fm06tVrtiSeeODw8fOmlV40PgFvA+RYeaegeIfbxzL5/eqZJ4dgD7kwpowrNZvODH/zg7du7X/3ac8a3AxcJtfBwTfcAsY9n9/035vSiWr8rO6Y2ge3t7Y9//OOvX7ny7z79WeJ7xMdAbgkNcfFYDMPx7openGJGpY7FqlkwJ0Cr1froRz9648aN3/u9L4H3UveQ72xhu6YG4InrYkFTz6ALmJ5RHxXfljijJps6o6Fpke/rwc7Ozsc+9rErV658OnldDHek4xiFxJ31CpnsxfeKjwLcAurCSjMKwNCCfXpn/aLXkGKBJ2BgAAMBDAIfDXAtLhd5d4FgIEJF8aoBu8UbshdhmvYovPF68eaYUaAxqAqDHjT7DTgWb44Z9VYv3sJbeAtv4S28hbfwFt7CW3gLb+ENj4GR6kc++yNrvGn4guELYQHTjrYVPx61vTa16xMf/gSAH33lR2d+6pR1TbfL5eHA64YAiwi/Sr1gwBHBw0TzJPeoh/CJ//ynMRwLGewCMS+2fvoW8Nuyl42fBHuzDY+SHOAHo77txLHBSajGYmJGNWAXsJjwygnrEX33ntifupKA89eJZ+aSVrjEe6Q/RSa+sWWY0YuFIKFzGNRuaUu7K1vI3WhNonNanmkeyn6xZvsA8A/+wT/A4l4QDHBpcf24o8tfZvi3YVF0cWD890o9vE6LyqpjsRSqOS/a0iKvKqE9oB9FmJ9832DTcd5++pFPAPixH/uxeZeZ8c//8F+4+dj1n7/28wPPQtpbJDzWLVxIyaOrIHRMuycp4lKNRRKV4aqQJF/YJsdZOskJqKZYs9A7a0IxQgqMDyu8NIOxQJK7uxwiXp903LlQComFCw0MUKO0LKvKolSRUeP/RTUP20iuL7o+ZPQEugbVI2pAJPrTHxEIFhAmFvpklSEgeMnkpbZeENqGWsM+NqieuGKojbnQXkPzlYnpy1pQiRQujRGWUG+cugLNxkFYi0gpDJ7Bduh7Ns2xvSLUMybwaUm4+XP/8/ntK/zTNW0bup5Ys84Jlo6SXrdUHtWaM1DlCWfbmR697xQokrAG7kR/CYr3G7YwHoAgKcZYlqUP5KswTRNJFctLAzkhOh1GIjPP3GOM0StMllWQydjGGoIsVgLBbDkPrwmssrBmxkIDxzckTuMOTWYC2+AWj4raG7gNP8GRbB1DIwbYXcNAcheiATTSVCsAucJ5Y+10stChwzmPqCaumZmBvP57v3fx5355ywo0wEZq/QfKGE1dYBk9pChVJ/a+VigHcAx/KMRGBdaJ5HoS64Sgc9HvQVX/dUJgLASLZSqZw3qOPcZb7m0hY9msRNDMr9EJ66SkAa0VBEIiNzgZwVknILqpwClS7zcGAtvQ9nTTmNO2Vi0bdgoYbAvMBuHWI5zV4wcYPPQuFlkMWHVcPJPtcBbj30nbMCYqxq3WhK6ev9i62W/d7lJAfqxWzWL0YUstjYJ1DLeB3VNRuW/MSHUHJu0yBLJhKs6+WVQAH5ZegpfuSq6b5GSErJpWQqWNwi2aSqkn9U0RFodGhiBrVlHbwweP1echCdJKU1GqpjPbgmlz6GNnImo2nbRchWnxqwu0M84QGuoZM7tnDak3N9F1zc3YgrbAmnwXqMx3G3sNWjSFhQj0uI68gQ2AwhaUc8XJTy8FX8+SsfnsGgJunt/+7T/z3va5mgiasS4l5wyzpHpic3YjKaCAd9y7lcA4VV82JTZ8mW8jaVoTIDw4jQiGCPROU91FaADtVWjVVwQzoCZ1jhuX6A+UXjeslDjuQAHUfGDicVjp6jH2HSXohql6sgZrgaSRoxKHU4quSrFv2j672mqez2TKmINSNm/iLDmICJkp+LGswI1hqGfMe5gbuE2U2qx6R3Ab3IIMqhPN5cWpNg7X2Y3CSmgAc3bVBWDpOJA2WVZquGFqPwuf/thTuFxRIAt14TBZLRDQJRqzXnwJb0NdMa7HvLQRsbECKOioTDcJBdGoDGZELhiDBRAwquPq6+SFM0VACrDoWqt1jtUe0QRahAEFMa3TiNvEvdCKlWZUCNFQ+gzdYqyGxuBiINsOzMKCMuYsqb6glSvQnRgMyRuIqJJ20hH2zNEkBJZzuVLWhoV6xvASIge34XtJ1cVP1gxuA1vDwkiEbUEdombL3WInQ0aZ5pU7BAAFKN6NMoNgw1aspCj8/9v702DrsvM8DHued+0z3uGbeh6ABtCYQYIkQIoDRIqMJIoWE0ZylaTISlVUiakhimNX7KQsJynHkplfiVMpVyp0HFlTWVVSFMmyaJq0JZEEQZBAN2Zi7Aa60XN/87333DPt9T75sfeZ9zln7XvP/foD0091f/31veesvdZew7ve6XkF7znz3RvztYjypy685vzAxGaQEU0lKqwCMIJyoVnW4CGoKPShPpDvUu691WIDUNvRIM2KEvYMKhnRyj/KU0RYW4ItCQ4MXT0hl+86kkhNcY9oFzFKREdYTuqUAvUO8YWaJsWhlLtyVegWKyAU95EZ5GvflHJYD5HFW71wySGCWbKIkpDXrfk2hwy8BJDI4WNwSObJjAy1sE3PWECbGANrPKLn7EapZ8z3I4gH8Mzh2rmhiIAa7tjgeRXAi7qZn3PBCjqBmalZoxUOjBurWNbuhYo4U1Wpz5QCXpONWXo1CLSBYfLAHRxSjfLvGkSdguM1FKHnwIWIDTOzzCpZ9so3ZXAW69qsDXQwGdeUFkYA5jcEAWUSxXprUgA0JnrCQFBS3a6kdukmyAxdY3dSjbWY6xZ0Siy6dkW3h4L2nMeqoetEMgIlcfjWrtO7QhAnZ+Us9Hb6F0J9nENlqwfSLUNKTCAhOTfYDz0KcSYEVu8QjCYIQQgMLaILRGBoGLtGOoc4WoRB+7Bu8hWG4B4wEsc7vavM6xlLaJIhqVp4XaggN9MGgXSBAdA8MJ2cL6llLN0VDolWmg0tQj3f7QUrR0RUpQwsnI+6G3VKXZ6YYJpU8PRq3Bq6tamx/BQcY67e/S6xa7FBACANa3Jpit0+qzQgyJV4lKfValyEgFPqVJYnHV41QHjLQpdqrjSckS0tFi0lRB0AD0nH4QK3VhMeHOuZeinopL6ydeaNE7haj6ESTjCudzzm5qMi4ar8bdWxuHiiGBSIBgDqLnm6m3duXfpevaYYqH34XXBXqk+lnnGPsNObKyGKRcmdKNtoedeezOkn53q6cuBIOADa244DgSfQ2HcbbqrLzhsL73AaP178H44DbkqXKDhAz5wt1HCPjel3hJwXmpp1LrEhgoRMJBHAQGUqo8rKFb2l70RR9zltJRbPSktnBaAR/NRtCHo9i+b2lgPYDexyXaUdtG0p3ZeAgvhEwHcuxnJSHKhNsbFZmxFOi7z5zQYvFfV3PDgbRIscAoP6N9hAhKSbnWDI18b/CIWrfMMdX+wWFWfmNNTCr6hlze888KhtxsIqtImR/NQB2Dl5CjboGQAAkbTzWXTPAXI5mE2Tc1fFiWFQEAxskIHMQKNEvxM3x6rQ5TsJZsmBIwibJIeTHBROhR17Newh6gaLWarijBBG8Ncd755G5dJayPsOpa0cAeMLn/o6YoOQCSYaVbCXZoDRAsGiiKQEOsTops2VIGaQ+8TEs70DqRldDj8lT91i2N0dD4BAqgXuGxvYIOzUFFvQolWUoD/qtmdIKEh+ps7Bm27tgCpmi2nGOI+huOlNimCQGlFNY9MQCLooDupHoQRLjMU0QflaTz4Amq2r4zRpoupbxXHjm1quh4HYgPZqtkaEPfMhPJ5vNW7TMwSSsH2z7EJSsghkWVjrEicgizYzVckEcw+GDDRjo+ChdpREGPLCi+kVVsdlnAoD7GYeI3gXUGEer4BFqXch1zt/FPYtmm8IAqa9TkVXKGfQG4EZ3orMgbWoITbYAA7JYGUoz6LFqLDql85r0RU3nRZz5CKMTI/WU9hk0SqMkBwCPbEs/rZTM34m7Jm1sT0eg2BHGi45D2V7ZP14qnQwUC0Uc4A5UTH/GQ0NuaMx17FF3YItshkYMHnXAsgMMkctZh4CDTmdCbcklSwX65pisICs+miRBAgBlfdV5ZtiBKp6rU13TNF7YBNqpBpXUayXiNQL4/qebdYzUGwjOi1cXAhskQC05peLBtvyxAi0+VdVvrdiUZYxP/Tq2qTz6O00OsyFI8iJPS11DgJ7WmW42Ql4hdZGJWvIRPkQbjD2iX0AoAhztqDRvYt+3Ioai1gAA2FlOsXWD2+6jM/9SuleNG65klhOHMPvqEbsQToMPAzspr4zb5q3l64rQoC/Y7fRvwsgEDtSNbnI5DNDcN75QSFIneiXxKuGy4YukK1cCwKVnrU3RQ6Ok8ysjOJZTRCVBJEzRF+tX7a2qT34Jaq78Z4ZgRNZravoCH7Xz5UwNNEz7sPQ1rUgLDOGFPHKrdk9yWmyyXDgRDxZOivchtTprp81gQ6EQyyYUucyxssfHQXdXnhlbFHhXvPpbUAdI5V2dXOXmjO7VKltJH4zGLg2OMBvyaNsrYX8fBDkApIi4gvzt7UM/YWXdsZ4qmSQxP6mM5Igomt6k2qAXaJJBs7pFtVNIwPGdXojqCecAg15y9kwNMo7R0U4XNxkE6BHedj4vkysLKfKmFsqRROBNq2J7et8CD8F9ouxbGkSI/pdP2/SXxDaqYZfVIcMnAVzmpdEkptT0Bd+wajEDBqnkOHec2zCiwgR1/4kPTZa7BV5NnW6QrATfBwx5ubzgQ3EB5i9Xppmqq47xEh8zfTkbD94Q2xcYJ5yXdQQG0Wl5JR1KxU8Wctq8uQdCXOuM6lGTO2qw20eHi9y0QnoE+0aAVlsUo15bfcexFOJXTkn+T6L5CIFwYgIH04oPRvwrlkxIxvbdQMbhn7NPgsQNASHJgMyWWZoSQ0tGi6g6Bs83syp0ZajUFmZVLfQiGTJ/vDSY5eyygWcwhru7W2yv9Azzp8oHoWecJhkGBN2cXUqjArmHogMZmSDyLmxcc2bEgTBU6llaG9R6WgBJyTKSwBOxWH9aLE2dBgt0u9us25R9gCQoTIDtDAPRAe/G/ExQ5jrRgcY3rPI+S2opW0w0UekMrK72isLgO6a+GQpMsIbaReNjZrajuOlVjEWRkAreeYM7FBzy+ii46mcYtdCA4xWSS4CgBHsBSAHiLg1iXDac2cG8ewLlw6MqJHQF0zeJJssfKQyaUxuyl3ntgu02BZh8xnyYpFjn2yhCqJ5yr1AACK8BzS4btntTM8oH2kaiE2gs83GTWoP6X6XybcgYBrwYsHQcJopm9ili2tg5slxKYAbI8rss80QZNptcG86BKAHOdgCTlG3E56B+wUFNewSdeQYbVRYrzhbgX1MBzzJAfTpEvfrbqfAAYpXbyJAf4sE6ypqiY3CEbFjcacizysNNN5jtroFONQnm3XeQQvMMJ/DcdHxVOwyNA1rijWhOIB7KMeQi1GJq0CZwc5noC8bIiLYN/Yhc8/EBm1ElUWUz7rAqgQgc1c6CV1mtbKjNKL1HPtrjuhd6RlTOHEiNBzZluFsLUBU8ZVOQNsRQKOZJjFO58oWK8y6qVlZtvOjpQ4E9oVBfV5Ygl3YdEYy8BJxBAzWnFKC7yvsQ/3yfl3lgxTvmm4LhwESc+iUPvBdxoRWDaToXgpqmtd3xIQ1D4o1iBAMa1Ml7gk4FMao4cQPZBuLG6KMp7oIEEBTaNi62065PidBKZKt1PlY07JIE7Jd3RtY+Mrg5IjoQXnR5fWN1592wZQzWT2SZdX0u+tAQafSEEuBWgQ4ot/dPYmhcnovbgwjEUAhxugx1pDwzBwtMSMMKiSwKg0A8k2QK1csDVUUmSfFsBF8y7c2dCYu8ZbCUhRvIA6Jzrq1TDTgVxVjzGNeFFBY0aONY+I1IlI9+B3o1M8cMLIZ5ZZriIdiN3V310v3S2SbCSEww6YQwCzmwHTp17gPboj9uzdwoA80078gFJU+Z6HuQqCfgZ9q65OKfIt9qes8KZTDyRueW5eCVNAAZ6BDeUwtJkmoaWex/J4fxa1sje+qNMet/NJ8Sy7IwhOMnrG2ncSJntjAQurRzvWMObBvyIjtmeo1LS3JqS0bXOKFsFF/pmHIhcRA5fSsrPsGMnGPvpo5G6BLEMl+1bui4oNR4GqA33wOoF50f1eEV3xsh1BGdsAOEYCx0PcUGp4aE0UZlxn61n94CxaeWyPxm9p5LXBnPb4iDb06Zqfqs+V/F4MLRbeHioDxncgNKght8JC8EvgAfd/lM+V3OW8D4GDKciPlNSLZmGmn6ZOp0Ng2BHGVxYEqK8Km6VICYI5wpkieEdDzQgO4OD1jBtFPofUMV6Ir3f02QdoeLDjh1r4jFiQxc6Gy6VsbhMJ5z0dmExbqewHaHtmspjwiaQdgtzrHRtfA1qx6RBGDG0KYhuEK4HXi+AJlhgKwR14B9stLjzKqsXttQ5JfxJwo3WWy0YxxRhT5i+lRWNEwEPaTIlo4MD/VYiLbjuKpCFhU06wBa9IzK888wfYK1WM9ffqQPnEU21hwpuTpC0BmsovSlzc9uvSrrQfFTplzOvvhBpKrRRjg2TmqyvaJjOjqQvWMKSyXn8AuVdCAE/bxxg9nl+0z4fdqHTgpe5Cgmm7ZJpWfi4ybLlmatkHA7HzxtwT2yA4RpOVsjN3DWwrdjQMz4IAyYLkz1L54CPTLMn+rXyXAE+JI7F7AOAxqe+gENBYtuCTa0mj75ame2Ei9aGqSuLvQpfWvOJJesQcKBa8s2eaUq6idvasVQUot2j45go7TW5X3wW6Rd1E9KIHMpVPp1FfjOc8XT0UEZwa1iGawjCiz7DX9fbykUPRhvkvzeeMDaAjuF4MhIpH51jBoAgiO4Bfh4tr8XBVl0eYeu2wOJlBW0JxbHlHypGuGA8rOcSNx+KkH0HsXXJQJQHEVHUKnsr2Fu38RO/x04z258mfDM/XuqZFMuBMyC9YIWK8WkKB5nCzr9KwsAQqoT3E9h4JLm+IeaBZPckZcVN1rQ7ZnHuIWBgSSexDEk8VvN2gPEjfWhi4YKIfdpD+yG16cwp9Okk1ax7xdkd5GCE3AtieI1PRteBKP2ySye9E2Mr+KF8toS5rpYoVUcMjFaB7dc8jB6HIw7RRIAQO4B3QAA8xwGmts+AgMgO6a3xa1PU60wUJSxlPtG46SN7e5tyw0xCYRNhWcsX0KM/NUReBqDvSLmDSDi2NXK6kLbrDMdDG8C5thIWyq50w44/JOyD2xPhIJbs4m3NrCmH50D50+AnuuRsDcxDkZ+nnjH/+jmzev1bVtpKdPkdiobSw3i+RgKjNurAm6CcrASZFwEeg6aDo+O/XAZrALbylJJhHaX/mgSQ+ZfWOZz3+O70DBgev0aBuEdJ0egxmxR7TgFgGrfNMMVMsruU/mUU9s0A2OVLUzHYJGIKAo5IXAABxSsYqK5bw7XziBNllWFQYABGdbOkllfaGAgdBeup0JhRW+50Vtj41tyPbJR6Cj1F4rs3BoKDOeN/VTB+62kWzZxV5xKy/6vCF6YQEU2dA9qfC0ACu4Djd+Rj3oWGwATcgAuHLa9u8BAOnIzh0Beo8DBVze85CZZzLBKY708PGVB79z4/MvfskffqxWh+iEI5FTtEazInNT5iknhqx+SMLkMdY1NKarmADYERF0smObIQE0gO7EBZEAA1evmLzqaFmRvcEq2iQn/a5jBHVQg7avCmrIusY2SgPJhm4TbBH9LSbhmjcsbQqPOjMYpSP3u8Ax2YcNgbysN75zR4YazkviJZX1s6Z96NjmXMKFRgCNsJwOKqBH3XH2kRD0KRj8HZ6+URlF375OBXiXS6ObNVIEJDDwdNZJ5Z7KsER4YzPl+u4hQKsxiqvInSfAHemWeMfRA0dMLFuvwMRk5guBgd3a2apOYCQ/FQQHwkBP/O6Nn/8Hr979Y3/6+ccfq5vcJMGTVpdSMPf5pGZLnFVwe8fRXXkKoY7jEGrsMohDBPbPvVoE3wcOCNKqGdUoCUfCcUmuePZnEdbFhExvJlbXool1p8cUNettCIibmLDLg8iryUWm4LL/zejAqmFrR/CpttIh96xyypWJLVN6MR/Rh5HtDATgHNFPI/shORmRAPggdUDcSbIOSFDU9lQvgB15w9CfFV2YD+wr/vRT92k8tZtyorF5NRVKzrRa7729Wo/keeHfBtb5yWxCajSGxsSgsJam7bdAWr2kjZ2hKP+3BwbquMZlu7Cq61RsAoa9F/q/8H/5vfxo778+3u/nLsJquvgZk7TtzcwlSxubxYlR+clJBAeKsUh0bb4KVyModEOxn+Z/XNjc1IKZxbvieAf11QlHx9CqpxOJWinPRzTAa+CNlQ/PZ4xH6Cb40Baeq22Ph49lWwrszH3cjC1ptOmJNX0bACrvDir/sZwe3caKm0WA77hm1lZYRtuXWhttXZ16aaIcmLXdm6ZTQ88Z62v4e8DDzjtJxX7pUO5sbX+K2rC9YD1gLWcw2QccCiBAl3JZYwNbOODCyDiERqoRdbYjLJndK1QPA1pakBO1+pitMexftDluvn5GlxqLg5oNOHAkE04a3X/x8Y+9Or52ymBUlLwmZ6y7hwsQnPIVqrBCVOQGF3N4FMbuLjgsTXTNQGjP0FjrzTfAG7RL0NEOuNCVGfdqEy5ahB+vzAXlD8u+FQoTwiq/CACIfB18vyeWyFwHDg0JN85px9QSeptOwpq+DYFF/o5DguVyF2NR59nksChIcQwWxR7farDw4nbJDrTlrRENoCmuYwVYhVO9orZHilWqAjKFx03PSUmOO9PYkyikGgh7wKalJvTpkQylDqGcC2dkyVxK5tTYOaRGRTJ/Sj/fAhBwnLEggShmrKRfZYfe3xYtc2Ys1c8wYB8eafUK8gg58hNmz/Kb4XEzA2QheL4SILC5L6UhYfcktHKZG1ScGPToGFMu5lKRiK5CRymcBXX6DKAJdrDhDlpulwZ5CX7kZ96qJSLUd+4FGNIXmw+IqtuAX5W1ok4xzbCq+NBt9xHYPnuXAcChIS3zFNZtCsiIZlHcsBoTsZF+q+q7RkSkJJ94rDnlcxQgkuJiEsyuaJxrI5MdUq207UCwg1q1OrS+ancKCMZHou0H3N36SAGycZrfMjDuF2VvlnfIjCttKI6JZvm/NtbMviAgwkamoTQqsjTeenEhJblVzwCCBnr5ZhbEsg6InBdSsaeqTp81qC5QJyJLAF/PwrOONwwqSnMThAWrRS4CrDEkrDzP3TfcQyfpfrNRcUy/IzrkcrdCSHIXTlIPsL3kRLIMdkgdo/IET0Xhv4S4johstZNj6ZRVfitqz2PXdbTGkl+gF/JjoX2+DShwIHWTvZIE2ptOwonYCKlVzpVb8clJib8S8+8lhMBso4Mv83wSxnOxMKCJ9JgHNInGRRX2qoR1DQ8Dd7d+kADkE1KQjcZlGOwAU2pArIhtAhiJQ2lv8qMcyAmTj8UBMaZi+e2zTZKaztx2xp4i5Mhtfb4IkVjZobpxP3YNHc2iailmJXGCwj78zo5pYJb1jDlYWxpiw0Vv1muCufC84dmcvVC0JJUOrcoksi2dypMCdjbW24CwvDgZyzyAaZjU+bd9kYVgnaBWHdmTkYeQn2+DC+jJFW0/eOCWUi4SerI1BAfWIK5Kry+4LpZqjGsM3gAfTCJm3tSRHBiD6axILSLzdWXJZyVYztOnVWwjF7lX9qsxUUchoG3gILsISLXiqYrqERvnigAoHTCimlwExd4ec0FVcuBIuiXcgfrCtqdsBa8arxKXxDYQzh3aOhLjpjib83Y3wvpmd6nb8jvS3cmmBbxlO66pt7FOnwfgwJhgPObA+DnD74hHQXOJUNPpri05NsqD+w3WMOueZV52MEiBp6Zj2TYnH083Oatk0sOklYl4S/wiBUy0N6VaxZgr4cCghmfdg29g3pv0Jp065nsKEnwQ66j8QpvbvCA7xCyeKqmPonJtTdYnxH3fVA8bUoT6c1tI0EjIwR2FE5FEA+waLpPXZJeMe0BTstrGCQLISbcNd34GWYOT2IyzQEWF+0gMgV75w6IGBLtAa0cH6oKeUfEeKCIT9rHRnyLcMfu0wleDjVDEZftEcpxH66LPlAUVng6IEBzKwQHVF7SeaY4MIHaTn7YJpNkeVZdDTNApOdrFAhfQRzzOEdeZ9sQx1dtIwCzwKqxtwcwsVFymCRC8Yxgta/4KYIfYEMayimERi5gY6Um21hq1agbgpkGONf6d6Sfu3a2GQ8NY3mLqgWhAB0gmbdwBJvFUKZ/VmLaVslamfSFbIJJbyBiXRNrxPQk5JRCorgtiNObSWBqDowi3RP8kSQtWyZtU+u6N3gDO6hXfDA/gnmEMnTOErMqfUY02MQZ6VY9z2qtmz2a6TcFpVqgaC9kS0lkUesFdLAg2Bcshh8b0qIKjwd1wwmDBNt6r5PHCCVbaVH0vMUfQ6e6sjYL1Axx2QGUrMyV4T1tZOnQAu0QMuZrWMLHKiCf0Y6CNki5ojlHKT8X0Os1RGBGdpDVsDgSjeeWavwCxIWBQeNc2WEDvodhwok82axyQbFE98F6FmU7jqZI+HAFnpYtt/lPoGDJ5f05ULPyakHByjlp99UAUDBMGNMkmKMgDemAv7Zjn1OSy7v4jh+/aBTF5uEidO1VlvT+j+sN7wFgcLdynlSt8w/gloq+S9y+EqR3S3Yu3NAmDqz23OgUGKqJ3LVqhvE2zngp/yrZ4RGi0C3/35n6OpXFZvyCVw8qRn8q009B/wYcOhx2aGov9GCjB9040gKvAm9LkxrNiwyfG4E3iQYCOJtgNaJdr0QpChHSqwIHY3uLMLw4HDuSn+Tpunguhs1V6XujFQ4CGq+k2G7/S0Hkj3rbCZu++iKfCYdq924F86zKh2h7bcV1UH4uHn1YXNL4XIBguKDBq9+AIflR97UpuYpM/o/obAdjjxKZNADoVPgM+Q5xCczkZUx/G/HSfxVQlcGA4JYe0sa3Nr78PZo1j6i6sH2okR57Shrs/lwRgLD/SvO1LUd5Lu2ZQ/rBggYSZLTk2ps/gG/RMvES7TLXniPky1an9A4y2lRIQOBBuQ3fJ0VpfyIUYqcow+vthfQEAWERbH6T2hxBahv7uy32XAa4tYZ8YQScln751DY+lNSHTeL1UY8k8bMEq19M0UZw0HztyqNay2yGEtGwVCDIveJDv9YoidlMPXA1aZ1Olimq0yTHUgzl0E/g98LWZrJ0Pmpqmic2bp87k5Niy4pUBmTazrN+DaHtCGMOPc1BobVRDiy6NpdNCL9nxErLCBzWS7soumZqCpB45TryRU1fN2sCgomPTBEDddmsbOhMtbvqCCbapYbIm7PSRW0VRDQHQiDoFh9vPvYvxbZQKxwZf0D0XKgOim8zUVjhFL2Dxe4B1YV16AAM1KEvMyqj3pMoojh2VVAFlvgXjSD5wNslJcuAquQgAGyDPY73byg4h2DjRuUL15WuSDaeMoReyoIb0ox1w4XEMDYS97Z9c/Bq4RwzE582fjbgjCLJSMMxLCLMyS6NQOKYfOG+/q7q0mVwEyfWdd4DccNe5b+pu3DsOnVwUFe5cZ+B3nQegyH6NV6B91wExmGkZKxnjwpH5XdhB1fdbQiDyxCcSA5W033NdwJg4BQappTEuQmxQ2rx2Vopo1kQZtZ2cuysA0TEku2v5UEUAzmg4lXqi71jRIOgdcE9olIWaFcAWMZ7I19Tq4o6cimBDUBHmAuamsU/yLYoyd7T9Ijpj/a1wTIxJMbmOys5AyCPSqypSrjVZ7+XWMmCnjHUF/M6m1LYakNQDm1SjFnu0YxTsa7BngJ4Vpl93DyFMW12vcOiiktuhe8wMtB5CZDxxkNZBpUdFAAbOIe7FPTUHjiDUc7Gx6bhCXg/V/CIAQAydrwNPrFZ1IwxopYsNxxgaixO+feVEX+hvr7Exj7dm+mlnsmsToBDAjKxL0iKqj01OF8H6Fu9Ix9y9xT+Ah7BDcl49JNDWVHwynVPTUWZvRNgpcQdYybcQxe42r2gODui7JpNIgROMte6l2wIHd29+ALBTF3uETpKZhgGIdjOzX2P4dIMDs7kFXxlrOy9LJBEWV0lh/yCCETiSTiovhOJY6OGemTcVa4fbiYwPeVTMY4wxruqIBACzl21tGn87hTRk0pomBjGHetAdV89qyQxcjLYhwApzyNpPcLOhumBZBcxBIIBGZKDRM9JIgx87Tmv2awwMhc7qY4ko9Nz7RebNTjcbwRawTzWWWy64X7wFnNZc1IL3gSE4ohe6xcrLJIA9KbgWM6sXXKaF5YT3MNR4AhM12kSlvAACmeDQxGF279JFd4shcQrtC7QtNaIFvAT8y4hXGkX5JNLM5OtjbYt8MXcXKMEfcnxM+L/ufhByLyLsK39LYIuB+gJAB4ryeWUh8YltR/DTyLyGZWP+xliTyX5Tq7i94VwnrkBZVMTSbXpqWSbo18W++35F5BgzIEN68TSNiBNqKI1lZwoR3L3YIJhlgRlK503F8hIyVhfPzOAZzMBMNCIjjW4lv3dBb1rYd2n1TzsBA6C9uKRFDIUTrUujPw/UcNsj23SuMU0ULFh1aHdLDIrNselr3lUoXORrsmgYgV6xN+41Z3iRb5gKIgbSfU1cJ1kMwKZ0KverUBG8B2vA25t8exzTvwL7JHWcoVj7JIAQwlTPqIy1LQQLAvw9uf2Q6fBCxrCWdK/8/UU8NAGCeoAW2aJOyX5tg4oFCufO0ZkDbwf91tqtaoIfgAew/qTA8wqVhgN2l/GOcQ+r0UY097ZhnFyzIXcczwI0zrBbLiiSaobKi2H16Ax2aGwKM6HL+f+ZflN0hGrK0s3QCBxpVkczJ3vuZzi112O1tseWq1eDaAKDums0occtKBDjuO75AnhaNHTPz9lIxNSwCAZhXChFawcCg64Y94RTqQ4Z5b2GI56IDVu1KpQ35FPwdxzPAOPSLiXNNlEIodL1XX6AAd3IDwsf5lsWIPcWwoGeJOe+eSDHwKnXNU+RsAMhY7y7UoftTGCP8bMxvLHe9AKxCV0jb4gMVnVgEvCh9Lr4OKr8N8aW1Kvln9B5dKkL920UmY0b9vzCh4tM7q3S4MxKsEP9SQunpjuu03SfZ6r5nIF2CTxIc1cQbF/EuU00oebCmxSmUUdFtBt0siWdf0PzyMgOajuZim97jaIdHgzDWdjhZEVNNSQBUMPRjGrTm/eJt3Y9xsJJtQU7e8PCP6M+bRrOHBiYU9nn4/qXTFUidCnnJxB/QGrdK1P+Dks1Y7LDzsNgJuCUOpblQM/PkrHbobfgDbdL8M65o6/Gps/JXtw2HoKPkllW+TYLP7nnju9C69yuRQ3we4WdaRtToU4D3KeczYYi3Bag+eTUKmqErwjNwuiyfcmQZw/E0kjsm4/yonRronGGAqGihMzmLxjcg6lVFL9OGAuEFtAQdkKVM4PQ4Dw/qCTK5dFJoQjJcBaBiekcXCQysUG1xQwkdTclG3YZnqfX0iMbQhlKLIAmN3dnwQFXrigGYwAcGt3vfHyU4VRs0DIRMW8EA+Uevm38DeMbTi83T4U+sS7WlsRjkT+o/EEZ7J7FwJrcPLoRrMFDVWhGhTVR5jAg0AKVyYIhwE8i+mfdDoIGrhwcU4nUNUWImoGZbI8gISJDOAhQnayI+TaLjMyvur6VVgvzKthAcT0qx7HMQyq8AfZRHcZNWIveV2rO/PlwdrEhCiQJBZUu62AIojPPivp9AKDj46e/+U2Q33z/+32vGLEqIy8LG3tiiFUxs2eZzig/cio9vxQAurdOW5969bWPPtp4ao/bHmxjcRjUSRb+BQtWOrdMGpSJTblrmlHM09P3fu1rDPzme5/G3qEADcGcnm176QQC0XI2DU3ANNNaml44WuohTw3gkoGLUs36/fd/85syfvN974/txbxHwfK3qLZrHdB55XM3Hv3MK1/92GP8ww/42LLPgb8bcOpG08T1nRBr62RQQ3h6HH8A2Ct30T0biJ2ePv3VryoLX3/v02H/cPuDKRg9Aw1oiIHISBJBhfroBX+JrQ2UT4LAUQ0Ske6tfvNTr7z+8cdbH+7M30eVyQ7pJ86+1ewOHbLnTV9wy9O+ui8cqNSqJa9IACDuUrfFvQriNbrCnfFw3GB2L3Ktz6jRq0VcNl6hXTW7YnaZdkB2xeLiTE2N77fz+EpmLxtujraciyYykYhXlMEtPe5/4Tn0eqV5Ncif+Ftf/uG/++XL37pl2hYJKoMTg3pGVbar2NDOCYO6+fy9c4jwZqdzaxxvjyahfENqvBy/MdetgsWWuAJeBQ9tEuo3s9cxS679MoWg5AQ60rmoWfYYXmu3bzWzk+U7tTxqrRa/iiA0zsLIe3541KufudF86TSX4Q743wm/QfXKk26e8HxtrC1LAaGO9HHoh8ku7n1BzR7D653OsdjzbaZyivvgVeM1syvkJXCPaIMZEVDo8xNrtis7V8QsZTVOtkF88r/48o/8rS9efuGGtbBQ+kKmADsIrFVMgQIiXqOeEYbLtyPRq9OkWtJVd/cYY5XMAAAMHa9XxbYA7Rv9B/7RC72v3wnD6OO4uxiwapxRbFgGdIgWvOHYSGN6+dL+e6I/7bp8aXuObI3ix/ewaIdo33rs0oPH/Y9++WWm0W1pCNbyp5nQ3bGVGAUd3tw2aHZa78xHj2fZ4eVLKCLTxkWBwpVBBWJPvMyyZkar2iooQKE67XLTayryTtKgAC3erlqt5pOD00f6g2a7tfzZHDXsMx3jVQtXiAP3thBibfKPAga0vZaHkRnGP/4YmtkPPvtG9l/JvkDPi2IXZVjUPM3U9FvTv5cs5YA/KP1h6YNRjYnf596i3Wm/K+aPGPcOt+1uQm2PLSHzrV5CVvqFLwaiffXq3kPHg49++RXFitXjgTgg95KVWIG3Tb8XcVxlU8mscqWI9Ie0eQVKxpe4kr0hwA9fOHn3Czf/h//VV376P/hX+H9+0d8ccC2n2A4wWZ2Z0BBCojEQcFGittNPhnF89LXXHn3ttWy8je6mCAlNHCml7FzBAOmwloU//Z6v/Bsfwkn+xidf82GCcHNo4HVmzawioeTsKA3HB7P9Sag5Hr/jpZefeO219ngMwEDlwumKj4nAAXHIVd1iCQQsOCv1pL6UFzxS7lxU0eJ6z97qIzJQ4GnxdRJqjMdPvPzKE6++3hrnk14UfaTGG8sbLLQrZg6TmuS+hcvGq4GXwI7QqJMXQmAPdimgVVSrSP3a4aPdw+v99/3nX/FnbpUOM80Ew9Q2hSqFg1AWTO+C/RHTEzt2S6eA026MRo+/9NKjr7zSHJW3JJEYV+1i0aIFbZ8ggsr8jCK8Pthi48+97yv/5kfCUXztn7+UD3ypg1aw0+4H7KVZ0E9Nn5FdX8Ph34aqHbPkNWNzreQgYJLeFPtLn6GIG48d/uqPvfvGO68+/vnX/kef+e7hcJjbprJmZwF9usJLsWFXjVcCGtsIuSfQVlvNBLeO7r7setl16+ju9mZrxALcwxKBQN7JfudH33W73fr4t283RmkWlgHr5V7ufjRL5DM8Ou29Kb4U7CSOUXj33dhffukUGD2xPyKQVakbd6hb7rfFY7MhEUtnkhNIvxwACOacLQyBt+/efX0UXyFPBn1MkhXZJgM8PXKG9HmXCYGM6EKXjVeJK4mNgPuwPSiI+4b0PP+h/Iv2pd5epDp3b27N+p7+0IsyO03Ejzo/AVyJ99TARmGBroang8EtZm+EMMjzUsUVOKrwBAjyZGLse1oAFIjXGp/8o0+9edD8Q1+/0T4erzu2uU/ub9unI+JZ4btrpaO150oOz/9c8APoANPAoSWo4Fa4Q79NLirgHNv4sLX3Yw99/U89/f/5xY9/8UeeHDy8t3uxS0zDFOdqiUMIye7ESLqlpFHuHxweved9xV+2N1tUTEiJkqJSRdwuIIqdcPPxQ//hB+J+I+k7ERqC2VtgOp+AaFMZChI3AY1W+813vWeYoRGKbExB4EnV1XCcHHJAsVH5WTESERoKBpijCbVgGVR5G13TuGWF2YlCLGRg9+Dg1gc+QFqr3Zk05AqE6uSCmBiWpV1J4mRAq+IrK03M1c8QmIn7wY82xXMXdnM/Aj5Lfkd3H3zy9z/a9ccem38bla7vaXIfIN8nfkDxPaLhwgLoDZAKV0kADWrIzNSgcqplTi+yxZqdzpvveRpko9Es7IOiY8qgOT92kTE5iqUItLknJUeVkftqHLbuPnXF/9CVeK215lxRYfUVqV41MaJL9lXqWxvDmTJYk2UawELrZfYGrq915hPQEHhdeKL8kiLYh/pgjmDM91qDX3jf0ThaK9vh6SgSDVknhLYmg5jrUzHfSQ1JW9gRJgjt1nc/9H4ASaFLqToMANBWA9vOFFyVhu6Te7f/zHtjI7UgHQD1nZ1tlfimH95xVShBVBtsEJMyA6HTeuFDH3AuhKGpJ6zGr+Vu0ZQiv2XKHNwYZeCASznZ92k1mqRBkJ6RXBAGodN68UMfwOQUnnUkFjUvk1r2hp0p4WTasYo6fWo7xkJvbbsS+GrgZ6JuEJI/8OA3H3gQkyya7bG2Rj3k/iPCgyrJe6YBbTuFMqErZqCxzKIgHBJhJrQLixoIoJl9+/3vLS8g2+CezK1I2OpZlF6PKB2F/y9j+52dG//TdyvbsuJVSA6jjlYc2wKfJ7+oLQUtCLSBQdWsEXgE+KZtWsYCXqZ+iIQwoE5d+QJRTdYOaJ+DJnbhSQSADOzKOpx3CWTzn1KAyBR5QGeqWjBL3kvYpiqs4WmVFYKWajCoBQ5TulQbFNRgPGzgVMlkk2BODcWNTgtRFuED+oqP4XwgADbIFnQy+6E4jcEBIMGsR61OpVNRiVaXIvBahU93/SyzkGR1DHcMghFeyLjpmbNmRUVLPFbEMhekRlfmYeDeap0+gsAeMdZqdrEoGxmfg38+x4kt5v8QKbG2Jr1X+AHpQMSkgX0Q0PGOb0vWkPYW4us4MUChODpn9TamF+vtL5OR6Ym6Cssf5SX6sbjberMtsF0+yLO07FzAGtCyQuV43fCMMNxSlpgiGkAGjivOWV4lG9JwY0ded9wyJ5XL/NxFJ9d3FAZ0yA6RFc+Y9Wohb6OGPVEXQ2OkjSS1S1i5Lpb8PLvtEgAD24Z9KDimpGkpENAHWptsCRzRTxzDHfKmzaEBbaQ/AoABlWMpyNW80H4SdQKoAYx3X8dbGY2UgyNuJuAioHFqLggLUXem3nqA7QNrIjKVkXtA7kux4daDvuD6BjkKoiq3+jzN1IxNxCyPEW3g++EfdjRRBpYR2If2QadycMXoUYVkVayoKL7rjSTHNqadEiRgy5PJNoLZ+UtmzRoMxD40SUJKtiEQvci4cP7xdsCnnUemlJSJALRUmaelg1n2xtpu3za9Cjw0jY+4ABjQNnahRjWH0+J5FlDxkSoIvJCyJwJiclIFhUUeahtdwEtsApeoQyKQIDs1E9RHwGg5LESEWEgg6ZYwqJdHsh2TfAtek65h/flYqLrgmEuHhERP53YkWCbr7BhqSHS4+2hjYCJMoUY8hczPliWjgLBvBSN95YY1iS2wOzNuU8JNxU86fx8YY0OAUHWsrZFXiZ8Uvg9skKUjBdgH92ggDNyXV9Rrm8OB7JBsJQ/ZL6RacGGiSAmRUmEoW9o1gJriJVOTovx8RWIIoAs01ictrfveUBpgnqm8YJ3CjcJSl6agt9YUQ2kK17Z818fSdZguJo6UQAe8DB4SDXKNQCi1jeOXjgEgp+4khY0K5NEa80uEbgPHE6lb1q7XwrfvUK8t33BLHMM6SNFk6NBtnw/lPH7teHvXU0ESbENdqj/pTWEuOPbKCo4VTRT/uQkcFpEh5Wkg0saIp7IhtKK1nWcUBNUSW4aGYALhQ/JkMfdwaUbGwHeNl12c2ToooWG4jFnw7eZVMYIfLRwIO5kL3iQ7lEfdNRxP+jb3JmdDOgFedqU5UZkB/aSAjvlRiOQeOARubTGiyokjL+NQb4qfNd0p6JuhiU4/JbBfHEaZsUGQBgC6Ev0DVCviTc4+3QXG4O058+wp0FtubTaKOycA1AN7W0cMAAriiVVvz6XdXfYbc7aS9bubQI9opF0wRtLdxVEUZxSBMf3YmZ8r2ISAjsBu4dItvUrbv+bAXSGfygfKyS86vgFOD05uqoBYjEIC7voq67ZAmNvJJkcBAXwDusQ1C5hLBQk2v6OFJjJYBxoDJ9i8wsvfHTx5ULZRI/R8je1FQA9TRdJoBOOSSTusVfPXNlv5oElvj189BnDwWGXVxLOAQLAA49ydR14kNyen0ZQ7j6Ch4LSO0yuxT/z/cy9hN6NYCtceYIkzKjAI8mlPiDJOd3U6DGXZccp94666oLkQQInkjN2TgUZ4vtSdJtCqc45sC2ovtvfyKNJD4afzOyxoY2Z9C5ahOCA0WVGLIBiMVsTJN4EmpgvOi3202o01O7eciycPMLURJWLdSBd3d9nhJUG+fnczEEw7oFdX1JNzc3H+VDZNjhoyWAAQPSEwfK5X5e4uDNFzxxtLW/vyfBwfL45i3RByYKvJMRPalWoSCZopzpn6A0HacjbKBK7Frm87e4t9cSGWsXk0Oq3wE2H4uwOd3OvKoxsxP/CK2SOt9bEmPjrjF9axxr8y1nHqaj0gfz6E/cnTbiL8msanebrr5kLAfWv9aDt+Ko77ScED++RjIfu2K0/ZVBcPgs2n2vYxDX5tqJO3vj91YW1r/YmWrrls7YrKyJ8NjceBad54gRPgn8d4fO9KdSfBmpZlIfajJ7glA61tHLuPLnYUW3b3PIoZ8VYc/jdjnG13zz+M/F3ga9Gjn9+IX2MU5RcO2HpvJ//iMJ9QyRIw2jtC+OmqWNYzr6iZS3x/fz/G2Gw2R6NRlmXu7u79fpKvbRO6Ub9IPacanuRzYDqK4XDo7q1WazgcjkZLoS3M7OGAbtSpMIy6UzUr0s8j/gd5nLpPXoB+V0g2vVyT/kYe3zl54jOZ/ZaslxZiPD8KSe12ezAYDIc7iBLTA9IvOj4ft99oAADXoD8EPX+mTb60okiORqPBoD5f7hwE2AeV/59cz74FK0pSlmWj0Wg8PivxZBf695H/UIzm61ZUA/irwh+V2+LR8yLwaehstr/VuRgMBiv74ixgm9ayfLBZG519fB+NPoZne/B0FHmeFxzyVaMwsgEUheAEbVNMutC/j3g14vfOvLtniORfy8LX5g2+60excV8w8DJR1AQdrzmjlqFr4p+UfdkQ50fN9wn/N887Kw2ceUWVYqPRaPz4j/94o9EgOR6PG41Gu91+5plnXnjhhfptLkBF6Mc9wfworl+/DuCRRx559dVXP/vZz04+QkBEeAT/zhX9QuTJXfzXr+pvqiqfWwRMs87XH4VhlvmQ7vVeGsV4PC4Izr785S9Ph1ClJqa2r5psnmdTSFdX1OHh4Te+8Y1nn3124xPS/KX3KtNzaS7c/eGHH/7c5z736quvTj5Sng+GJhgACa7N9TkNMmxm22P5kQXLwpm30dJctNvtVqv1ne98Z25fzD15hsSXXG8uqmMJEjA/ildeeeWxxx5rt9vf/e53J6OYBAKT1+wvdPHxXNf7fOZO/JXK3b0Ag+pn8czv7nls3umJ+4KwR/hvHeJPAjjCr6w7o6oeX2U7gUJVb8+8okqxUYjubrfb6/UKYXjlypXHH3/8nGKDAHMLXwjW332mziqmozg5OZHUarXyPL969eqkN4EIRdx5hstNPAUPp7Z6ik0+/XyW/bcWpuGSb2rcr1HMoQ/+RggPTSIpn7cQY0wpn700ik6nI+m1114rM2pIlrFc7PL7GnxE4Aiv9OOXUhxT1kf4QlDuG25DS6O4TjOOq0jekkZRrKg8z7vdbrfbXflgofntAYjo5f7GlrB3Itw0fJKxn9+DwhqrK+rNN998/PHHT09Px+Px6emAyIAM3H+Yf3Efn4D8iP/ijfifri20NqZ92hrXmxlca1aUA88aowJtYV+/KfXjWUyFS3NRqE1Xr14luehmONMNN6eFpEOXAKloefLqWzuKg4ODXq9HshwFAGREG3Ag38ePXfO/QIUb9p/fwa9saXdM+7RlBw3vxzPv7ikceAmE5YjVY0zeF2jgsa7/IIC+fT6xV9Ynv71QZUQAqVuW/Rq5Wu/xzCtqJqKffvrpPM/39vaOj4+Lo+r4+Pj69euS8nzmAjN2WTrpRq7TrQ/IQkAzxMH4jFXk6mB+FDdu3Oh0OmZ2fHw8GAzyPO/3zfivg4fUtx/jX3rA/zjBm/xbL/pfW5XkBLOmaVIRHYAccRjTCVYJtieZyMWfAykmRIkujaLb7YYQ+v3+j/7oj77x+huf+vRniY9AVwg+zl+8pp8WeBP/4CX/X1eXZ19pPLQbGMWp9XMzDGgGGztiMgvZ6iiOj4+LUfR6vWLDvPjiS9BVoU2Ex+x/cxV/DNBt/tNX4v9+862KQMiaangc1JiLM2N1LrIs63Q6f+Nv/I3PPvPML/3H/wnxvxB+hHjiCbzzml8DcZN/+8X4b60bBcmsHQo6BrkqVxSBjtlq3q2AgfsZhOXq7s7z/PT09MqVKwtzwfCY/ZUD/RSgY/7qq/E/Trnhkghm0oIbdtOHwXims2B+FL1ez90bjUae5x//+Mdff+ON3/mdL1L/Hvmg+IUn9Gcf8B8FeJP/ReXuXmo2awcA+aBGv+Z39+LPkUM51oaQVO6Lo6OjPM8Hg8GtW7cAAB3iwXfa37zmfxbQTds+itlYmlkc0z2fXiJJZrRGlRQ784oqtQ1J3/rWt5Z/l2XXrl0bj8eTwQCwhj0e+ACAqBvD+PzWG26MEf2z9OwMqBxFs9l8+umne73ed1+6DrwuZLCrt/VP+/zngo/wzcrTUNB4FDHCmdT2soX+nG06/X5VOYr9/X0UdjMZcAg+IuA2/ptT/rrkI3wrMQZOUuync0LBgWHKeVD1oNVRdLvdT3ziEzdv3vzud18HnyAfAnCET4/0ZcFH+tbWjgnI8xHzs1xXz4B1c/H3/t7fe/2NN6QIfFV2IuCmeEIAPlL1ipo2mPcLmoG1cc0C+q7N6Y3nH0W32/3IRz6yMhdfGuLbQEyZi0njyGNqVyWcWdpv2BcAoFz4lPgkoFv4L3v8B1i/u5eancxInc4s7u4lbFidlaPodDo/+IM/eOfOndu3b0syXqK99yZ+9cQ+BcSUUUwbHw/zpQ9LGivutghcXYseYa3Zivfh/RBdkwaCIcxFVwMAQkGW9PYo7i3eHsX9g7dHcd+BbBJTKoHv1VG8jbfxNt7G23gbb+NtvI238TbeRn2URqpffOYXZz/rQIdkhG5VV71d15A64KWq3EXRe0IvsTXaswxfCgVZ9HJLxQ+leMDw56jHVDz6lx//5eVRfA/iP/v4f4bkUTAjrqaS1IrgCLq1Q4P5WqSMQkE0qlmy5zLQpHgnKciwKD5o20sMAwAidHNTs4zgb5AvhKkRoFhjv/zLvwzgL7/yl9WLOt41aVj5bKBDHlDBcYz0pIESBywYcOcgDMxPhLwkeqm1os4AEsjEpqnjaGyO55QNEO8k21q61OWSZrDY3X/lr/yVOInjCCFMaR9LhmCpqHZFMstKl+20/tWUSLj8IoFLnr/f9aSwL1K+Soy6a6ydCwOvFvFrC3DC3KTInD4Wh1BOuYcR8GvQq1jvYhCeCPhZxwqL8K5Gka3+QgOHG0CrVdWUWFuonbI9IEAny7QEldBjiF/zVQr06dxTsJ7j68Qh2LV7FsV/X0EtXwrQ3ILhBZQrOCuKCk7IUcoxKhLrYhYrvp6+FwRpA5srNY52tzlPMjVfWk5R3r8IksaybzyFK1qbPvDUwiwFMrBdVR2r7RaoY2F0MaJuEU7YgXlL4LZaPUTMCEtNP0B087XzNpsgaekvlTTey78VdJv2GehrwZ/M9c6M17zqLDw/th/cEhRlEzJKCnLIxTE0ih7d8uIotmIhyoAwRx83N97i7yTh9KikejlnQsWrogzDYjg1oDYqAoNnjQIdMoNOhC1+dOEqdAV8fUZtPRUY5ScIuOFF4X1SLuxdOEXKfQcD2lbjJuHy0QVwYZ8Ps4NGC//Z8i2AhrK4RxmcUT0uERY3l7sX+mR/rbjyPri56s75IIh9agh6qswohm0dIVuVZwSABnjJdOrYHh6/A6ioOrJ16kQaEICYNs2RcEOY2/VzR/+qbFgVG5USZeG30XgHvGX4evSHyPcQjwndnV/QtyQ4UlBOOJBLY2AsjKmCu14wGMq3KwoEYQptY2bFiOZp9qeQu8MJS9xTdbEjCWvrVY05qCG7BPWg0003X7WEJ4HXgTndcx4kgwXdhm65QvTRvUpDv2+ghthAjQU+ZiW//1qsjQ69DyAUxbLMTJlIKgCsKgugrWzqstsBOcxsautY+HXvwlSNedTRAgkoc3U2GlUCcEBkZzj/6pk16MDYi3p/22H0BmwjB/6s5Qjkmi9SoNn9utzvlWkW9RURcGB8ybPXjZdCfFL+DuHqLpQPAgFqODNqPaWOAJxCAzE6ZEWy3oYbnkg2jZyUyawYlJCTybr7GbAbsaFWVQ7iClhUa96HZYjHTlcl8YgoewL6squ3nDFQMEuXdZWH1MvCA+SOy6nWR+G0qUWiYkRTMIKisS7lAq2s9gxsvk0DQJHVU6OCAoFDgNApME6q9niPoSEwZFGVSZSC06gMDFQAjSwESVE1Z1P3yVsoqgfOFPy5g8beYubJarBtDFsOBYLonLH5yVpMywSKqcWXAFnm4lrtcOGjAnJwvqj7j5LfFa6TcSpMNukTc01tVkQkgDLKcIfhLvENx4MW3+18BNqrYRElCLiMaIiZqSVmpBEgNiZ5IAoRSrNSGqj2ZgoTIsqi+0WVmt+J2DCwlmZnrqZtqAxMUZfkD0V+Z8FsNxMYAEBI9jLjB4hVjq4LBRf/QsgIgpRyMaZNVdvtkKAcJqAoH1wDA8HBrqmdsLejEguElAhAmzBYExoIp0keqbcAKv8sLDw2RBkgQblNtJCN1cWZ028zrORn1Shzee+RgR1cyEWSgkFNtwb9lInlV20sOFNrl2UmphRqAgHPnZpZYv0j4vuBm9KLMb6KcGRT1vkNgmGhzQ2KiE2EpcAB8LKyV4LvC09FeyfjNWyQ0ypsSUY0gaYxozKf3OYIwInCprQzTN72snI8K6LDLSXNz4ddiI0W2Kij24oaiDk3XToC7R2mFyOdIAsil8XNLEi67boJPlG7y8XNO8mVTqlFhOL5DlIEjaDTKAKcXFEB3vU0j58BXpRnmlT2rjnHAobQ2DV06wQ01r5Lghot1yjdCLLtLEpyBnCPaFGnQn+Jxvu+Q9E1qhAkBMAxNi5LYuB+W5bTi8Io55AWNVbUOSCSXdWrL7kJxckmZM6GoQVkZCAojlOoagBATsbkkpcBMKWsRqcjX6g9bpAaxCPyhyNP4K9QLwg3iOGsyK6tBImcURGRyaXbjjuMX6c9AX2UurpwxyegTGqQDaIhBqLknlx2IprcdudvF6GWuzboG4RHdyxUz9opzj2auqoGAIdOC2G8bkgiDI+6DhHuZhaWDX2z2Nwx7QWPj69pZ6YWqFQ1CZhIWqQnmrYy2KWScrWM4hJU9n6pyluNGdoY3pMMB08tDmVtQ1eVkykX+nWCaoKjzfnylmo4DoE2dQoO7g1NzL0AEXEcrOeuXHl5rExRuzUDSNRmqaiJhqy9I2+LARnUcmsQDVPhYUVx7BV+kTSXi4AIX1drbhEkbaVI+LpWEQXXNDJW0z8IHdDfn+s9xE3iRceLwhHn6p9TE6dWXUVk1oFSfsD6bt9p8rb8B6V3uCZeejeEA1OLbiLoWO8GI+Lmqr01oWzz2SFEYnRRMgPnFxtqkSsRx5vhfTBi4yFLQThA9kSwXsV0Lkz8awFHkxt+mwrO4vpvJCFTQbxZ/oni1gGcIJwkvlQCNjl3J/+t+mZhIE9qEm6+iZG/FhihnmsEdoFOMfi5+JO8pompYUuLstS2m2AGtKBTcATc14pHEkThjqa6/HRd1ZUZpZ6xTzSgY3B0Ia+meEroUOcJxidgQAPWNDUdAdMY7oUZh5jJuaX2bQlBYxTkzNs/a46GVqjeKmAyuZjDQ+XbdJFoSI+ID8M+YP4K8AJ4mxpjfnPN3FRpisiq/QoMRuAuw6fpd+EfzNUqzVkukgnx/xWxTueApFYRTVjx4KLbLuf4Anfo+cRGfVVDEeqneVkJf6f4bdi4LLddmQCIY/GlydK/VCjLFFBSQk/MlZi7C0x/ntRhKdEnXHhUEyW8VjTZc4JjwxEwELvCjDaM6KOG34RAW2udgAZ2DC3hVOpf/M36guEKdrMIylhjIE4GG2TbYBGXgROifyFpldYg2md/5260fbBFZXJsybTwjLC17O/LyN20PTIDRZ5HwxJtJxRjjzitfJWc/kdEvBx5yfy9yG9Evkh/JfKI8FTBsFkRmRHdDmFfNBxb/ENAUwAU16eWXCgMwkI1xfmeE1QujTaFYy2Dk3/Squ2eT2xkdRtwDGjpl99rwiXpBtdmjLucwnOzNVRcleY+NP354ndNWmWmPh8cpuRwpXJAu1xyPnF4AC2yCzQlF5JLAhJQtiUirvD+YZ9sCz1qcC/Szi8IHCPcNoRZsvHZuP2tiFkyRxEaegA1hBMiCqiOFTxLbyF2i8I6aUFLlZ8KQDb5zYaxFjkWJsQ0g1KkFnMs1oGgMolpQgbgaPsmKb0JABv0R52PACfIXqJepN4khoWhoaKRuooIAEXheeO1qA/KQC+4pO+t4BCBJpxxbboSZKLywmpT8ZnySgkqOIOQBTboDaeou56SqHQ+sTGGjoF9MM0hpmg49STNt/h8i/5E5PXlcnQLm1zk9SmFgBJ7ckEwu0B7Yioc6EtDYk8A6x3rrVS2EmTUIQBbcxn8HgB7xMnkwjE5Hc4iPxrifOICwQ4RLPbchjtbDKXpsY6OaqOFqTEHck8t6UZ6g5aW62MRzFNzks0o25pPUxdT5RogcID8Q+J7XdfNvwN7idYvglZmu7MymWNzhmD584jwJfOrio+A0XdtNUhC6UjbAAHDKo9moVIEetPZJDNjKOP4rTgrEnf/mbo96xxPhVzYN7S2f5x9IE936BEUniC+OosfXd3V88EWFJUYk2SpsotOeFqgSDqBF0CvWZq1LlzlmZj8FBnYqrEDKCgvXv5bLSnPBB6TQ1sS8/X84XQQ1gmypTsn0VQIVM/Q3xGni0PHQAT30rzPLu8t/UjKmZpjQVmmNbr6CgTkSDoBBBkUyPzCs4HUIB6TPUL7CPw14QW3m+bD8mic+sw3R1jNKyIzodIzfiHnTxmNijBLPXV2Agpqig1gWM0sUjhZFcsqKCIUYEEIZk16MxaxqbPvTf9mUpaUF7yDuDCO6HfFrrG7MWElQv16fhCQ8aqya8ZXXDKXL10DiymfvYLkQ2wrg87ZwGTfxr04aes+ogk16tydRsD4DI+5X8CbLHgZzmabKuBN45q0IWXAoaEB9cTxLt6SgyfwGLlPbssA1wir9kkb1/GpZSbzxLtQjG4peoyAe5ucK1O87Lxk+XtoN6UXpVcR7tg0CDidjGT2AYCvZviq62NipDfvCZXAHMxoDbMGpfURHMMiM07MZJcCMpepZCJZ21syMMXuvAOxIYAROPEYPXQNjUoiFNeAyJPPGDoI7gcAerf7K9BKmmWR/begQjpMSZJfdWySiXeJtHj0+xUE21X8HOsg+CByR4b7twBO3i6DTufNU7XaEMnuWnNBkcOODtCQjsVKo0FNlBxWuWPf2CpS4VZ8fhQcGkTzZR1ZTkQyLbuEGbg+J3fxiY7cqoNwGQUyGnJpLB+BY+ea6KiLQOn5IHhIPAh8xO3E8C3qa47XgNGmvV2pagAgJBe+Df8g7CA5R36HyGANYH3UHwHkcyeXzUVAbIiDAJB5ys17h1kosFPDSNgX2ssvsvRq1Hm93rTQFQl90PUF4s5sdlYFRm0wVTGQUoMLa0ESVCfU4YLBALRq7GXlwmhb5yeut/sQHAJ3S5K4opusLz/YJFI4dTLi0i4jrGxsflfYA/eqFyaHZpUkSBJzrXJ0V0JGBKYm/UXJnfNEWQIcHAeMpLErB90MSs4M3CEIgntA0wn6VdmPRPs++Kvg14DvSkcyL0IvZzGYmDuUl2jxSmbVI9hrpgeiFUm/93ZAW4neNUBhXZcDLqb5LGlUwsG4Y7Jg5cSRM6f2bP5SU9OrAYDWmdzjroHvpO4AAMkQwloZKyWKpk2K2tInk1eEg7QqLuvqZlnSLCQHCFwgSLQdNWjYhSG5MTqTTeOe4HAXc8gFh3ziABJRZAIDdu9VFkqnQK+4Diy//1T3hoEdU1p1ARpxAGTS0U50DtCJE/fotheWNjEdOI3Vmq+oPPmEoyMThim+DaOLEcgEETmUO4aGMRTnqYrfuhtE4X1pEJOQAHXA9wDvAm5T35Z/XXpdGBbFWIsez64Rs2amVT2KXIKXI97PGoaLXYCEmo5WNpFzs77N/grjJD6eboxIuSsQQAY3mbZEAF4Ax7xTPSiP2CeL3Mi8rlcDbGJ2jwvQB8Sv0aJVhsrN3teFOGjljjoc5Ymt1iSmv1AwmcS0gBPbYoSUOdsENLFzkJEQLEpRiPIoRsolT49b3g0E4DZjP1JnzwxXK6Y4gWcoqQp2d8YIPDWNxAOgNRN/Gpb5mNXIi7vK9j44YdlGMoc50Ik+NRZGjjHpxP20wAUoesUuNvgDUddoH3G9Zvwaw9dyHiPachkoLJZvKA/s1wPvSlfIC6nVMQcCBmZQy5EFkrEdtwQxj4AIBEhATPZokQzc6n+6mOEKHNBz2h7YBvpEnpxyVng1yuiU8kd8guGhgDdIzIvUFWOCivCk7Y/yAKb5IgRLTfdjIoXltBOTWgVvNVRkgKd/fqzt4RZZofmVIZEAEMrUkOIHpsnmy4W7UryHb0LEDcEhLGSGV1GfrYGBnbDRu7j8SAhxANv51TSH7op7hq5gggf14yazapTcUgxFxWQlMg8CUF+T7124WkFSmRDIYZJB0YQ1ORYsk4E7pneDT8Y/8sxXLv/e8Zc++KGXHnl8HMeVMmPyTeBE/rrsSZyJo34DCDphCkJGNsAMagAGFhkGArrFJysnmoTgYJnoL8WCeSXtTQXfmmx0gfYBy4Uj4A7Ur2fQ9aZpKRt2D3raMaX2kFZnEYVe8JaCvBhvxQWfqCRrxNELSiC54tYimwQMCNgaFLR7RODWwkOLK0iMMc/zPM9XS7wsowU063WbfdrwYtanEyfSkSs3DBwjbbgOMSo9lsmMduHFUpNBIIAt4NBxBbhKXoIaSWPxYuDrP1ucvgy4++HOQ5fv/pl3P/mv/fTP7MXS1RFjXI3hNLMMIdy2XRbyIpiBbccBcYW8SrsMHLg6jrBwDqjKVzFNT5CkITSZaEUlinICFrYLhQtWriQlZylPMOfVmP1Mejf4rKs/Iymr+KbICbPsZliyT8GUHCAlOpUe4UgHyqsONhEbBrALDaD8wuL8+sAY2APbcBO4toaHCOZF7u4WlKWTUoIOnEqcDAItgwnuiJTKN07Vs02yj3Aima1jH5jWrK5GAOqUYQAAp/d3ljG+Ckno0/PIkv13beckQ55WHUdEEDIhTwuoumDYJXrDEYpqKiUlhLKNFrkJCCBSDoZNO9+Nz/zr7/vKH3/n+8NDf/SffPoTz3/z197z9NhnGR5YVkkV+iEfb9CUhSKsJlG0tMBDwGYxcgJW+W9AqDt1slRcoIEi+LbYGOQYiclnAjwDtqXxX7RNrjYWvBozSA9BDwvfWX5BCwbHdGsqLyT4oYa2URSey1EokfS1pmC1nHuBXagP9bGbDIBV5PAjcJBQw2OQRFWUTt1SZOOkTkbX2QJBOSkqAi7k8ChGyMVYFIzdsO6FE7O+RcLOlhneVt3yCRoA49273ZZg45mZaO1nBE/OFRfo4b6wowJQZ/luJcDCcsLm+u/DcmnbgSeif9j6YvxOfHLws3/lf/7M5776xptv0tY5wMiBl+SVS78R5EIODqmxpwakFfetCixPgsL0xFhzbuSyUZGEUfj5kyMhbLtD634SGxVejdnv1BKfhl6c3f21Wi9WWs/Dt4TUzA0BcCdMnBjJij/K2w44uYnRmUr9VrTcE06BsljAGi2SsiI8n7C9goCW6qtOQnoqKGEIjaWhsxvUqDKaRmmYZp0PStQ26EjVFkpZX5IhAGCYuk+IQu2IcBedHFCDijYF2O2FgKIzMItQcktMGRUc6vMCdY0Ztj/BMVFb10RaAYCTOZVHDI0jvJURUAtY6TEdGdYxLy1D8JyJ1xkFPfd9l37iX3zpsZdfu9Hd23SmjDUtJyW6RRYlwTUWR5CzXpx9FJLyJon9uJlVz5wT6ndQ5BjKknYuA7TN73s/iY0i53YNxycBf3fk7wYcVxcYrweyhpVBZfxJcbaXhhGXJLlNTImAo15Q0EqJ9IrPNBZ1rww8AFvwQWQ/XMh2Lmp4jGRtU3eF42uMxJrkTCurAECefBXiRpcJAUKhLIUiAYMqH6gbbrEy5K4y5rICp1QE94k0w7r60AXpiGdCEQxdYbJwICdH1Ng10v0WDbUKUw1bKABEZxpzFoGRn8b/7z/rNN6ND3xw3ccEYASOyQEEYQwfCxH0YpnW58ctgtQT7hfMUGbwFffXuZLGnNQWiqOo8kYlj6umrjUtk1sdk/eR2NiccwtQV4AnoN9fY8sDEEGVqarlvwRUhM9qphMAcCaHpAF9aoipoqeZzjE9espO79whzpYpLLo0CLRozcCGxb5fUK1vy6ETx9CxR7Qn1jdBg+1RFgWEJE8MUYjPtPOXCYULSglklY5fUYi0O5IFQ6VuURiDN+4LQQMgd9s3tbfY1xjpfU+mQLsXsJwozTWCjBHI6WPHkMw1peO+nwVGAYEIQHB50h0wnZLLQWvo5qP7PW9ingFx5UqhkXEcdQQs2EW3WwsrQQejEuKyxK6FZuDG+ENGTfV45qlEhW7Y6jFKERtSEDPDxdSfmWJ7zm0G/4Dr65pWOeakXuykp0BeGL7FosyLAAmFvuaOQnoIkm/OVluACmv+Pd/5AWiv+RWhroc2vX9Rtb4JYGy4C/TFLtACojDcsaFFgrszzdTOUkdMkzFr/OyhRzsJhAPGaUHfmfyQCO4lqFQ5/MiRy/Y2JSz6oHAp19k5F+0EETgmjZ47h64czI2Tu9b3GAgFWlpJojKKLO2q7K3sV//qx8ZfaeorGw0bLsXpJfK8EFJzLJiBRsT1a8UXSMlKnX5Tw1OJKgtbMsVTXiHZIfYMp0LPC5rd3ddMTsu55TvEK6brPiUxXLh+5tQtTda/Y6JjTygdZp/cQaruxUNNMqy7sxOAbFbrWwPVEIQ1OgEMqTHUjkSxTHcJAhaTV1OQLFHxr74WmIBjYails3nZPPVIWo8c6DHm4gGZLV8wRdi4qH9cY7ExE/eD9yNGF7dIqR7UK/JW5hTo70UQaMCHaUGGDkYpIeCbgBroHba4Z1usWlHId1epU9S4iKvZ3B697RbA9dcbqSjGPmknp0VTtr5ZqvAOFvyHm/uZIDYC2KGCbI/KgnrCBRhqve2WkHOrfTbebfEmqjmp1viVvxchgp0kB39R65tN4MgRL8bt6uDphbQswZPLqtfgLRZUrcQw3nCM1maGFz+LTybzJgkcADm4b2r70jg0qKkIEuiYOkDbcAycXtR5rvSCj/c9LJinxQcL8MR86SIOymTNCuVvPnSTY+y2ardcScE6NjWezTqz3NS8b0/rJ10le5jG8NyRU75FbmwTGwTbQFZEKwhtWAbvAf0dH9CWhbXJAnN4PDz28R/62L/86r8aDCqp2v7ggA0wrWx9yT1m2+8I9yOEzfbZhc8ml9Gmc/VtCKADt+AuwNdkhosZ9UjNgItcfiTm4h48lMLNxtJpvRlRRnaAgvvjAGo4jo1RTn0Pkw1fNDInk1xuJnpecZcvgygAi0R0jOVjcUQ61HRpob7sPCi4O8a7nBrLU3IsxCbVcGxcYIzwSW64yo0m0CFSkBfsYZFDUz4RV0Xj2zq5TWxYkcU+265qgIeG5u6qCAAAvCcDsLUyOfF97/r+bzzxzeefex4TAXueegn3KQi0yOTyLwQxdKXGStxPqGXzT4/OUgXdPUGNot3OHEWQkAAUmX1TgycJ7YlX6wtgh3pEPouwUn+WppsEAp2584JAxxDoJ0BCfuX/34JGpeUjOoF8epefnGmFF3pkRSgtoqZxUABigwU1VWWDIuW0wU7LNKVV84ZtDbemBhORWDScO3NYbhrTc1fuFomqdIet2CY2OlS28EpYhMh0gEw62U0VAQAWgWN57raXIas27RF40998Ob74oYcfe/5bz/siAeQfJMjEFpLXouDAgPd3tGQVimykxJs9iwtlogaG1cBzQt4TeljlLJ1mhpO0Q2HvTKeAoAGYOw6IAPVrfr0B6yx1mGrCLtF7htM/GItdBeW3giMjMmjgiOcjMDElEryzCJlxyaAI5NDIbWSKZbxMGbg6HwfVBDJtyMcyALGQWjuSHEVKebaZ7UJqgM0w/9wlpl4AGtNUOKNJAadAH+4Tit9zkE5sFBsB7KxlAmeDuGTsuU53VJ9I5GnAWNjDasWOAmOMvvKpv/1j/+mXOu//vpN28/6pV7FjNGvmIQ9Z72J7/+CCtI2qzHNBvB003uRUlxQfkqWZB6tbyME7QJaUSz8DxQ65UouegDLwwJBJvVkM4fckKJjUdDTJBhkoSpEWzyUQRSCzrZTMJRw6pkvIZVUGq+Uuh6JwN8p/p4VXJ7kRAH24S98GBOUJ56lBdLlvuisOBDdlE/mwuxzharEhACQ7jmyTx4EmFPUpT1w74vPSGJUVO1A6vPny1X7zWveAPJE2vrPvVZBkO6QLxDKv7T57E/NlFtYZSyVYzkQboyhMcsK3owyYXnJlEjecHjbpZQZ7GKmkr+u6qgSG4CU0gHb16VPq910wk5+AFxhhtXMUZ6uQORuGFpSZhWIOy0LlzM47GtEYEv3IgKD+lOFh+5OtgayVYVy97ooL++5ledyqvBCBam+r9RJN513L1ai+0hJAELq2fSYIdIgr9G7c2dW/qNhxN67m1powevAR/M3/iO94vGCjOkMtz/scaiilZtwUHBMJbG73GieyU2IAjrg2pK+OXa1eSYwq+yVz4tY2e0gLepD3OkeHYHc7M4i3DJeJ7veAji0CBjShA+cV8KrpEtQhs2nnJ2PIzp8KKTWsJrVkMgK8AaL6JCxl1WDHglwbyXpLcIvbnCTGyRSKNbHWSGUdKku0IQgZ7SAgpVBoIgQOqCjsAR3O7HCRV778Ov/f/1F8x3tpAX/gZAYAtoMspp8NGopJJeYm7U/iRi4Wx/QihYxScBoVxGDKwILDlDRCyZYcWUKK+BRx9SwiBo6jTcx3FHAgHN7zU7kJtbfb96mySqAy4aQeAdo9BcE9sEVk4KSuWumEXoEFV+FtOPPTJBlhNa2CiQiwYBNmoWVIAqhhTFR1EsEIVtGgFKqDIpETwxxZSTNZwSwya2l0EXegNWIjA9v1Thbm1K6VNRYVO8bgHhVAoD3qfOJDP//GR3/76PadLGRAjcrP3ytQM9bIE3ZgWCPZiIHYh4bC8OJdrCr/pBsAK0Q/AcpNJBWA5NtQLc5ireSCEF5W897wJRLXxM69DXVl4UHcPomza3qHPpTdy8JW9SA2Dc2ko1QZVeTsp7W83KZTERyWJ+oZ+rrleUFqOMTC+bx61BC7NxuqyGaammQdiuLIinhZRTKW166QbSoRrjGRo14NyjQsi43CIG0dzRwpKSjYinZe2lOEqFNXHrgPZfqAvf+Jw+//Ly99ZXj3qHjYGcp53ufwI1gH6hQ3h01zIBBDR15j0apNdsQ2OZSflswz90bqluEphYeqWCrj1IcTkHFSWGPbdFfkggigbm9OviMoPpJU+W6XaAKtGmUCAWCEMLyo2is7gIicaiexpZFSYIoryEsKapmbIjSGxm6joKgdOntX+ocYppRiq8OhBM8lJ8PuJsRNuQB6Lo7FocEhFSYFI1DEILG7TSxXxRPuBMtig4AyqVPvJFYuDS7i8C6btKFrDDbwnfGL//jz//Cll14SIPc/eDIDgI0MY2AgdoXWxhubVC/u1oCCeo9Ax9gSTqU+kd+/R9AMDg1kwVTYmTcoHxVJG+YSr28SUgIYTI8uxll1HX27QLlKWLcmo3oRSXl/z5jySE8K6xCJglRxKyI4Mo0Vx0Iui0bwos7FKQxozcXjLkOgOAYj0NjdQwWdCBJjwAaL8vplUwbgjlL5quuiwkhl7XSvBgBAYO9iDIsAAMoKHmwNcef5W3c+f8vc5hmEvoclx+TsW8y5cQgYwsdgi9yjGkBFSWcxLxi5k+082WzCy8TyfbINnfKiKK12BAEYQWM4JRMMCGIgA2j0DCQ1d92r0DZy8c6mVBhC6AhXFk1bh4aGeU+bS4qeHW2ojg1BIIfC8J5LDRLB5YLSgqBreGLFTEpQIu1GmcmcHge1GzQkzDxPq0XhNFRhn18dAEEPzlC72DjzQiZUj7F8A20XyjtNlaGerjKxtdajU7CibQSuZV1dA42F2pVf64KA7G7wL0YOzelTTqr7y7dROP6Sq0fYHtUCyPlQY00qMtKhvjAUO4aulC1XysWgIKFKGz6BjmgrPuFZDQ/nhd6szw2KKG4QETYyAA6RkDmMMpgRGQhWRGb3TMebx0ZcFg4EzL1SAl23BnWs3XMLGriVFmEJku6lqmFQRrTEjMzMjzwkSqw6XLPKErj7UDPlflcg1JLcqfW3/gjL6YzQ5BpIMNAbkQ0LDSqjhwuwo4XiNr3u12KEX0yMZdXERiCtDlTJ5dL3wud5scjBrwi3ypgqd69mM7wglFYRZ/EvIarIPqMZDGRZ54NiPAHTqOLUFJrLOyZ8I2gI5sIQ6kqPSJelIbgndSZV7SREYlDnKmFAc80djUUND0PD/MJqeOwK8zTlZVG/aJgLI6jOfjgCt6Vl8QGiuRQzQwBogJdNJ1KfiTO7HXRkxkCvk63LoXSRwdYsZK4RBSVaSwwTczomXLMpDTmQVDcCADmpmXGurl8UxOaMNbAaUYqiSBDB2Ia3HBnMAiY3jU3c+mftmNrYED9GQRLjhahlKy7xKN2N2AvsMIUe3Ybug3sx3+G7mb1AwKdsl+eSHEW5lvT32TbuAYViUIoQTcK55TABBgnwHOilGo5WA34A2CeBokAuII9sQleFx6knyMdle+btKCNHNTPD20LYuHYJdD206IOLquHxVuIGfJmgdhGEHi1CXKumz8BDsUGd7OzNeO44Mts3pVUJhOMCVQ2CAWhILWNGFYajRc5hC5ZoHBKA6EyMLiCU2UXwau8AhFog4yYHVCRGAEWntyMPz8eVktwx2MLhsWI9o3Kxqi7y+VGhbTCajqVxxIEhqAgwqf62Q/304LnJlwjUqdjhVHY38MvgiCEYyYI+CCuSQ/QitAoTb0FRlQ4GEoVDhIWWQALwkzixIW5DJjU4fy+czthU/qikH0jNyxRYWRK8bGcyJA2B18DXFL5O/5D0oZyXTF36wGsE0xjQtm2yjAAUwD2yQd3R9yphySqcuOVbGJYbwAEwkAxmlavC0AEz6th3YLCSmYABlDv2AzpbdhFBDRyjnSYIFB1pOptk09SY1krWbHHP9yET0nIsKGpEdtI6y6l54/5bbzK1BNuYJeNg7kW1aOSQcFG5hwsdIxvKma8nkBYgRCsob8/2EMKdWGVfrrQ+kgIGjlzcM21Y0GOoplfDW5GeqU7oDnPDV2g3TQBBM2JCXIqJ5Cj+boI3yQMDVZbDJUCImhzuRfzn5P9OU/uwlYD+IqB5rkaCg5B9MeC68o9FXIuT4aU11RAbmPhetn+tqOBztm7fh+BQfrfMjFg79jZghjsO0ivlOQBATdhl+onjdDd+IOXAUWQEurbhMiWX+gnBx3WfbuJ+QFuCC9vc3aFGjoXlW8vJzcDM3LTy1u8LQcJNqREAihg/FrVx5OJ22vMdwZaT+4qUhbm4D3k/pR7FCkojvLkZxhXzvd5pJcMYOvKyioBhfglMvBqxnlfDEPYzGHECJdAoFZXH7EXiebncJqXZCzkxLznKz8OMYGO5HOji4uX0P2kViAGAIiHVqBO0HaYaFJBl9VIXXqad0H8Aeud2OorZs9ph4lCG5cqbwTZIheLScJ/amuuDUo/sAWtkRiGA/VDWgTRRHdc2Bhl5YMh2F2Hl0AkwBg6ArFq/10AY1zhGCaC1fQYpMsIB0/bNQFvIsSjFcO4GxWyZfkORcDCkRYhkIkseplJZL44Ywt6idchCwzP3PTIzxLjpupUHygEwGnIq3ItbphqcO78JgEUlplmRbNtQ/m8eRSizDJ7JgqEJNkgDcupOhWK9LdbBoR5VVhFY+E2yV0PTS25o05sA3Q6hJr0n22Imlt0J+JI4MkDCjF5iSXLMvqA6d5xkZ+Tlr12/Tmt8/MoW9wCAi0klmWhUBMDbxk8BN6GPONrFszaNQwFsqiBhGHz7+D3/8pXBTzxw6wPX1hobyprhb/1FbwMEUErhGxGAO+RoEzucAF51VUzuGv1kFmGFM4QRUtLIldnsJitgIOTifpV+H4visjXEhgfwIGEdCsqLdLaUEJjFHAvpgW/cfOAzr33p+x9tffTa8kkp2RhaPNcwkcnzDiQBCEAA8uIfcmAYuvXAU0Nv+yDOg1I6TbzeBRkBA9V0NWAZTfRM3DDLJT0ii7jOnQVNbO/67AAj3Pqn73/uuRHtW+/7IJsZAIgaiFp7zqlw0BrRkJpkg5aJNGeZEcA1BreEELnSAivuA+3JiZzg1RCR3R0dffu0/a5O++bg5nN3Lv+Pn0BBl2xgFyGTn5Cj9ZW5cuHLzlvlEbaUpVEpOZKjXyHATUTYTCjsxN7NwQf+ybdvv/+KPnK5cIxvazgtDg2g1k/pnI9rKatREofEV4Q70g+JV7iBmkIAWkQABEqBdunVE/3qyJ44iIcVjIkENRLuS6+GCI28/0rvyiU76qP5pTv7P3Tl9KHu9q/d2KTYqbDVPGBloMQ8BoZ2pbwhMSGdrCk2RHSv9/PffO32Dzx06dHWSa7WQQtBRTUIHTkj2NW0SiAgDcSctZiv2WEiIaZyQpamRy/kWGism8/eeOcrveH78sZqC0VZ7M5MqyWiw6zQ5xwauw2hU4YT6iToTdMtx7FjJA7NYkAOglMbw0WAgIb50Uu9g0db2TduHz939/DPPO2XG2qAZiAEsQ3LNmX8AODYi9wOihjDOvfizqWG0JzekXmjP7raHx41w8lweFCIDXpZ0W8JBmRiVlQTIQKnzD2F8JvaShWk4FwpNZ0WWQ0ghx8BY9geEICRUrwaD7xw+x2ffOXwt/Wu33zxTqfx+Z+4cnTpcDoObxkz4MTQr9rVAl8Ies4V3biQpTFzZtiChxzFwJPB7StSJvmd8dceOPgfPPPK0Rt3Pvcnno4fumyt9V9kDYdYQYW20qtl6pt5sTGTJZH+Hegu8HHDk1pnsKJB7cLuSjvNf+RXn2uMRr/x/Y8+0Kku6CHXziv+7hDd10/e9f/4/PfdOL7+oWvW9+eebJw+3N3S2wjc2nxPFxtBV8aGZYII+1XTH4UOKr4rFJcnJCUdzLcp7L9x8r6vv6bn3th/6e4zD1w6/je/f/TE5D4yMVjZAZUJgCJ0WtM6msE6SOS3ZC4m5lgUIU+clLBu8O6PPKIbRz/x7etf/aEH8uZ8ExKoqBDJMTGSBsJpFo4Q70TdoR0B/YABwxCIMDdz5A6PpXkIE+36AtP6KJHXvnN37/dee08++sA/e+7ug93f/YXHj9thLr+PyqQmNjhaKGquRLXH4hZ88WYqzoJdBexfOnwkyx4MNtibZd5piApfS4s4pAxgGSey3iYLVsWFJYsNgA70hLGwRx+6bfNqUJAQxnnoZmyyf9Cw6Jpj3ymiVnFANcQTzDMhOhXumn/ROSIA10LE1LwbnGQIs7fCmBziwmJIm2eXAAfvOLjVeec7Pz3+4f/7F46/dvKt//BH/N1764SDKFhiuItBSWUC5gN7pmLD3SHyJvDJaN8f9AF5SxXurwxF0aE49Pxvff1D/68vv/Cn3v3gzzxqofogUn6fht4W8Qwt4vteP+re7p1cevxrl5uXx9vfH/vg0aZaaQJtD9grGa8WvvtVxBD5k8DlitXu/aLWQu1z7ajT/NWnrv3YePyO3/zOT90Z/PeKo4VQSmgA5I5Cv+/DkmnHCn28FqecBCbmWIgMgElF7hJ06T37l46G1/7Ft5996mr4kcdtTIyIAXAKO3LvS2PjqdgzG5FjwKmx3F1F+KiVrL6gvAhdmW3JCz921aF1GQ78Azfvhm6z91D3uUutPPf5nHBACqbGJtpIURxCPjnEo+CW7nc8O4JzTp51Xe94880Q8+fe/+FRs3i8WfRlT+/8sZeSTVIlImqIjRIjep6WvULceerK139YBx++/Pt/7n03bw0P3nFYsZQJdAwZ1IsclNEpzM1+P9ht5iX99nKs7UIA1ZlqsRFA2h2Omfbf0f3WA+8dfuno2gPZC4+0dpVxJTlX1teStjGvasyc/3O58WGYhc+b3wF+UMv3Ys5qklv097x+fONS+3M/+DjXyAwIGBC1jLP1rtpnR3EHPb7a+cd/4unu+y/H9z9wdKMfH+lun8Jj8HTTBYGgX4nWtNVAxSjxi0HXxZ8i3wWFmXqoCPVrUhBOXtXgqUuNa52vN/nVRw/aLxwNH9xbFX7KoSNwRAyR/ooJKHN1aqQO0IHc0VqO/pmNdKFvjDQOhRE4MPX4mbD/x0959Z+8cXT90SxmGAcbMR9Hz0svLcwK5zgWmb0nzUqTGxix7GJJ816dETSgobvv2PvWRx/p/szje9lHbt8adt55MPMiFsiczW19yN18Eqda1Dm+eLFBAnNRXif5+OUsdGM8Pu21mpdLrrphkb698EW5U1vD8UtYqAgrrC02VKyztI+OLjWv/uFHYBhexUFjf93OJYAG7DAog3qCEL5L+w5gFgRXdX6fu5sRZvkHFztUOMW3hmmhWMIpcaYUMHis+9W/9JG9Rxvj7ibSMhaewyS4+Sa+7CVfzmqt4OK3NJPDns/icY4fBB7NwdkRYIEOEkI3e+3f/ujxB6/d+tDVbrULyOXAMHW9E1BGHgDDSVzc/EgakESnhIpr/JkgQN1G889+MBpIHO5vZ48TxFumfLPHS7gKWFUohTskvAz9U/GHTT8CdlR+ZcBtAR0VKB8Q2L7cGkP4Y0+d5LJW9SlPB04dtV4dwU7Bxpp62hLUmEQRp07CKUI0p3JhJJ5SffIudBc6Em/DjoABYu425s38oS98tKPHn+Qb5oYQJs7lyXAJLN7flzGVDfeSXI6FYi2OHtq7/OeflmEM7D+0t/qyFWCNjSZOgMMyiAkA3ZAT2QVfpopt3bKpxysjrz/xZDfGZquF0sUqlukki52JREzK5hYL6pflC1V9beMM6KRVxjZwj8jEVwO+AowMUGGAmt6ylyWHpMeif2RObEwyJHc7gmJl8+OXB26bL5gTJSZp0aREfU2Vqvks0OnfzWxyJYzhdcNvun8003tRJh4LfhyRU10wsP9gZ/BvvLezljmEHNZk/mkLbaANdKhTcC6omleMkCLgQg6PYoRcjJN0h7Nuq0ImploinbqpVVf3PJTJrlZP2UxU96DfEl8FfjLT4zmi6XRb/uDWvoEICWkBtRDAzrbHApjNAR3gQDghR4F9t+NMR8Cd6HcjjzINgD4xckVJtAl9OYAizaT1yOPfeaT6GdWPT5AN94BlzgHGyf6b3bwrjCFGqbtFDMtn6RISkG+qV39+iIUwo7qaWvRCu/3qBz4IwMqAFoJAFZeEBKQlohmAwNVsx4sXGwZuz0+egEID/BZwc3Y9XSc5BGhP+P4i1mXWxCROMWF1WpGKkbpGQwKdpMPE1FDz8vxf+fTUTjW1UK1TNeaECgXhlPwscNP1Q9C+IGMETqChY49qg8bVnM9JCyjD9RI7b+AkM4BNMANa0DSDMggAJ6VmSvIooRAeiMQgKariPBCFSNyRM3BZGyo7DjhahsOCQXJjay58E7ye4yeMT4H5fVfxQiT31t/sBReYC2PagBiAR+YnwinZB3PhVDYOjITDx4XOECdWyHIS5+rcEeujFldlw6q5qVI2rMaDbIAXrBAy0lHyaJlGwnB7C0Rx6RbChnEAxS83GqkI+NiYe5EoQ4g5uXU91YOJziICqkE0yWBmpktFcMIsJhrANDyGghxwATZvX6DAxPJNhd0mCIvOnQsXG2ynBgIWCM/RvwKLEmeLr1JyIFAfhB5a4GgWpHndo+jDwv9hTnde/iFQI4R3HczOSx+WCVmen4awaqFa8GrMxQLMXOUj4GtBd/PwwxYfLk36HBvugn2xi7U1PMZMTA4q0YQac9qSgR2bFCeoAgGioDcXwDrJa2eGndCPScR1xmYh4CD6fvKc36Z+3fn9ho/eq2TgdDRkrYqzSs+RJ+Aps16hOpBjcEwfRxbmu8AyqnDiXJikiExfy7yGuPZdrZ74FeXwdmSJKsqV08gm0AzMoCBZ5LElXkckIW7pTSEbrbOl7LJFIZ9dABUdyc6DLQjuDVrm1gCKSogEiwRNSC1xoyNFETauULY9pnrtSWCFNvuCxUYAtic6lBAdd4hPif1AaMmyH0KYj7WVnI8a3xfni4Xyi2Yd6QA8NGXOJhgcTciABkFTQzIZJRYkx0Vup0rOkeJGXGWHpxOJnAGpnp/isxUEFd//4Q9/6OT48qd+580/+2c/++u/9nJ3b77FymSORV3EAfBV4jdhH8v8XVEBVoxvSI2BFtilmli+ZdXKDCfYWvYgCWvjgOc+NFHlk96TANCIDBIQIZUm2RSBQwFHxd1znYiSSX6Z3HblnOuQOJK+FNGhPlAQH27qS2GtuGjFpGBDCZ2gSq/GbzictEI2iNMLWXlFKox4izwVF+ZmUKnhV6saSx/DiuAhpUzIjE2wabTZ+yUgsCSJSPJYkiOyvUm7KTh2fKvdL0blnBE9FhnytlItrD7YNdubvZjZHQ0AyDZQ5GSt+7pDEaseXOaJFhmI8AYwXFjDpdg4fu04bRT1wE5R6Rpz0apzu2yeDhCApN+lfZ2ETfW7pUw3dy8kB5vEw9TtBctD7zeOUBgjAwlG5CLQKNKkqaDQMmWuLpTJuoZiqQVHG16k1BNoVs01wVOiAWJhBlhqpXOnNsVT6kRIWboGni7HUv2hTvv2w80v3sieePNbn/i1X//1j3z/dx5+cPqGpipXCGFqoZrXwybiFtlpU7dc74v+3nKeZ5qsER3MM83RoTvVjo3qpRWI4SYF//ilTStKEO4Ko5RNRbXc9gMARUBiTkVn5NZqoAT8efFOcRtYj0i8UT1fx8drR6HfAAcBj4w3B80IYCDDjPC8WPXzVoVpbxcdD9Wtrfm5mJFDVM7Iyd0TzJk0p3tqtpvW/2oVZLEJ5n18s91aXO8kxRiLgylMY+Unf8nzvFilU3XZVn41dWFOt//J9WO1nJkhgyyAy1SehSRUTt1FajRgCzyc8PxWfYNANIUj6mT5ejD/f+zDX4ENJ4YpgsdQq4IkpvZJe1fY4zpPiW4KvaWkt8UlFICXyailqBQ1ZMcGg7FgWN/YhwF1vLDTyucdPHZQbzCJYLmnjGUpFsF97p4ZSCOjyrsPekBOQrFQAjbAgM5swx6/egzg4KAYxTQ8aaJDAGVUwVR8CWVKMCetAQiQqexpxRNpZqT7XA2gQDOD+4ISV5oUAVdCwbw5Lp/iqH3y6tV+10Zx2Bp69/bpMGv02meqIl+Yok3oIGSBNHcAcuXlW5mnEdLaHGTjspJllKC45gpdjOLgyW0rKr2Y4GQVTa9HBAMDoDzf0oqGZb301VHMmm/DWnJHnFuZiytqTeNNoJVGZTMLeEFmAWCRseNzyUoZA7YFP29ZUStDLOdi4yhqgoFGymFTxXF+FLPPAcGMgE8q0E3fU/R6IWiF8D544iDp7r5+JVcjoOB48TXRIIFmOby37ANdmgt1wHnbzTJH12Qu6p603HgtGWOJfMWYYXFdoVNpVBKMJDKzKN8iZOdeabEvzqBDLboSUr5wwMafbHDf7GbI/9t83B9OGzLaO0L4aSDMNXUC/PMYj+8zBlYesPXeTv7FYT65hRHoZvazbFxDXH0T988omFnrj7bxOGRRxxr/ynhbnbsZDsifD2F/7icR/FfAd2N07axi0dzf01bUPls/1+SXbfD1BEbMqlEsPf8mwq9pfJqfZ0RJoyDY/kAbPy436WQ2FwQ+2Gj8KBDWL5j7YUURbD7Vtp+CN8rqJetWVEb+bGg8XpoX7vHurreiGoft8AkMf2tYGAmWGqrc4/fDXKyiOGkBJO7xA/KnQvZJ+d01muV27O/vdzqdS5cutdvtZrO5t7fX6aya9KwTfuBS+IVr4X+2H35yNVWtGk+BzxtjaH2m3bi2wG0WGH42NI4KF93kn+fAp844iIVRtFqtvb29VutMV/VVPIXO/6HTbDTmFiUfCNmns+YYNt//HY5ib2/v4OBgf3+/ehQ0MAMzbGRJYdua/7ydjRsWA5831OnWU8Bzi+M6In82NEJC3OjSisqyLMtWrz3M7JGWvadl78nskcR7DN/J5tcbnb/YTgxzXB3F4j/26az5QMgqn77bURDs/MV2c9C0aPNzQeAvZs1eCCOu7eeu9sX6UaSAnZ9rN06ajFb+s2ZFdchfCc2hZRe3L9bs7uKM+vlL4ec74QcS8u7YeHej+d831+yL6j2+k1F0Op2Dg4PDw8N2u2Yh7nUoTtrkPf4U8Oshe3eoHdpRLp1Go/HjP/7jjUaD5PXr1939kUce+fKXv/zCCy/Mf5qwh/SXrurPE9kN+/s9fCrxMTIUSu2qBCQUoPmOnzm/cmkU4/E4xijpS1/60uxpAIAMVw17BCNOctxMfUCVWabo/+qlZiejeOWVVx599FEze/XVVz/3uc/NDwGwFp8KvAYxx/WRXtiknJtkkiWxCSx/dXF2ApCSGr00F6PRKMuyXq/31a9+df5jhD2Cf+cK/hSA2/gnr+CvK816pZrVLpZGsYR1I0oeRXiE//YV/WkkjEKFI35lLggaEFa5IGZDOCO2jWLOUkmbkBSttfWoePnT3myiZys+uDCiXe2LBx988MaNG3mez+3u4qHFGfU/AXCL/+C7+F9t8YABwJZ9sbrHdzKK8XjcaDQODw+/8Y1vPPvss9OnVTm4Uhd73Q3OxJvaIkqxUTigut3uycmJpP39/UuXLj3++ONLYgMQQVMXdcrzsc/Gbzb4NQvPh3E+y30QQOqWZb9Gzsfovin1YzxDdObSKFqt1t27d09OTi5fvjwej09Ph4CBGXH5Ufy7l/SvGcIt/sNX/P+YclRZn/w25zc1oQj+noUbVfe284+i1+sdHByMx+Nms3n58uU/8kf+yK1bt778lW+Q74ECdPMR/ruX9AumcJN//2X8b9c9iUL4XGYKTuFNH/dH6fHBffA3QnhoLiphBNyiUePNCvrSXBweHj722GMvvfTS6iczXGr6UwAyu5TYKwwYPpmF1wI4SknzXx3FEp63EGNcDThJGgUNCEFXGnqK4pZRkOG1jL9uZpnm5oLka6b/TraB8HVX+2I6isIR7cKkeqtds7+wpx8meMJP3/S/uxruRiLcNP66WXPSzzdVuaIceNYYFbhYKnFX+6Lf7w+Hwzt37qx8sDij9jDx3m9ulpD1wc+GvF8RN79uj+9kFPv7+3med7vdbrc7+z2LyCg2+VSGq4CPcXPkL6R4bIqTFkDiHj8lP2th6LUtVOXLGI1G3/72t/M839vbu3HjRrfbfeWVV05PTw8PD/M8Pz09BQ0woClScNZxPOlNxL8cae6IGi74naL0+Zj/hcXJFTBYaSQFS6Not9tHR0f7+/t/5+/8nWeeefaXfuk/Af608DHTxw1PNXGNQsar6aMY/6M8eiGey8iLI49/XdX0DzsZRa/Xc/d2u33p0qW//bf/9m/+5m/9tb/2vyP+uuNHyZvQk5muUQjcRB6uked/cwCDQHm9goxvAv9LacEbSIzd47bDemkuOp3OF7/4xVar9eEPf7jX67344kvQVVBQEVhSrKXkTfimxn8tH8eYmBpWMYo5FJFDg6q2to3iRUnEZbP3Elcgn66NdZA0+Jcj/BYIyWcJj5L+Zcx/a2OG2K72RTGKBx544Jd+6Ze+8Y1v/J2/+w+Ij0BPg+/ew89f0w8QEdBN/f2KPS4NP5fjz08vfli3okbS/znmq9m0u9oXMcaTk5Ner7fxjEpaHvmbCP9hXtmtdXt8J6M4Pj7udrvPP/98v9//yZ/8ydu3b3/l978FPk3sAXyIf/Wa/7TAW/yHL+PfSxlJcdICqXv8hvQ3Yj5Oi4WfxyYFpdlsPv30071e77vffTXYh4A98c0u3tf2JwSN8I1j/+0UBXD6oDJCrm4fz4f9/f2f+7mfe/2NNz71288Qf1j2DoB7QksE4gjfrDmKCqfZPRjRwcHBz/3cz73++uu//dvPAH8YfCeAPaSOYtLJLfGdF41ut/uJT3zi5s2bn//87wMfAtvEG3v8YFOPQqo/F9jVWGpN4nQUn/vc5yQFPkH7eBeHTbUBTxhF7RCAi8Dcvvgs8WPiO8DGntgSAB/iWyf+yXWjWKF8WyePz5v3uhU7OaMmIdEbAhkudhj7+/s/8zM/c/369d/7vS8QH4c9CmBP3ZZaQj7Cc8fr56Kqs7jodZUUOA9msBYkaAwAFGGQS/lbuO5rgmAIWMr2DMDbo7j3KEchQCxjlykD4vfUKEqQTZYM+PjenYvv/RX19hn1Nt7G23gbb+NtvI238Tbextt4G9/r+P8BacpxgNgMCtUAAAAASUVORK5CYII=\n", 96 | "text/plain": [ 97 | "" 98 | ] 99 | }, 100 | "execution_count": 4, 101 | "metadata": {}, 102 | "output_type": "execute_result" 103 | } 104 | ], 105 | "source": [ 106 | "# Fixed input for debugging\n", 107 | "fixed_x, _ = next(iter(dataloader))\n", 108 | "save_image(fixed_x, 'real_image.png')\n", 109 | "\n", 110 | "Image('real_image.png')" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 5, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "class Flatten(nn.Module):\n", 120 | " def forward(self, input):\n", 121 | " return input.view(input.size(0), -1)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 6, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "class UnFlatten(nn.Module):\n", 131 | " def forward(self, input, size=1024):\n", 132 | " return input.view(input.size(0), size, 1, 1)" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 7, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "class VAE(nn.Module):\n", 142 | " def __init__(self, image_channels=3, h_dim=1024, z_dim=32):\n", 143 | " super(VAE, self).__init__()\n", 144 | " self.encoder = nn.Sequential(\n", 145 | " nn.Conv2d(image_channels, 32, kernel_size=4, stride=2),\n", 146 | " nn.ReLU(),\n", 147 | " nn.Conv2d(32, 64, kernel_size=4, stride=2),\n", 148 | " nn.ReLU(),\n", 149 | " nn.Conv2d(64, 128, kernel_size=4, stride=2),\n", 150 | " nn.ReLU(),\n", 151 | " nn.Conv2d(128, 256, kernel_size=4, stride=2),\n", 152 | " nn.ReLU(),\n", 153 | " Flatten()\n", 154 | " )\n", 155 | " \n", 156 | " self.fc1 = nn.Linear(h_dim, z_dim)\n", 157 | " self.fc2 = nn.Linear(h_dim, z_dim)\n", 158 | " self.fc3 = nn.Linear(z_dim, h_dim)\n", 159 | " \n", 160 | " self.decoder = nn.Sequential(\n", 161 | " UnFlatten(),\n", 162 | " nn.ConvTranspose2d(h_dim, 128, kernel_size=5, stride=2),\n", 163 | " nn.ReLU(),\n", 164 | " nn.ConvTranspose2d(128, 64, kernel_size=5, stride=2),\n", 165 | " nn.ReLU(),\n", 166 | " nn.ConvTranspose2d(64, 32, kernel_size=6, stride=2),\n", 167 | " nn.ReLU(),\n", 168 | " nn.ConvTranspose2d(32, image_channels, kernel_size=6, stride=2),\n", 169 | " nn.Sigmoid(),\n", 170 | " )\n", 171 | " \n", 172 | " def reparameterize(self, mu, logvar):\n", 173 | " std = logvar.mul(0.5).exp_()\n", 174 | " # return torch.normal(mu, std)\n", 175 | " esp = torch.randn(*mu.size())\n", 176 | " z = mu + std * esp\n", 177 | " return z\n", 178 | " \n", 179 | " def bottleneck(self, h):\n", 180 | " mu, logvar = self.fc1(h), self.fc2(h)\n", 181 | " z = self.reparameterize(mu, logvar)\n", 182 | " return z, mu, logvar\n", 183 | "\n", 184 | " def encode(self, x):\n", 185 | " h = self.encoder(x)\n", 186 | " z, mu, logvar = self.bottleneck(h)\n", 187 | " return z, mu, logvar\n", 188 | "\n", 189 | " def decode(self, z):\n", 190 | " z = self.fc3(z)\n", 191 | " z = self.decoder(z)\n", 192 | " return z\n", 193 | "\n", 194 | " def forward(self, x):\n", 195 | " z, mu, logvar = self.encode(x)\n", 196 | " z = self.decode(z)\n", 197 | " return z, mu, logvar" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 8, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "image_channels = fixed_x.size(1)" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 12, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "model = VAE(image_channels=image_channels).to(device)\n", 216 | "model.load_state_dict(torch.load('vae.torch', map_location='cpu'))" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 13, 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "optimizer = torch.optim.Adam(vae.parameters(), lr=1e-3) " 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 14, 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [ 234 | "def loss_fn(recon_x, x, mu, logvar):\n", 235 | " BCE = F.binary_cross_entropy(recon_x, x, size_average=False)\n", 236 | " # BCE = F.mse_loss(recon_x, x, size_average=False)\n", 237 | "\n", 238 | " # see Appendix B from VAE paper:\n", 239 | " # Kingma and Welling. Auto-Encoding Variational Bayes. ICLR, 2014\n", 240 | " # 0.5 * sum(1 + log(sigma^2) - mu^2 - sigma^2)\n", 241 | " KLD = -0.5 * torch.mean(1 + logvar - mu.pow(2) - logvar.exp())\n", 242 | "\n", 243 | " return BCE + KLD, BCE, KLD" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 14, 249 | "metadata": {}, 250 | "outputs": [], 251 | "source": [ 252 | "!rm -rfr reconstructed\n", 253 | "!mkdir reconstructed" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 12, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "epochs = 50" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 27, 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [ 271 | "for epoch in range(epochs):\n", 272 | " for idx, (images, _) in enumerate(dataloader):\n", 273 | " recon_images, mu, logvar = vae(images)\n", 274 | " loss, bce, kld = loss_fn(recon_images, images, mu, logvar)\n", 275 | " optimizer.zero_grad()\n", 276 | " loss.backward()\n", 277 | " optimizer.step()\n", 278 | "\n", 279 | " to_print = \"Epoch[{}/{}] Loss: {:.3f} {:.3f} {:.3f}\".format(epoch+1, \n", 280 | " epochs, loss.data[0]/bs, bce.data[0]/bs, kld.data[0]/bs)\n", 281 | " print(to_print)\n", 282 | "\n", 283 | "# notify to android when finished training\n", 284 | "notify(to_print, priority=1)\n", 285 | "\n", 286 | "torch.save(vae.state_dict(), 'vae.torch')" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 15, 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "def compare(x):\n", 296 | " recon_x, _, _ = vae(x)\n", 297 | " return torch.cat([x, recon_x])" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": 22, 303 | "metadata": {}, 304 | "outputs": [ 305 | { 306 | "data": { 307 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIYAAABECAIAAADLO9p5AAAbsklEQVR4nO2cW4xl2Vnff/+1zjl1756eHnua8WWYeIwAc0tIbGwHKZgHEBAR8mDlOVIQD0SKFImHvCRSXvOaB3iIIgtFeUwkhIQjYZPY4NjjC8Y2AhuD4xlmTE9P91R33c7Z6/vnYa21z67q6q7qdtskkpdKVaf22Xvdvtv/u6wN32vfa99rD2+qf371pV+98FZL+dXk/xncsQFIKeWcxxsiot1pj59Tyv5h+72FmR5vit6WrpBkIGofBqH6xwJ+822/CfzqJ/8Fc7AImEOBAMEAwBE6Qa9LR3BbLO3XABSQZEPACcxQ3ZglzrCALXnXXMXXzZ64YbZgGxIYDGEEKwBK39eEE/K4e6jOvE5fWEimCBkl7N/68d8CZo+yN5QbofeJPzR3QUopjV+ONJh+lkhJ+lr2rob3DOTHoYpOzG0iSULZCEtKQkGq62ndegtla8CD6OtHbZVagOAK3jTFuiMJEgiM6vYlKrvVr6RKKnPPFHEMT9ubxrCAGTaKxiKNvSs9ZSY0GDew3tCni5HqBOR+9VFIUhkynic7xScjH8/6VmCvR57SJqUsiWJ9MSWleE+QHp0qpbLeegzLbX1tJeM3oth9rkp9szIW5MrI5lBchRXagZM+n8BFCLLJWCIg4bpzR7jAMZrBLSyxBTOrdhvUGTlBYi0LfXewkI3ar1S5qE7Otqab8ihS0kcpz1sr6SVzLMD2SJLp55TSWoxOpM+nbJX3lMeTlWnTerVn5ueqxxxWHVlNh5AgC9mWbC9hRzwlHeJjGBA4WaH1PmWBVfBJ14EnuGrCsBeQzQJkIY9SkoQ7/3j8cZten7SbYAWNTutFPBpJAGEELwL2S3CU7TJ+O1FZp9UahRV8QVKKH5Zy8B1oChFYFlAa2wqcQHgeLKTNiG1pF56BXce+dBsOxU2TRLjxbhIz21Ll6IwSrMQBvIECH9jH8nNOW3I0+ZABk6Bqs+gk6WxkNXmqvCKpcVXicRTX6dUzvFgkpU8HR62zKT1yztJ915eJz4Vm8IPuauWJtmoA3A2J+k4ESAzVQkgZtvAmBGyZDPtwG1G5q1oDAyRjQCqNrqzQm2KF35AI9vCsrhjLmCqmU7tRVV/HDK7fV4G1q4w1e1bb45IEEPGuSJH9ktORImKqskZ6TM1M8eAT+6Ugw7tPa9An0ZxNqPJp3QxJdR86OhPZZKlu3a5HgeCWOBQzWELCoQqonKu0CWADVriYY+nIkLgSPpJ3zB7MRHDK1lfg0NFhlwNhO0yWqi3U+gm+LZKApPLuUCE+QxyvL44q61wzo8OsT0cmr95dnixVhBhsmh5wk5JOFag7RGqs6jlsdaVxC2VTYBCCeRevaocGkd0AHmBYwR3z10nFPCM2THaDuu62Yr32ySxT/yfsUZX4CZGkboN/wFomPm+dJOSpp3I+EkvOyxmfJaUU7yp6gmQJqrWs8KfqE5ouUVMhAw2kSSRY4E1rSxzbr0OgVVd07uSsJJ2JQBKDSbAJSDdhJQZ7S2xI2S5qzlAfk3G3G3JoKm5tSGD9yBMgCZAVPxIIf865nK+ypsg4V7V2nPJnSKa8GGiiSr+dZshNEqw+AzXz2gaoi69UCixpbjJ+xmTpBJZwF4AZHpyKLGtEUCiJqFiroCNsK8nXzFOOhWUoZ8zJuBHNz+n/TYR40p4ESYAs3uOM9EW0qoJ7ITIOjuBz0kaKd3rNUd9OW2BgEMHExLdP7d+Y8Gql1iAb7cAcBlFwQYc4iSKnap5T7dOFSAKU8IDDDOIWegWu4ZyqlzE6ik3GNCoxqQ3eIdlIjycpJbVllfcUkfljaYgpPR6EjG3rHulTStbw/BOAxc4oMJY6ZupuxmlO9PR3cy1nkM0zsESH8hIP9AdFcYuU1GhNxhly9VHEkXUT/41YmA2Yt3hCZU08elI2TX4x9kQ7TLjxyZEEyPIP2SfBlxlh3Uibc5GxgX3KHxWS/A53JPSYTbOmW0aHYB1iqsZWNDVe/6k+WkDl+iyuwQwtYGFeRUVaEdE98wwzWNkVbVeIVYML++IvnQI/i5+ih0num6KncFfrfyYewRMlCXhRyk8YlL+cKY7whcg4HOyLT0WezeO51bc1uixVFw+FFBAm1N3lKrP9nxE7dY4mzIJ4CubGUuBj9AY66r5FEdmyXYWjMryCEMfoJiSYiy17o0XJ+szWOz5xU2gQ7VwQrL0UESlw9zmbaSyPpuHlxBx+wrb5slzOCa6cj4zvKD4Z/hB6+hGGOzt6rj5xDe5CdLvSncdxKXIXmYbQhExAETP8tNMcI92GldOhXFIkhB14mZSpfr4zOFUzqHu4iLnZQ9db7Av1wKhbLHichlKPvkiTGFEniXcRyRVFWrgv4559ePktqWAcLyL+Lsbpi6l2ecaEjJ/XSMykN9HnZ+WDweZj2nmPIT+tESeG0oFntyuTATxx4nAYiE08twa0hQ/RXetui/jKwraljKtbWjF/NkfSAb5j3cVXe2S++kAtBmpGPJgqJfq+TcSoS8mI2Djte6538uFNMXPeVQt4Ro6wPoCL+bJaPLhtwPnIOKWUUtbLxJ/hHy1cdtzTkxhDjXQvjx7JKKOs9O1vyKe50BNsKsneRG/FO0qLpKXza8RtxWCnKhnVnIjo6Nh4wyylA/mm2LKum4UqBFCi7XGFC0kkmE0yLt2k8XBb0gDDOUbqnHuV7S21AD5JSIR+TrGI/KW5wg9Hxs3BDM2+kst1+23xmGa+5bQQVGjUnJW6oLZ/UzfIjXfr09WbsSR7G2+ajRJSfC301eSbcnHTJiuRItnRuUczsL1C9+B1e9veqCjTXWNJySByDwo0Kbl8jMvY6ZLGpMO+ypNN5rzj9KEURPpSijAXI+PQkfzH4rq9lQTM0aZsK3C4QSmaAacJfZ9hZcbRG6g8mLpdaV579d40uirNytYNmpmZFHaguT2XF9ZCw1OposV0U4EIyzgRKWGUccGzisfgltOxoiS/GDzd42MtDlBTMn16bf4TBXgh4rIvz6zn3qjYcfpQKSrpj/OYrXlQzLja3XyT8lpKL4RJ3gh2M8349q1svcj2mCMB7O4pj9w3ZrHa6tUD6Kw5051Ps5k35q39M3PM7L0Q8P0p30kcJA3EvBuG8Clmn8NgHeMDUdDT4orbHtegcprgq/Epn2dLHrTH6dIOnNLaQz113fI2+hnKifOfZtvEOr8yRcZUnYFV0Dcdz0vJWsfwT9k5gNxU1Cn/dwpeToPL9r0nAYwpv2mi4rMQFKsgEQltkd7u/GaZ37Jen61EZK2O7RQencdwNRs+RkK38deJ3cR1s+0qK+vU8vjBp9bHBbbEVUoe33cbW+xYP+uhrNKf5dGE5JzPILGu161XE/cKVx5t6OoPO7nRaaqpm9KqQZQzdOuGhJYzXzvfBUiSPU/DdfK72bxdNg90vLkYNvIRZbmKMKxMUp455qJAhoxX8Kr0VrxptlDuWvR+KTndLoFsdClJcYp48AbK8tV429vf+Ds3X1ZgdMZTmaZbQD5M6a+zkdP9gvfgUUTNkPT4xZkFq223UPvTA71Ww2PjRaGZlLocirLJ8kbyT2jnhZMb89XVnBbKqSSKZBFEJFb1EblatTfRK1aBhTyHeaNW+5l3icxqcQHgYpJcekMujLHLev4vvvXh//XJH3n5GzMp53M8x7GlYn9zDKletlWxPtWZp0vohJKtaiQ9/rhp9HG6laM1Og/Iq21uf//s9vvz5rPLtx4eXVmWec55lhvvlxph69YbtIJ9OJJl5Y56R7hVR2mFFJO1P3SJKHRZ3+SUB3re9+ILv/Suv/zZGz+3OnnnszdajcZ59LAjHLE/aBXK5/X14CHqTM7RtVU+jCxZE20sjT+euGfCVQcmKZGInFBOh1vcevfG/o9t7Z0cPHvz7tZS2WYOMyEiYAWlCZYDjuTjHvzMnR6dKlWk11eASymuR9uPBzbD4VMbv/Nv3vfaf/jnv/grv7y3uek4GzCOiFJKhMPWMnl4tCmMispTqqw/u0WKWiFQH1p28tpROP3jHr514AhUVpt+/bl094WtjaRds3BqzCtLxiaTFtbCDeYem6UpDX9PDYl791M2eiBJrNEJuMReNFVwke6Cg2uL373+lfQ//uuPfeVPTVRKlFKqLTklMSu84pES9N2W9CqQM5N3LVnouMtqIKAV6p323Wpn7tIDmFkpcztFDHl58x1x8I6dncK8DABBS6M3fC6nhMUSz2i1mqqSMSIutQmrg0ngSUmJqtK9BP0Mt1a3vrTz2jt+6v0JiuN+xdX6LEordDlw0To3hB0TGxHTyp3Kj2Y0G9WRmboy6+HHbLFbyCMqB3mA1bZPbti7w1bEZoVYY0639jNAGGoBWKulW0twn67bQGuvlYeQpGr6RwgDV7G9uFmhuz/ywu7zb8spp8ok54mCw15xqsLpwimUXnhYKVF6SVxUVbbGWpMh+8epVFVvtJocS4Giki85FIWVVHZjuBKhE89XqBLeDruYAVZm6PH8Y1p0YB0E7mPXX+qDAhcGVC7bhKTLEVChcuW/f/Tko/v6hz+dZ7P7/cvOti7FzrV651LNNRdbQ+4xEZGwQmPAzuOvcbRa5xATWsqjIzqpfEWi4KMZd66y8dyw9+cSgTNh1cBMgVpRmRvq1QqK+9Otn+msxz2srZNkLMab3CindXzywu3QfW7AA1pe+elvvvnalb0hJ6H7n6ls67nTluzVJbuFXjFdp1+0ZvyoecZW6Fa32qdkgsbppYtly7i0CdaUCrZMQiEfz2f7u6n45LAEAxhH6qAWK7Wg59xsIuHoVmQy33EyExYZSXKCJE/qaGVFNJt0ic24vHYhUvr4P/vR8qlFHPQ44bldLii7Q76cNmxt6CwkuaU3DKpZkD7RnjrpfnvbD7tRbvwBkk9TzjYpzAzE0XaaXd1gf9WqL9SQrit7Bs5mA2/bs24ITqnoSdf3x7j8RgcjguSmdh/ijt/X3LijyudZmZs2Rfqb77sy20jca/l3deSzvgd7U2ycXcQFrdqSWqHrCfu7dt8osU44dIpUQjURWUeOx7Wt5yXDssyK5odldtdHsTheJA2DQ57VquJax2UCHcsLmItMI7lOa62REpMRZ2e/qxvScfNlOdToEOVeQJ7OytykY+yko+C4XXA3Hg2ANBKkslu0eMQA2+gIt8SSJrmQ8RJnysY6QTCi1OqWvncx6Xn8UFIMLsfaXBYdLFfOA1CshGtqJKFkZ5FQNhkcjUGnlNaEaSbtgeb9EdQFEPgenijE7gBAPaTTgtJOSTGUfI92EOL+cV13p2jPZHfn4YEyd+rZDMKlDtXLFbvEqqOdZksYdVYvZq/WZ6y/nFaZTBydsErBh3l5qO2T1SINq/mcWT0lVpPKVC/RCTZwcq1fdVJDg1OIdd8WPOEKlVPcVFupMtfK6drK9xWrijTPqiz67sTV5Ih0/CCZOyeA0xZcRBrxu1udUPdKJoa0yUpLvk/tfVgD1LM5VDXaUJwxEVoShzo+UNIs0sBGqx5aE7vqz2w26uMoaADodLbhfsI8aZI8sPVjjEKWbzd9P63yGsljUMZXQit8x9SI+lrm1M4VKJrXPaWoaslJzxRFK+wYk7v9eMHEumtyvT5V8CCpJ/NHNqiQIayVtO94Iw5neHOuaBX70U+WpGNYoU28ixeVKlKsy8bGMIHpxxgvk3v/TjQZl9CbZw+XTKMpkmKRvE1X8vfLHKMO9ikZc4MZo7Za28NeddiKIce71tWIjWzuwrN2+9XuaSSBpbWUV/aGmMFJrQyuGcYqB3bGG/K2Na8WyE60A1N1xlN6T6zbd5skIJakwx43Og9AhEObXLJ0qPM8wDrCKwhhqx7JqmrH3fx3+9F76Kxa7U5nghZpX1dEjp6XVeBIrITXyLv5JfU0RcVdG7AHgiXeqAfDuvgCHW6esfDffZKQjsknybNuLXs8eC0lwNWiWbZCl7DqY6tS41F+piT3qK26m+gp7qp+4PpjhwVT4GmpQ8pa3risHoxbRr2d7tU4lub2Zs//t5Jit0q8ei55pM0El373SWLdUz2IXmdUy4Xq3jXyGF+tSfdHgsATC+5J0RTr0qcOs9ZHc6oRWRNqtPxr8FgDXaNKxIYV6bbZVwWTraarAQUR0mDn8FW4ZhZGrUy5j1BPiHUtp79tKdGdROGMx3GKPMlxLczwyIHqpphG4KNTuLw5iniUibULs4bI44w8VrS0iz34MaATOBJLexuyVJotaUe5Jcts4qv2HtpQP/NYu2ky2rMIOuOZfNdJYrhTHLPGsff55gJmeM9jkOIRmkaO8/p8oKdfd3gzsUH91Od4tqB9r+ZDqiE3Rjgw9tmrfAVqtsRh2SzENuzBwpbVfZFTR1vbk13L/q0prkHaT4y8MvHY1U6FKLaKt+mq5RF1V6Ja9dHn7OcXTsnHFBavbcZpH6Fd90iMkRAmiRleNDsxFnkyxn8W+AraqG816MXvaxU6EUmf+o/vAkmsNFsNgWOeZbG0D6Z2dZzoSJ7ENmw8Fj1yzTdprG2c4MtR5ia+utc+zTpr0uZEOJqsyaRUUspRtDRO69LIKm21Qn4sji9ogLktWJ0CuEQvmNck5OxT93zHSSKXu5+/+eN/8trtn3ru1g9d98HMx+UhKRDbsTfwKFUQk8FQhTxVTfeQL6zBb79vDcXW6m5M+UywGYaQ7p1s37ybxPH2LltbNUDArB7PTaxArmceGxyYOXbMwg5U1nqObtJosdlT4fnanlw5xPlNRFoty1OvHh382T0N5m6UZZRSxnz7fU+UdHXyhp5HbV3d0NITLTmojpw69Oq5d0lJ6sVUSi3LqQQzJanMTCn55Tvbb7y5c7DafOXN7btOB9ZhdT7SCGFrRYUHSPYW7IkNjSdP67Dr+fSJrOHWd0txOZfhg//njdib3/rJGzuzGXdijPmvz/uMtkQopbhaHlll1aZmS0AK91BjP9PT7pj4IjL5XDVSNVKyLed0Msy/evj+P/h6efbKS+96NubLgYVXaxdPuUU8q5IjiV1zzWzCbER23R8aqxTGQtLTa10XDzXYIFuP7A88sJmTz/7ND/+Xr2wfHO+9bRMX7px3lz0WDXkW3nlcEalbk3oNI5M1j5WQrbBRLQWfeqVhZv1hJmZoRsqerQZOVunWaudbR3v3jvNVHac03MNHsMLYCddXRsmEOYK79RyFRnPefJ91mUEXjVGAJnVc/cjP04lwsh3Ysh2rSMffrlqzefZLt4bwF95ytaSkpef3cmTZ95UItQeITbw15isecbiJLemqevQ4RlePzs81WyWN21Hplxs6CpzCKo63LO794OaX/PTGD91Yvv1aLBYpgpUo1gwCjpDrAVTYFIINNLP72yRGM2ZN8jGnJXZsXXHNx3BpU7s+TD45dXL+gU2w6G5aFc1o7zNS5vifvOsLb9m4/c6n5wpO4FgpVa5owjG2tnG7aPGYQlod/jGn2U4suhsWr6Mzra40TcoP6XukRqFsSIntlBJ+7/e9/vxs2NzZ39zN+xEHpCQVVfmQYLDbmz5ENlHf9OUqmM3jkVWL86c1fHCG/Z6ALXFSupLIUQd1P9CnsM3R7s7XX/iBeSEg3UJLRY/5TcuFRsKUa4V8Nk582ZnUX92ao24Vek6k2Y/+whuSqYqrV/GeHbY7DXp2fuvGc7ls5/2lD1dedIw7dMKjFsWquEu49OjAGQa7vwrq9NAPJImivYzk4qaY0rwJKLD2hlVnkv5CLh4DTaf6UK+IvqZBRYbUXnGnUPfr3GsV1w+GRl+LpL4vwjnavkzvri6j6OKwrsftYWBgXTmCx/SGwGV1iKUhsbJzeAMPlhNFZKKEkFZqp1gzhFuhBW4pS5la7zZVXKcdsCcgJa3W+DJ8fYsopqWpzommMLOvBDihmCtdSWhy2irqsQHXA84KOapcjv521AxZq+Zg1BhdguQuKpUs1dZM1HOMV8cu6Z9a5YoKyrCNc68WG3Du5qSdWJBSjLak71QPyLMOQzeunbzJ78G594j7eflB916GHh6IN6h73E4STOsflIRZoK3KV8mpaYD7TluNvgXtt/vx0FI7bb55zcu6EaKmrnrhstsbHtr0xwA7YJNaoNCGVEsd6/skxejyW1rhIs/QEM7yoJStIGaKk5L37asdH/UTgc2DSXVYNyhitzIMYCTJ3W/ePbuD99avo3tok+boUH6Qc1ff42JzRPpG1v75px/bbmSX2/ahRdKmNZnUGKV6SLv71/sMsp0CTiCIebJLbmBsNuqmhMgUhWakWWqxW9fgQVWyVn2tjaBQ36nQ3kP0utgnH4oDvBQzc2TssoKZ85ApUU6CHZQTO47UdFcDeN28VxKF7ITCeaxyrH/23rF3dn2XLpsHHhL/yEqIEkHAvYuqJzNsgUjKI/KpzQ3PnT+nylI7b9shWhkqbi8yqa4bqh6qc2olllFPCUspYzsKk6ORooIoWSmlWg2cKUPp71HCR7W/mlSQMCEHOREQydog56yMc60HCEKmFhcjS3O5nqVRSkZJd/7yTR7TSb50E8op5efmqzsnce+yFN6Tfinn3cmVE+mjjpvFxU/ghUSP0STlZ1LZD59cdhW78Iuz2TWzSAr0OtxO/NFytX8RZlrbkt3d3VLKYrEYhqEetF0ul8fHx2ef0HiqK7hog6q46gfDfw73LrkWrtv/fijPT67cTvolLW5y8Utvrj11bRiGvDGjgEP27f39yw784CbJPyC+AieXfeQp+LfD8G6kUt+Fp6/CL4u7F2mfRpL5fP6BD3xgPp9LeuWVV27cuHHlypWvfe1rn/3sZ+uUxrlt6PuznsEauLn0X12mjvsxwjNpNNkAZOsyr3ze2tr+Bz/593Z2njoo+4lZGZY+OP79T/5hPyisyUsJ6gH3wev634evAYZHC4WK8Q2TFVaQ2onIC1ojSRWL7e3tg4ODvb295XI5m82effbZD3/4w6++9tonPvFp6QW8AYc3+I2r/oXkfEu//TK/ceEAwvnmXEfjO8Yubkfo4zm/dQKRD9C9VGRfZOArKl4lZUWUQcdD2d7d/scf/Jnbb9z+vc/+SfLfhxkMz/CPFrxl4PUjPn8n/tvFVBHpdWJ1yQMbAMfw0Tz7MmxIM6W7Tp/JsSorDfHwLtqyJb344ovDMOzs7BwcHNje2dm5evXqRz7ykY997OO/9mv/WvzH4AN4+Q6eue49mVvpP30jfv2ijZaklBWlXLq+GKHNdFYqBmJ1XlTsTHv66adLiTxLNsNqAF+7fv33fud3P/b7v/8v/9W/g/8cvFfcec7PXGcRcEe//XL8urngVKRQSopz43IPfIRNzRDGMyVbg2IZ5cIeHqgOJO3u7v78z//8t771rU984iX4afQ8sAMbFpQlf343PuHLSP2TaI8ThgRgZ2f7n/7KL7zy8msf+4OXxPusF6Sy5diyxOpE39iP//24fX9H2qU8D5RzrzTrrYYLhv+nFnNRy+nsKmailsL8f7SK77Xvte+1/wtUTc52v/doPQAAAABJRU5ErkJggg==\n", 308 | "text/plain": [ 309 | "" 310 | ] 311 | }, 312 | "metadata": { 313 | "image/png": { 314 | "unconfined": true, 315 | "width": 700 316 | } 317 | }, 318 | "output_type": "display_data" 319 | } 320 | ], 321 | "source": [ 322 | "# sample = torch.randn(bs, 1024)\n", 323 | "# compare_x = vae.decoder(sample)\n", 324 | "\n", 325 | "# fixed_x, _ = next(iter(dataloader))\n", 326 | "# fixed_x = fixed_x[:8]\n", 327 | "fixed_x = dataset[randint(1, 100)][0].unsqueeze(0)\n", 328 | "compare_x = compare(fixed_x)\n", 329 | "\n", 330 | "save_image(compare_x.data.cpu(), 'sample_image.png')\n", 331 | "display(Image('sample_image.png', width=700, unconfined=True))" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [] 340 | } 341 | ], 342 | "metadata": { 343 | "kernelspec": { 344 | "display_name": "Python 3", 345 | "language": "python", 346 | "name": "python3" 347 | }, 348 | "language_info": { 349 | "codemirror_mode": { 350 | "name": "ipython", 351 | "version": 3 352 | }, 353 | "file_extension": ".py", 354 | "mimetype": "text/x-python", 355 | "name": "python", 356 | "nbconvert_exporter": "python", 357 | "pygments_lexer": "ipython3", 358 | "version": "3.6.4" 359 | } 360 | }, 361 | "nbformat": 4, 362 | "nbformat_minor": 2 363 | } 364 | -------------------------------------------------------------------------------- /vae.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from torch.autograd import Variable 5 | 6 | class Flatten(nn.Module): 7 | def forward(self, input): 8 | return input.view(input.size(0), -1) 9 | 10 | 11 | class UnFlatten(nn.Module): 12 | def forward(self, input, size=1024): 13 | return input.view(input.size(0), size, 1, 1) 14 | 15 | class VAE(nn.Module): 16 | def __init__(self, image_channels=3, h_dim=1024, z_dim=32): 17 | super(VAE, self).__init__() 18 | self.encoder = nn.Sequential( 19 | nn.Conv2d(image_channels, 32, kernel_size=4, stride=2), 20 | nn.ReLU(), 21 | nn.Conv2d(32, 64, kernel_size=4, stride=2), 22 | nn.ReLU(), 23 | nn.Conv2d(64, 128, kernel_size=4, stride=2), 24 | nn.ReLU(), 25 | nn.Conv2d(128, 256, kernel_size=4, stride=2), 26 | nn.ReLU(), 27 | Flatten() 28 | ) 29 | 30 | self.fc1 = nn.Linear(h_dim, z_dim) 31 | self.fc2 = nn.Linear(h_dim, z_dim) 32 | self.fc3 = nn.Linear(z_dim, h_dim) 33 | 34 | self.decoder = nn.Sequential( 35 | UnFlatten(), 36 | nn.ConvTranspose2d(h_dim, 128, kernel_size=5, stride=2), 37 | nn.ReLU(), 38 | nn.ConvTranspose2d(128, 64, kernel_size=5, stride=2), 39 | nn.ReLU(), 40 | nn.ConvTranspose2d(64, 32, kernel_size=6, stride=2), 41 | nn.ReLU(), 42 | nn.ConvTranspose2d(32, image_channels, kernel_size=6, stride=2), 43 | nn.Sigmoid(), 44 | ) 45 | 46 | def reparameterize(self, mu, logvar): 47 | std = logvar.mul(0.5).exp_() 48 | # return torch.normal(mu, std) 49 | esp = torch.randn(*mu.size()) 50 | z = mu + std * esp 51 | return z 52 | 53 | def bottleneck(self, h): 54 | mu, logvar = self.fc1(h), self.fc2(h) 55 | z = self.reparameterize(mu, logvar) 56 | return z, mu, logvar 57 | 58 | def representation(self, x): 59 | return self.bottleneck(self.encoder(x))[0] 60 | 61 | def forward(self, x): 62 | h = self.encoder(x) 63 | z, mu, logvar = self.bottleneck(h) 64 | z = self.fc3(z) 65 | return self.decoder(z), mu, logvar 66 | 67 | --------------------------------------------------------------------------------