age_cache_simulation/00_aoi_caching_simulation/06-multi_aoi_simulation.ipynb
2024-12-13 15:57:34 +01:00

1403 lines
180 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "71f85f2a-423f-44d2-b80d-da9ac8d3961a",
"metadata": {},
"outputs": [],
"source": [
"import simpy\n",
"import random\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import pandas as pd\n",
"from enum import Enum\n",
"import os\n",
"import shutil\n",
"from tqdm import tqdm\n",
"import math\n",
"from dataclasses import dataclass, field\n",
"from typing import List, Union, Dict\n",
"\n",
"# Constants\n",
"SEED = 42\n",
"ACCESS_COUNT_LIMIT = 1000 # Total time to run the simulation\n",
"EXPERIMENT_BASE_DIR = \"./experiments/\"\n",
"TEMP_BASE_DIR = \"./.aoi_cache/\"\n",
"\n",
"ZIPF_CONSTANT = 2 # Shape parameter for the Zipf distribution (controls skewness) Needs to be: 1< \n",
"\n",
"# Set random seeds\n",
"random.seed(SEED)\n",
"np.random.seed(SEED)\n",
"\n",
"os.makedirs(TEMP_BASE_DIR, exist_ok=True)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "d88effd8-d92b-47d1-9e15-527166073e81",
"metadata": {},
"outputs": [],
"source": [
"# Types of cache\n",
"class EvictionStrategy(Enum):\n",
" LRU = 1\n",
" RANDOM_EVICTION = 2\n",
" TTL = 3"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1d6a3c67-f9a5-4d9c-8ade-e1ca6944867c",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class DatabaseObject:\n",
" id: int\n",
" data: str\n",
" lambda_value: int\n",
" mu_value: Union[float, None]\n",
" ttl: Union[float, None]"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "f40af914-a6c3-4e44-b7de-b3b40a743fb2",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class CacheObject:\n",
" id: int # id of object\n",
" data: DatabaseObject # body of object\n",
" initial_fetch_timer: float # time at which the object was initially pulled into the cache (object_start_time)\n",
" age_timer: float # time at which the object was last pulled into the cache (initial fetch)\n",
" last_access: float # time at which the object was last accesse\n",
" next_refresh: Union[float, None] # scheduled time for the object to be requested (for refresh cache)\n",
" next_expiry: Union[float, None] # scheduled time for the object to be evicted (for ttl cache) (ttl)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "ad6c68a4-2ebe-40fc-a391-5a4724e07bdf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"EvictionStrategy.LRU\n"
]
}
],
"source": [
"print(EvictionStrategy.LRU)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "00a944e4-842b-49ba-bb36-587d9c12fdf4",
"metadata": {},
"outputs": [],
"source": [
"# Base class for all cache types\n",
"@dataclass\n",
"class SimulationConfig:\n",
" db_objects: Union[int, List[DatabaseObject]]\n",
" cache_size: int\n",
" eviction_strategy: EvictionStrategy\n",
"\n",
" def __post_init__(self):\n",
" if not hasattr(self, 'eviction_strategy') or self.eviction_strategy is None:\n",
" raise ValueError(\"Eviction strategy must be defined in subclasses.\")\n",
"\n",
" def __repr__(self):\n",
" db_object_count = self.db_objects if isinstance(self.db_objects, int) else len(self.db_objects)\n",
" return f\"[{self.__class__.__name__}] Database Object Count: {db_object_count}, Cache Size: {self.cache_size}, Eviction Strategy: {self.eviction_strategy}\"\n",
" \n",
" def generate_objects(self):\n",
" if isinstance(self.db_objects, int):\n",
" self.db_objects = [\n",
" DatabaseObject(id=i, data=f\"Generated Object {i}\", lambda_value=np.random.zipf(ZIPF_CONSTANT), mu_value=None, ttl=None) \n",
" for i in range(self.db_objects)\n",
" ]\n",
"\n",
" def from_file(self, path: str, lambda_column_name: str):\n",
" df = pd.read_csv(path)\n",
" lambdas = df[lambda_column_name]\n",
"\n",
" self.db_objects = [\n",
" DatabaseObject(id=i, data=f\"Generated Object {i}\", lambda_value=lambdas[i], mu_value=None, ttl=None) \n",
" for i in range(self.db_objects)\n",
" ]\n",
" \n",
"# Specific cache type variants\n",
"@dataclass\n",
"class TTLSimulation(SimulationConfig):\n",
" eviction_strategy: EvictionStrategy = field(default=EvictionStrategy.TTL, init=False)\n",
"\n",
" def __repr__(self):\n",
" return super().__repr__().replace(super().__class__.__name__, self.__class__.__name__)\n",
" \n",
" def generate_objects(self, fixed_ttl):\n",
" if isinstance(self.db_objects, int):\n",
" self.db_objects = [\n",
" DatabaseObject(id=i, data=f\"Generated Object {i}\", lambda_value=np.random.zipf(ZIPF_CONSTANT), mu_value=None, ttl=fixed_ttl) \n",
" for i in range(self.db_objects)\n",
" ]\n",
"\n",
" \n",
" def from_file(self, path: str, lambda_column_name: str, ttl_column_name: str):\n",
" df = pd.read_csv(path)\n",
" lambdas = df[lambda_column_name]\n",
" ttls = df[ttl_column_name]\n",
"\n",
" self.db_objects = [\n",
" DatabaseObject(id=i, data=f\"Generated Object {i}\", lambda_value=lambdas[i], mu_value=None, ttl=ttls[i]) \n",
" for i in range(self.db_objects)\n",
" ]\n",
" \n",
"@dataclass\n",
"class LRUSimulation(SimulationConfig):\n",
" eviction_strategy: EvictionStrategy = field(default=EvictionStrategy.LRU, init=False)\n",
" \n",
" def __repr__(self):\n",
" return super().__repr__().replace(super().__class__.__name__, self.__class__.__name__)\n",
" \n",
"\n",
"@dataclass\n",
"class RandomEvictionSimulation(SimulationConfig):\n",
" eviction_strategy: EvictionStrategy = field(default=EvictionStrategy.RANDOM_EVICTION, init=False)\n",
"\n",
" \n",
" def __repr__(self):\n",
" return super().__repr__().replace(super().__class__.__name__, self.__class__.__name__)\n",
"\n",
"@dataclass\n",
"class RefreshSimulation(TTLSimulation):\n",
"\n",
" \n",
" def __repr__(self):\n",
" return super().__repr__().replace(super().__class__.__name__, self.__class__.__name__)\n",
" \n",
" def generate_objects(self, fixed_ttl, max_refresh_rate):\n",
" if isinstance(self.db_objects, int):\n",
" self.db_objects = [\n",
" DatabaseObject(id=i, data=f\"Generated Object {i}\", lambda_value=np.random.zipf(ZIPF_CONSTANT), mu_value=np.random.uniform(1, max_refresh_rate), ttl=fixed_ttl) \n",
" for i in range(self.db_objects)\n",
" ]\n",
" \n",
" def from_file(self, path: str, lambda_column_name: str, ttl_column_name: str, mu_column_name: str):\n",
" df = pd.read_csv(path)\n",
" lambdas = df[lambda_column_name]\n",
" ttls = df[ttl_column_name]\n",
" mus = df[mu_column_name]\n",
"\n",
" self.db_objects = [\n",
" DatabaseObject(id=i, data=f\"Generated Object {i}\", lambda_value=lambdas[i], mu_value=mus[i], ttl=ttls[i]) \n",
" for i in range(self.db_objects)\n",
" ]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "5cea042f-e9fc-4a1e-9750-de212ca70601",
"metadata": {},
"outputs": [],
"source": [
"class Database:\n",
" data: Dict[int, DatabaseObject]\n",
" \n",
" def __init__(self, data: List[DatabaseObject]):\n",
" self.data = {i: data[i] for i in range(len(data))}\n",
"\n",
" def get_object(self, obj_id):\n",
" # print(f\"[{env.now:.2f}] Database: Fetched {self.data.get(obj_id, 'Unknown')} for ID {obj_id}\")\n",
" return self.data.get(obj_id, None)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "499bf543-b2c6-4e4d-afcc-0a6665ce3ae1",
"metadata": {},
"outputs": [],
"source": [
"class Cache:\n",
" capacity: int\n",
" eviction_strategy: EvictionStrategy\n",
" cache_size_over_time: List[int] # To record cache state at each interval\n",
" storage: Dict[int, CacheObject]\n",
" hits: Dict[int, int] # hit counter for each object\n",
" misses: Dict[int, int] # miss counter for each object\n",
" access_count: Dict[int, int] # access counter for each object (should be hit+miss)\n",
" next_request: Dict[int, float] # scheduled time for each object to be requested\n",
" cumulative_age: Dict[int, List[float]] # list of ages of each object at the time it was requested (current time - age_timer)\n",
" cumulative_cache_time: Dict[int, List[float]] # list of total time of each object spent in cache when it was evicted (current time - initial fetch time)\n",
" request_log: Dict[int, List[float]] # list of timestamps when each object was requested\n",
" \n",
" def __init__(self, env, db, simulation_config):\n",
" self.env = env\n",
" self.db = db\n",
" self.capacity = simulation_config.cache_size\n",
" self.eviction_strategy = simulation_config.eviction_strategy\n",
" self.cache_size_over_time = []\n",
" self.storage = {}\n",
"\n",
" db_object_count = len(self.db.data)\n",
" \n",
" self.hits = {i: 0 for i in range(db_object_count)}\n",
" self.misses = {i: 0 for i in range(db_object_count)}\n",
" self.access_count = {i: 0 for i in range(db_object_count)}\n",
" self.next_request = {i: np.random.exponential(1/self.db.data[i].lambda_value) for i in range(len(self.db.data))}\n",
" self.cumulative_age = {i: [] for i in range(db_object_count)}\n",
" self.cumulative_cache_time = {i: [] for i in range(db_object_count)}\n",
" self.request_log = {i: [] for i in range(db_object_count)}\n",
"\n",
" \n",
" def get(self, obj_id):\n",
" assert len(self.storage) <= self.capacity, f\"Too many objects in cache ({len(self.storage)}).\"\n",
" # print(f\"[{self.env.now:.2f}] Requesting Object {obj_id}... (Cache Size: {len(self.storage)})\")\n",
"\n",
" # Schedule next request\n",
" next_request = self.env.now + np.random.exponential(1/self.db.data[obj_id].lambda_value)\n",
" self.request_log[obj_id].append(next_request)\n",
" self.next_request[obj_id] = next_request\n",
" self.access_count[obj_id] += 1\n",
" # print(f\"[{self.env.now:.2f}] Client: Schedule next request for {obj_id}@{next_request:.2f}\")\n",
" \n",
" if obj_id in self.storage:\n",
" # Cache hit: Refresh TTL if TTL-Cache\n",
" if self.storage[obj_id].next_expiry:\n",
" assert self.env.now <= self.storage[obj_id].next_expiry, f\"[{self.env.now:.2f}] Cache should never hit on an expired cache entry.\"\n",
" self.storage[obj_id].next_expiry = self.env.now + self.db.data[obj_id].ttl\n",
" \n",
" # Cache hit: increment hit count and update cumulative age\n",
" self.hits[obj_id] += 1\n",
" age = self.env.now - self.storage[obj_id].age_timer\n",
" self.cumulative_age[obj_id].append(age)\n",
" self.storage[obj_id].last_access = self.env.now\n",
"\n",
" assert len(self.cumulative_age[obj_id]) == self.access_count[obj_id], f\"[{self.env.now:.2f}] Age values collected and object access count do not match.\"\n",
" # print(f\"[{env.now:.2f}] {obj_id} Hit: Current Age {age:.2f} (Average: {sum(self.cumulative_age[obj_id])/len(self.cumulative_age[obj_id]):.2f}) \")\n",
" return self.storage[obj_id]\n",
" else:\n",
" # Cache miss: increment miss count\n",
" self.misses[obj_id] += 1\n",
" self.cumulative_age[obj_id].append(0)\n",
" \n",
" # Cache miss: Add TTL if TTL-Cache\n",
" # When full cache: If Non-TTL-Cache: Evict. If TTL-Cache: Don't add to Cache.\n",
" if len(self.storage) == self.capacity:\n",
" if self.eviction_strategy == EvictionStrategy.LRU:\n",
" self.evict_oldest()\n",
" elif self.eviction_strategy == EvictionStrategy.RANDOM_EVICTION:\n",
" self.evict_random()\n",
" elif self.eviction_strategy == EvictionStrategy.TTL:\n",
" # print(f\"[{self.env.now:.2f}] Cache: Capacity reached. Not accepting new request.\")\n",
" return\n",
"\n",
" # Cache miss: Construct CacheObject from Database Object\n",
" db_object = self.db.get_object(obj_id)\n",
" initial_fetch_timer=self.env.now\n",
" age_timer=self.env.now\n",
" last_access=self.env.now\n",
" next_refresh = (self.env.now + np.random.exponential(1/db_object.mu_value)) if db_object.mu_value is not None else None\n",
" next_expiry = (self.env.now + db_object.ttl) if db_object.ttl is not None else None\n",
" cache_object = CacheObject(id=obj_id, data=db_object, \n",
" initial_fetch_timer=initial_fetch_timer, age_timer=age_timer, \n",
" last_access=last_access,next_refresh=next_refresh, next_expiry=next_expiry\n",
" )\n",
" self.storage[obj_id] = cache_object\n",
" \n",
" assert len(self.cumulative_age[obj_id]) == self.access_count[obj_id], f\"[{self.env.now:.2f}] Age values collected and object access count do not match.\"\n",
" # print(f\"[{env.now:.2f}] {obj_id} Miss: Average Age {sum(self.cumulative_age[obj_id])/len(self.cumulative_age[obj_id]):.2f} \")\n",
" return self.storage[obj_id]\n",
"\n",
" def refresh_object(self, obj_id):\n",
" \"\"\"Refresh the object from the database to keep it up-to-date. TTL is increased on refresh.\"\"\"\n",
" assert obj_id in self.storage, f\"[{self.env.now:.2f}] Refreshed object has to be in cache\"\n",
" db_object = self.db.get_object(obj_id)\n",
" age_timer = self.env.now\n",
" next_refresh = self.env.now + np.random.exponential(1/db_object.mu_value)\n",
" # next_expiry = self.env.now + db_object.ttl if db_object.ttl is not None else None\n",
"\n",
" self.storage[obj_id].data = db_object\n",
" self.storage[obj_id].age_timer = age_timer\n",
" self.storage[obj_id].next_refresh = next_refresh\n",
"\n",
" # print(f\"[{self.env.now:.2f}] Cache: Refreshed object {obj_id}\")\n",
" \n",
" def evict_oldest(self):\n",
" \"\"\"Remove the oldest item from the cache to make space.\"\"\"\n",
" assert self.capacity == len(self.storage), f\"[{self.env.now:.2f}] Expecting cache to be at capacity\"\n",
" oldest_id = min(self.storage.items(), key=lambda item: item[1].last_access)[0]\n",
" \n",
" # print(f\"[{self.env.now:.2f}] Cache: Evicting oldest object {oldest_id}.\")\n",
" self.cumulative_cache_time[oldest_id].append(self.env.now - self.storage[oldest_id].initial_fetch_timer)\n",
" del self.storage[oldest_id]\n",
" \n",
" def evict_random(self):\n",
" \"\"\"Remove a random item from the cache to make space.\"\"\"\n",
" assert self.capacity == len(self.storage), f\"[{self.env.now:.2f}] Expecting cache to be at capacity\"\n",
" random_id = np.random.choice(list(self.storage.keys())) # Select a random key from the cache\n",
" \n",
" # print(f\"[{self.env.now:.2f}] Cache: Evicting random object {random_id}.\")\n",
" self.cumulative_cache_time[random_id].append(self.env.now - self.storage[random_id].initial_fetch_timer)\n",
" del self.storage[random_id]\n",
" \n",
" def check_expired(self, obj_id):\n",
" \"\"\"Remove object if its TTL expired.\"\"\"\n",
" assert self.storage, f\"[{self.env.now:.2f}] Expecting cache to be not empty\"\n",
" assert self.env.now >= self.storage[obj_id].next_expiry\n",
" \n",
" # print(f\"[{self.env.now:.2f}] Cache: Object {obj_id} expired\")\n",
" self.cumulative_cache_time[obj_id].append(self.env.now - self.storage[obj_id].initial_fetch_timer)\n",
" del self.storage[obj_id]\n",
"\n",
" \n",
" def record_cache_state(self):\n",
" \"\"\"Record the current cache state (number of objects in cache) over time.\"\"\"\n",
" self.cache_size_over_time.append((self.env.now, len(self.storage)))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "687f5634-8edf-4337-b42f-bbb292d47f0f",
"metadata": {},
"outputs": [],
"source": [
"def client_request_process(env, cache, event):\n",
" \"\"\"Client process that makes requests for objects from the cache.\"\"\"\n",
" last_print = 0\n",
" with tqdm(total=ACCESS_COUNT_LIMIT, desc=\"Progress\", leave=True) as pbar:\n",
" while True:\n",
" request_id, next_request = min(cache.next_request.items(), key=lambda x: x[1])\n",
" expiry_id = -1\n",
" next_expiry = float('inf')\n",
" refresh_id = -1\n",
" next_refresh = float('inf')\n",
"\n",
" if cache.storage:\n",
" expiry_id, next_expiry = min(cache.storage.items(), key=lambda x: x[1].next_expiry if x[1].next_expiry is not None else float('inf'))\n",
" next_expiry = cache.storage[expiry_id].next_expiry\n",
" refresh_id, next_refresh = min(cache.storage.items(), key=lambda x: x[1].next_refresh if x[1].next_refresh is not None else float('inf'))\n",
" next_refresh = cache.storage[refresh_id].next_refresh\n",
"\n",
" events = [\n",
" (request_id, next_request),\n",
" (expiry_id, next_expiry),\n",
" (refresh_id, next_refresh)\n",
" ]\n",
"\n",
" event_id, event_timestamp = min(events, key=lambda x: x[1] if x[1] is not None else float('inf'))\n",
" \n",
" # if event_id == request_id and event_timestamp == next_request:\n",
" # print(f\"[{env.now:.2f}] Waiting for request...\")\n",
" # elif event_id == expiry_id and event_timestamp == next_expiry:\n",
" # print(f\"[{env.now:.2f}] Waiting for expiry until...\")\n",
" # elif event_id == refresh_id and event_timestamp == next_refresh:\n",
" # print(f\"[{env.now:.2f}] Waiting for refresh...\")\n",
" \n",
" yield(env.timeout(event_timestamp - env.now))\n",
"\n",
" if event_id == request_id and event_timestamp == next_request:\n",
" assert env.now >= next_request, f\"[{env.now}] Time for request should've been reached for Object {request_id}\"\n",
" cache.get(request_id)\n",
" elif event_id == expiry_id and event_timestamp == next_expiry:\n",
" assert env.now >= next_expiry, f\"[{env.now}] Time for expiry should've been reached for Object {expiry_id}\"\n",
" cache.check_expired(expiry_id)\n",
" elif event_id == refresh_id and event_timestamp == next_refresh:\n",
" assert env.now >= next_refresh, f\"[{env.now}] Time for refresh should've been reached for Object {refresh_id}\"\n",
" cache.refresh_object(refresh_id)\n",
" else:\n",
" assert False, \"Unreachable\"\n",
"\n",
" # For progress bar\n",
" if (int(env.now) % 1) == 0 and int(env.now) != last_print:\n",
" last_print = int(env.now)\n",
" pbar.n = min(cache.access_count.values())\n",
" pbar.refresh()\n",
" \n",
" # Simulation stop condition\n",
" if all(access_count >= ACCESS_COUNT_LIMIT for access_count in cache.access_count.values()):\n",
" print(f\"Simulation ended after {env.now} seconds.\")\n",
" for obj_id in cache.storage.keys():\n",
" cache.cumulative_cache_time[obj_id].append(env.now - cache.storage[obj_id].initial_fetch_timer)\n",
" event.succeed()\n",
" \n",
" cache.record_cache_state()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "c8516830-9880-4d9e-a91b-000338baf9d6",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"class Simulation:\n",
" def __init__(self, simulation_config: Union[TTLSimulation, LRUSimulation, RandomEvictionSimulation, RefreshSimulation]):\n",
" # Initialize simulation environment\n",
" self.env = simpy.Environment()\n",
" \n",
" # Instantiate components\n",
" self.db = Database(simulation_config.db_objects)\n",
" self.cache = Cache(self.env, self.db, simulation_config)\n",
"\n",
" def run_simulation(self):\n",
" # Start processes\n",
" # env.process(age_cache_process(env, cache))\n",
" stop_event = self.env.event()\n",
" self.env.process(client_request_process(self.env, self.cache, stop_event))\n",
" \n",
" # Run the simulation\n",
" self.env.run(until=stop_event)\n",
" self.end_time = self.env.now"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "e269b607-16b9-46d0-8a97-7324f2002c72",
"metadata": {},
"outputs": [],
"source": [
"# Simulate with a Cache that does random evictions, We'll have 100 Database Objects and a Cache Size of 10\n",
"# We'll generate lambdas from a zipf distribution\n",
"# config = RandomEvictionSimulation(100, 10)\n",
"# config.generate_objects()"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "33fdc5fd-1f39-4b51-b2c7-6ea6acf2b753",
"metadata": {},
"outputs": [],
"source": [
"# Simulate with a Cache that does lru, We'll have 100 Database Objects and a Cache Size of 10\n",
"# We'll generate lambdas from a zipf distribution\n",
"config = LRUSimulation(100, 10)\n",
"config.from_file('./input/2024-12-13/input.csv', 'Lambda')"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "6c391bfd-b294-4ff7-8b22-51777368a6b9",
"metadata": {},
"outputs": [],
"source": [
"# Simulate with a Cache that does Refreshes with TTL based eviction, We'll have 100 Database Objects and a Cache Size of 10\n",
"# We'll generate lambdas from a zipf distribution. Each object will have a fixed ttl of 1 when its pulled into the cache. Mu for the refresh rate is 10\n",
"# config = RefreshSimulation(100, 10)\n",
"# config.from_file(path='./input/2024-12-13/output.csv', lambda_column_name='Lambda', ttl_column_name='TTL_2', mu_column_name='u_opt_2')"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "0a444c9d-53dd-4cab-b8f1-100ad3ab213a",
"metadata": {},
"outputs": [],
"source": [
"# Simulate with a Cache that does TTL based eviction, We'll have 100 Database Objects and a Cache Size of 10\n",
"# We'll take lambdas from the \"lambda\" column of the file \"../calculated.csv\" and the TTLs for each object from the \"optimal_TTL\" column of the same file.\n",
"# config = TTLSimulation(100, 10)\n",
"# config.from_file(\"../calculated.csv\", \"lambda\", \"optimal_TTL\")"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "6ac338bd-2094-41d2-8e92-565d03422b87",
"metadata": {},
"outputs": [],
"source": [
"with open(f\"{TEMP_BASE_DIR}/simulation_config.txt\", 'w') as f:\n",
" f.write(str(config))"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "66f65699-a3c9-48c4-8f1f-b9d7834c026a",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"IOPub message rate exceeded.██████████▉ | 149/1000 [00:07<00:44, 19.08it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.██████████████████████▍ | 220/1000 [00:09<00:35, 22.26it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.████████████████████████████▊ | 260/1000 [00:11<00:32, 23.10it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.███████████████████████████████████████████▏ | 349/1000 [00:13<00:24, 26.18it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.███████████████████████████████████████████████████▌ | 401/1000 [00:15<00:22, 26.67it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.████████████████████████████████████████████████████████████████▎ | 480/1000 [00:17<00:19, 27.16it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.██████████████████████████████████████████████████████████████████████▉ | 521/1000 [00:18<00:17, 27.82it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.███████████████████████████████████████████████████████████████████████████████████████▍ | 624/1000 [00:22<00:13, 27.69it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.██████████████████████████████████████████████████████████████████████████████████████████████████▌ | 693/1000 [00:24<00:10, 27.97it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.█████████████████████████████████████████████████████████████████████████████████████████████████████████ | 733/1000 [00:26<00:09, 28.08it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.███████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 796/1000 [00:28<00:07, 28.31it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 854/1000 [00:30<00:05, 28.39it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 920/1000 [00:31<00:02, 29.13it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n",
"IOPub message rate exceeded.████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 978/1000 [00:33<00:00, 29.28it/s]\n",
"The Jupyter server will temporarily stop sending output\n",
"to the client in order to avoid crashing it.\n",
"To change this limit, set the config variable\n",
"`--ServerApp.iopub_msg_rate_limit`.\n",
"\n",
"Current values:\n",
"ServerApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n",
"ServerApp.rate_limit_window=3.0 (secs)\n",
"\n"
]
}
],
"source": [
"%%time\n",
"\n",
"simulation = Simulation(config)\n",
"simulation.run_simulation()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "6f900c68-1f34-48d1-b346-ef6ea6911fa5",
"metadata": {},
"outputs": [],
"source": [
"cache = simulation.cache\n",
"db = simulation.db\n",
"simulation_end_time = simulation.end_time\n",
"database_object_count = len(db.data)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "3b6f7c1f-ea54-4496-bb9a-370cee2d2751",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Object 0: Hit Rate = 0.03, Average Time spend in Cache: 0.03, Average Age = 0.02, Expected Age = 1.12\n",
"Object 1: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.78\n",
"Object 2: Hit Rate = 0.03, Average Time spend in Cache: 0.04, Average Age = 0.02, Expected Age = 1.03\n",
"Object 3: Hit Rate = 0.05, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.80\n",
"Object 4: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.70\n",
"Object 5: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.52\n",
"Object 6: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.40\n",
"Object 7: Hit Rate = 0.02, Average Time spend in Cache: 0.04, Average Age = 0.02, Expected Age = 0.90\n",
"Object 8: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.31\n",
"Object 9: Hit Rate = 0.03, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.26\n",
"Object 10: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.56\n",
"Object 11: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.54\n",
"Object 12: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.39\n",
"Object 13: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.31\n",
"Object 14: Hit Rate = 0.05, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.68\n",
"Object 15: Hit Rate = 0.03, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.15\n",
"Object 16: Hit Rate = 0.05, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.70\n",
"Object 17: Hit Rate = 0.03, Average Time spend in Cache: 0.04, Average Age = 0.02, Expected Age = 1.10\n",
"Object 18: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.52\n",
"Object 19: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.24\n",
"Object 20: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.40\n",
"Object 21: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.40\n",
"Object 22: Hit Rate = 0.04, Average Time spend in Cache: 0.04, Average Age = 0.03, Expected Age = 1.34\n",
"Object 23: Hit Rate = 0.05, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.67\n",
"Object 24: Hit Rate = 0.05, Average Time spend in Cache: 0.04, Average Age = 0.04, Expected Age = 1.48\n",
"Object 25: Hit Rate = 0.04, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.40\n",
"Object 26: Hit Rate = 0.04, Average Time spend in Cache: 0.05, Average Age = 0.03, Expected Age = 1.39\n",
"Object 27: Hit Rate = 0.04, Average Time spend in Cache: 0.05, Average Age = 0.03, Expected Age = 1.29\n",
"Object 28: Hit Rate = 0.04, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.28\n",
"Object 29: Hit Rate = 0.06, Average Time spend in Cache: 0.04, Average Age = 0.05, Expected Age = 1.69\n",
"Object 30: Hit Rate = 0.06, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.70\n",
"Object 31: Hit Rate = 0.05, Average Time spend in Cache: 0.05, Average Age = 0.05, Expected Age = 1.58\n",
"Object 32: Hit Rate = 0.04, Average Time spend in Cache: 0.05, Average Age = 0.03, Expected Age = 1.26\n",
"Object 33: Hit Rate = 0.04, Average Time spend in Cache: 0.05, Average Age = 0.03, Expected Age = 1.28\n",
"Object 34: Hit Rate = 0.05, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.53\n",
"Object 35: Hit Rate = 0.06, Average Time spend in Cache: 0.05, Average Age = 0.05, Expected Age = 1.58\n",
"Object 36: Hit Rate = 0.05, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.40\n",
"Object 37: Hit Rate = 0.04, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.24\n",
"Object 38: Hit Rate = 0.05, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.28\n",
"Object 39: Hit Rate = 0.06, Average Time spend in Cache: 0.05, Average Age = 0.05, Expected Age = 1.53\n",
"Object 40: Hit Rate = 0.05, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.34\n",
"Object 41: Hit Rate = 0.05, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.28\n",
"Object 42: Hit Rate = 0.07, Average Time spend in Cache: 0.05, Average Age = 0.06, Expected Age = 1.72\n",
"Object 43: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.45\n",
"Object 44: Hit Rate = 0.06, Average Time spend in Cache: 0.05, Average Age = 0.04, Expected Age = 1.45\n",
"Object 45: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.42\n",
"Object 46: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.40\n",
"Object 47: Hit Rate = 0.05, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.21\n",
"Object 48: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.53\n",
"Object 49: Hit Rate = 0.05, Average Time spend in Cache: 0.06, Average Age = 0.04, Expected Age = 1.10\n",
"Object 50: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.32\n",
"Object 51: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.04, Expected Age = 1.32\n",
"Object 52: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.41\n",
"Object 53: Hit Rate = 0.06, Average Time spend in Cache: 0.06, Average Age = 0.05, Expected Age = 1.23\n",
"Object 54: Hit Rate = 0.06, Average Time spend in Cache: 0.07, Average Age = 0.05, Expected Age = 1.26\n",
"Object 55: Hit Rate = 0.06, Average Time spend in Cache: 0.07, Average Age = 0.05, Expected Age = 1.35\n",
"Object 56: Hit Rate = 0.06, Average Time spend in Cache: 0.07, Average Age = 0.06, Expected Age = 1.28\n",
"Object 57: Hit Rate = 0.07, Average Time spend in Cache: 0.07, Average Age = 0.06, Expected Age = 1.49\n",
"Object 58: Hit Rate = 0.06, Average Time spend in Cache: 0.07, Average Age = 0.05, Expected Age = 1.15\n",
"Object 59: Hit Rate = 0.07, Average Time spend in Cache: 0.07, Average Age = 0.07, Expected Age = 1.40\n",
"Object 60: Hit Rate = 0.07, Average Time spend in Cache: 0.07, Average Age = 0.06, Expected Age = 1.35\n",
"Object 61: Hit Rate = 0.07, Average Time spend in Cache: 0.07, Average Age = 0.06, Expected Age = 1.23\n",
"Object 62: Hit Rate = 0.07, Average Time spend in Cache: 0.07, Average Age = 0.06, Expected Age = 1.37\n",
"Object 63: Hit Rate = 0.08, Average Time spend in Cache: 0.07, Average Age = 0.07, Expected Age = 1.39\n",
"Object 64: Hit Rate = 0.08, Average Time spend in Cache: 0.08, Average Age = 0.07, Expected Age = 1.44\n",
"Object 65: Hit Rate = 0.09, Average Time spend in Cache: 0.08, Average Age = 0.07, Expected Age = 1.47\n",
"Object 66: Hit Rate = 0.09, Average Time spend in Cache: 0.08, Average Age = 0.07, Expected Age = 1.48\n",
"Object 67: Hit Rate = 0.08, Average Time spend in Cache: 0.08, Average Age = 0.07, Expected Age = 1.37\n",
"Object 68: Hit Rate = 0.09, Average Time spend in Cache: 0.09, Average Age = 0.08, Expected Age = 1.43\n",
"Object 69: Hit Rate = 0.09, Average Time spend in Cache: 0.09, Average Age = 0.08, Expected Age = 1.44\n",
"Object 70: Hit Rate = 0.09, Average Time spend in Cache: 0.09, Average Age = 0.08, Expected Age = 1.42\n",
"Object 71: Hit Rate = 0.09, Average Time spend in Cache: 0.09, Average Age = 0.07, Expected Age = 1.34\n",
"Object 72: Hit Rate = 0.10, Average Time spend in Cache: 0.10, Average Age = 0.08, Expected Age = 1.44\n",
"Object 73: Hit Rate = 0.09, Average Time spend in Cache: 0.10, Average Age = 0.07, Expected Age = 1.29\n",
"Object 74: Hit Rate = 0.10, Average Time spend in Cache: 0.10, Average Age = 0.08, Expected Age = 1.34\n",
"Object 75: Hit Rate = 0.10, Average Time spend in Cache: 0.11, Average Age = 0.09, Expected Age = 1.34\n",
"Object 76: Hit Rate = 0.10, Average Time spend in Cache: 0.11, Average Age = 0.09, Expected Age = 1.28\n",
"Object 77: Hit Rate = 0.11, Average Time spend in Cache: 0.11, Average Age = 0.09, Expected Age = 1.39\n",
"Object 78: Hit Rate = 0.12, Average Time spend in Cache: 0.11, Average Age = 0.11, Expected Age = 1.49\n",
"Object 79: Hit Rate = 0.12, Average Time spend in Cache: 0.12, Average Age = 0.11, Expected Age = 1.45\n",
"Object 80: Hit Rate = 0.12, Average Time spend in Cache: 0.12, Average Age = 0.10, Expected Age = 1.28\n",
"Object 81: Hit Rate = 0.13, Average Time spend in Cache: 0.13, Average Age = 0.12, Expected Age = 1.44\n",
"Object 82: Hit Rate = 0.12, Average Time spend in Cache: 0.13, Average Age = 0.11, Expected Age = 1.20\n",
"Object 83: Hit Rate = 0.15, Average Time spend in Cache: 0.14, Average Age = 0.13, Expected Age = 1.47\n",
"Object 84: Hit Rate = 0.16, Average Time spend in Cache: 0.15, Average Age = 0.14, Expected Age = 1.46\n",
"Object 85: Hit Rate = 0.16, Average Time spend in Cache: 0.15, Average Age = 0.14, Expected Age = 1.42\n",
"Object 86: Hit Rate = 0.16, Average Time spend in Cache: 0.16, Average Age = 0.15, Expected Age = 1.39\n",
"Object 87: Hit Rate = 0.17, Average Time spend in Cache: 0.17, Average Age = 0.16, Expected Age = 1.39\n",
"Object 88: Hit Rate = 0.17, Average Time spend in Cache: 0.18, Average Age = 0.16, Expected Age = 1.29\n",
"Object 89: Hit Rate = 0.19, Average Time spend in Cache: 0.19, Average Age = 0.18, Expected Age = 1.37\n",
"Object 90: Hit Rate = 0.21, Average Time spend in Cache: 0.21, Average Age = 0.21, Expected Age = 1.42\n",
"Object 91: Hit Rate = 0.22, Average Time spend in Cache: 0.22, Average Age = 0.22, Expected Age = 1.33\n",
"Object 92: Hit Rate = 0.24, Average Time spend in Cache: 0.25, Average Age = 0.25, Expected Age = 1.36\n",
"Object 93: Hit Rate = 0.27, Average Time spend in Cache: 0.27, Average Age = 0.28, Expected Age = 1.38\n",
"Object 94: Hit Rate = 0.28, Average Time spend in Cache: 0.29, Average Age = 0.30, Expected Age = 1.30\n",
"Object 95: Hit Rate = 0.33, Average Time spend in Cache: 0.33, Average Age = 0.37, Expected Age = 1.35\n",
"Object 96: Hit Rate = 0.38, Average Time spend in Cache: 0.38, Average Age = 0.44, Expected Age = 1.35\n",
"Object 97: Hit Rate = 0.45, Average Time spend in Cache: 0.45, Average Age = 0.58, Expected Age = 1.35\n",
"Object 98: Hit Rate = 0.57, Average Time spend in Cache: 0.57, Average Age = 0.92, Expected Age = 1.48\n",
"Object 99: Hit Rate = 0.77, Average Time spend in Cache: 0.77, Average Age = 1.97, Expected Age = 1.87\n"
]
}
],
"source": [
"statistics = []\n",
"# Calculate and print hit rate and average age for each object\n",
"for obj_id in range(database_object_count):\n",
" if cache.access_count[obj_id] != 0:\n",
" output = \"\"\n",
" expected_hit_rate = None\n",
" hit_rate = cache.hits[obj_id] / max(1, cache.access_count[obj_id])\n",
" output += f\"Object {obj_id}: Hit Rate = {hit_rate:.2f}, \"\n",
" if db.data[obj_id].ttl is not None:\n",
" expected_hit_rate = 1-math.exp(-db.data[obj_id].lambda_value*(db.data[obj_id].ttl))\n",
" output += f\"Expected Hit Rate = {expected_hit_rate:.2f}, \"\n",
" avg_cache_time = sum(cache.cumulative_cache_time[obj_id]) / max(1, simulation_end_time) \n",
" output += f\"Average Time spend in Cache: {avg_cache_time:.2f}, \"\n",
" avg_age = sum(cache.cumulative_age[obj_id]) / max(len(cache.cumulative_age[obj_id]), 1)\n",
" output += f\"Average Age = {avg_age:.2f}, \"\n",
" expected_age = hit_rate / (db.data[obj_id].lambda_value * (1 - pow(hit_rate,2)))\n",
" output += f\"Expected Age = {expected_age:.2f}\"\n",
" print(output)\n",
" if db.data[obj_id].ttl is not None:\n",
" statistics.append({\n",
" \"obj_id\": obj_id,\n",
" \"hit_rate\": hit_rate, \n",
" \"expected_hit_rate\": expected_hit_rate, \n",
" \"avg_cache_time\":avg_cache_time, \n",
" \"avg_age\": avg_age, \n",
" \"expected_age\": expected_age\n",
" })\n",
" else:\n",
" statistics.append({\n",
" \"obj_id\": obj_id,\n",
" \"hit_rate\": hit_rate, \n",
" \"avg_cache_time\":avg_cache_time, \n",
" \"avg_age\": avg_age, \n",
" \"expected_age\": expected_age\n",
" })"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "b2d18372-cdba-4151-ae32-5bf45466bf94",
"metadata": {},
"outputs": [],
"source": [
"stats = pd.DataFrame(statistics)\n",
"stats.to_csv(f\"{TEMP_BASE_DIR}/hit_age.csv\",index=False)\n",
"stats.drop(\"obj_id\", axis=1).describe().to_csv(f\"{TEMP_BASE_DIR}/overall_hit_age.csv\")"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "be7e67e7-4533-438a-ab65-ca813f48052a",
"metadata": {},
"outputs": [],
"source": [
"expected_hit_rate = None\n",
"expected_hit_rate_delta = None"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "80971714-44f1-47db-9e89-85be7c885bde",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>access_count</th>\n",
" <th>hits</th>\n",
" <th>misses</th>\n",
" <th>mu</th>\n",
" <th>lambda</th>\n",
" <th>hit_rate</th>\n",
" <th>avg_cache_time</th>\n",
" <th>cache_time_delta</th>\n",
" <th>avg_age</th>\n",
" <th>ages</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1000</td>\n",
" <td>28</td>\n",
" <td>972</td>\n",
" <td>None</td>\n",
" <td>0.0251</td>\n",
" <td>0.028000</td>\n",
" <td>0.033253</td>\n",
" <td>-0.005253</td>\n",
" <td>0.023204</td>\n",
" <td>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1067</td>\n",
" <td>48</td>\n",
" <td>1019</td>\n",
" <td>None</td>\n",
" <td>0.0253</td>\n",
" <td>0.044986</td>\n",
" <td>0.035666</td>\n",
" <td>0.009320</td>\n",
" <td>0.043221</td>\n",
" <td>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1100</td>\n",
" <td>29</td>\n",
" <td>1071</td>\n",
" <td>None</td>\n",
" <td>0.0255</td>\n",
" <td>0.026364</td>\n",
" <td>0.036409</td>\n",
" <td>-0.010046</td>\n",
" <td>0.020289</td>\n",
" <td>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1107</td>\n",
" <td>51</td>\n",
" <td>1056</td>\n",
" <td>None</td>\n",
" <td>0.0257</td>\n",
" <td>0.046070</td>\n",
" <td>0.036398</td>\n",
" <td>0.009673</td>\n",
" <td>0.038728</td>\n",
" <td>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1086</td>\n",
" <td>48</td>\n",
" <td>1038</td>\n",
" <td>None</td>\n",
" <td>0.0260</td>\n",
" <td>0.044199</td>\n",
" <td>0.036152</td>\n",
" <td>0.008047</td>\n",
" <td>0.036428</td>\n",
" <td>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>95</th>\n",
" <td>11724</td>\n",
" <td>3884</td>\n",
" <td>7840</td>\n",
" <td>None</td>\n",
" <td>0.2759</td>\n",
" <td>0.331286</td>\n",
" <td>0.329118</td>\n",
" <td>0.002168</td>\n",
" <td>0.369984</td>\n",
" <td>[0, 0, 0, 0, 0, 0.689766350641932, 0, 0, 0, 0,...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>96</th>\n",
" <td>14086</td>\n",
" <td>5349</td>\n",
" <td>8737</td>\n",
" <td>None</td>\n",
" <td>0.3299</td>\n",
" <td>0.379739</td>\n",
" <td>0.382476</td>\n",
" <td>-0.002737</td>\n",
" <td>0.440054</td>\n",
" <td>[0, 0, 0.6257414568279982, 0, 0.29622958616229...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>97</th>\n",
" <td>17519</td>\n",
" <td>7857</td>\n",
" <td>9662</td>\n",
" <td>None</td>\n",
" <td>0.4152</td>\n",
" <td>0.448485</td>\n",
" <td>0.452053</td>\n",
" <td>-0.003568</td>\n",
" <td>0.576042</td>\n",
" <td>[0, 0, 0, 0, 2.1522941477509043, 0, 0.29715894...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>98</th>\n",
" <td>24674</td>\n",
" <td>14117</td>\n",
" <td>10557</td>\n",
" <td>None</td>\n",
" <td>0.5743</td>\n",
" <td>0.572141</td>\n",
" <td>0.571998</td>\n",
" <td>0.000143</td>\n",
" <td>0.919750</td>\n",
" <td>[0, 0.055604529502096156, 0.7127466308343264, ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>99</th>\n",
" <td>43025</td>\n",
" <td>33051</td>\n",
" <td>9974</td>\n",
" <td>None</td>\n",
" <td>1.0000</td>\n",
" <td>0.768181</td>\n",
" <td>0.768395</td>\n",
" <td>-0.000213</td>\n",
" <td>1.966054</td>\n",
" <td>[0, 0.7104371771341902, 0.9702423340515842, 0,...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>100 rows × 10 columns</p>\n",
"</div>"
],
"text/plain": [
" access_count hits misses mu lambda hit_rate avg_cache_time \\\n",
"0 1000 28 972 None 0.0251 0.028000 0.033253 \n",
"1 1067 48 1019 None 0.0253 0.044986 0.035666 \n",
"2 1100 29 1071 None 0.0255 0.026364 0.036409 \n",
"3 1107 51 1056 None 0.0257 0.046070 0.036398 \n",
"4 1086 48 1038 None 0.0260 0.044199 0.036152 \n",
".. ... ... ... ... ... ... ... \n",
"95 11724 3884 7840 None 0.2759 0.331286 0.329118 \n",
"96 14086 5349 8737 None 0.3299 0.379739 0.382476 \n",
"97 17519 7857 9662 None 0.4152 0.448485 0.452053 \n",
"98 24674 14117 10557 None 0.5743 0.572141 0.571998 \n",
"99 43025 33051 9974 None 1.0000 0.768181 0.768395 \n",
"\n",
" cache_time_delta avg_age \\\n",
"0 -0.005253 0.023204 \n",
"1 0.009320 0.043221 \n",
"2 -0.010046 0.020289 \n",
"3 0.009673 0.038728 \n",
"4 0.008047 0.036428 \n",
".. ... ... \n",
"95 0.002168 0.369984 \n",
"96 -0.002737 0.440054 \n",
"97 -0.003568 0.576042 \n",
"98 0.000143 0.919750 \n",
"99 -0.000213 1.966054 \n",
"\n",
" ages \n",
"0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n",
"1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n",
"2 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n",
"3 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n",
"4 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n",
".. ... \n",
"95 [0, 0, 0, 0, 0, 0.689766350641932, 0, 0, 0, 0,... \n",
"96 [0, 0, 0.6257414568279982, 0, 0.29622958616229... \n",
"97 [0, 0, 0, 0, 2.1522941477509043, 0, 0.29715894... \n",
"98 [0, 0.055604529502096156, 0.7127466308343264, ... \n",
"99 [0, 0.7104371771341902, 0.9702423340515842, 0,... \n",
"\n",
"[100 rows x 10 columns]"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"access_count = pd.DataFrame.from_dict(cache.access_count, orient='index', columns=['access_count'])\n",
"hits = pd.DataFrame.from_dict(cache.hits, orient='index', columns=['hits'])\n",
"misses = pd.DataFrame.from_dict(cache.misses, orient='index', columns=['misses'])\n",
"mu = pd.DataFrame.from_dict({l: db.data[l].mu_value for l in range(database_object_count)}, orient='index', columns=['mu'])\n",
"lmbda = pd.DataFrame.from_dict({l: db.data[l].lambda_value for l in range(database_object_count)}, orient='index', columns=['lambda'])\n",
"\n",
"hit_rate = pd.DataFrame(stats['hit_rate'])\n",
"hit_rate.index = range(database_object_count)\n",
"if 'expected_hit_rate' in stats:\n",
" expected_hit_rate = pd.DataFrame(stats['expected_hit_rate'])\n",
" expected_hit_rate.index = range(database_object_count)\n",
" expected_hit_rate_delta = pd.DataFrame((hit_rate.to_numpy()-expected_hit_rate.to_numpy()), columns=['expected_hit_rate_delta'])\n",
" expected_hit_rate_delta.index = range(database_object_count)\n",
"avg_cache_time = pd.DataFrame(stats['avg_cache_time'])\n",
"avg_cache_time.index = range(database_object_count)\n",
"cache_time_delta = pd.DataFrame((hit_rate.to_numpy()-avg_cache_time.to_numpy()), columns=['cache_time_delta'])\n",
"cache_time_delta.index = range(database_object_count)\n",
"\n",
"avg_age = pd.DataFrame(stats['avg_age'])\n",
"avg_age.index = range(database_object_count)\n",
"\n",
"ages = {k: str(v) for k,v in cache.cumulative_age.items()}\n",
"ages = pd.DataFrame.from_dict(ages, orient='index', columns=['ages'])\n",
"\n",
"merged = access_count.merge(hits, left_index=True, right_index=True).merge(misses, left_index=True, right_index=True) \\\n",
" .merge(mu, left_index=True, right_index=True).merge(lmbda, left_index=True, right_index=True) \\\n",
" .merge(hit_rate, left_index=True, right_index=True)\n",
"if 'expected_hit_rate' in stats:\n",
" merged = merged.merge(expected_hit_rate, left_index=True, right_index=True).merge(expected_hit_rate_delta, left_index=True, right_index=True)\n",
"merged = merged.merge(avg_cache_time, left_index=True, right_index=True).merge(cache_time_delta, left_index=True, right_index=True) \\\n",
" .merge(avg_age, left_index=True, right_index=True).merge(ages, left_index=True, right_index=True)\n",
"merged.to_csv(f\"{TEMP_BASE_DIR}/details.csv\", index_label=\"obj_id\")\n",
"merged"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "01f8f9ee-c278-4a22-8562-ba02e77f5ddd",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAACVcAAAHWCAYAAAB5HisgAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABjaklEQVR4nOzdeZhe48E/8O+TPZEdWRAREltIU5TGvoeqpdUSte8lSmhVVC2xNCgavNZqLS1Va/tqScSa2sWugiBUaSgRISEmmfP7o7/MazpPYiYmeWbk87muXNc897nPOd/zzDNzX+P6OqdUFEURAAAAAAAAAAAAamlR6QAAAAAAAAAAAABNkXIVAAAAAAAAAABAGcpVAAAAAAAAAAAAZShXAQAAAAAAAAAAlKFcBQAAAAAAAAAAUIZyFQAAAAAAAAAAQBnKVQAAAAAAAAAAAGUoVwEAAAAAAAAAAJShXAUAAAAAAAAAAFCGchUAAABAkvvuuy+lUik33XRTpaPUyzvvvJPvfe97WXrppVMqlTJmzJhGO/brr7+eUqmUc8455wvnnnLKKSmVSo127sWhVCrllFNOqXSMRlEqlXLEEUdUOkazttJKK2W//fardAwAAACgiVKuAgAAABabq666KqVSKe3atctbb71VZ/vmm2+etdZaqwLJmp+jjz4648aNy/HHH5/f/e532W677RY4f+bMmTnttNMyaNCgdOjQIV26dMkmm2ySa665JkVRLKbUDXf77bc3myLUfffdl+9+97vp1atX2rRpkx49emTHHXfMLbfcUulojaKqqioXXHBBvvGNb6RTp07p2LFjvvGNb+SCCy5IVVVVpePVmFeUrM8/AAAAgC/SqtIBAAAAgCXP7Nmzc+aZZ+bCCy+sdJRm65577snOO++cn/zkJ18495133slWW22VSZMmZdiwYTniiCPy6aef5uabb86+++6b22+/Pddee21atmzZ4Bw///nPM3LkyIW5hHq5/fbbc9FFFzVqweqTTz5Jq1aN+5/FTj755Jx66qkZMGBADj300PTt2zfvv/9+br/99uy666659tpr84Mf/KBRz7k4zZw5MzvssEPuv//+fPvb385+++2XFi1aZOzYsTnqqKNyyy235K9//WuWWmqpSkfNGmuskd/97ne1xo4//vh07NgxJ5xwQp35L730Ulq08P+gAgAAAOUpVwEAAACL3eDBg/PrX/86xx9/fJZbbrlKx1msZs6c2SgFlHfffTddu3at19x99903kyZNyq233pqddtqpZvzII4/Msccem3POOSdf//rXc9xxxzU4R6tWrRq9qLSotWvXrlGPd9NNN+XUU0/N9773vVx33XVp3bp1zbZjjz0248aNa1J3dloYxxxzTO6///5ceOGFtR5DeNhhh+Wiiy7KEUcckZ/85Ce55JJLFlumoijy6aefpn379rXGe/bsmb322qvW2JlnnpllllmmzniStG3bdpHmBAAAAJo3/0sWAAAAsNj97Gc/y9y5c3PmmWcucN7rr7+eUqmUq666qs62UqlU625Gp5xySkqlUl5++eXstdde6dKlS5ZddtmceOKJKYoib775Znbeeed07tw5vXr1yrnnnlv2nHPnzs3Pfvaz9OrVK0sttVR22mmnvPnmm3XmPfroo9luu+3SpUuXdOjQIZtttlkefPDBWnPmZXrhhRfygx/8IN26dcvGG2+8wGt+7bXX8v3vfz/du3dPhw4d8s1vfjN//etfa7bPe7RiURS56KKLvvDxZo888kjGjRuX/fbbr1axap7Ro0dnwIABOeuss/LJJ5/U2f6rX/0qffv2Tfv27bPZZpvl+eefL3uN/+33v/991l133bRv3z7du3fPsGHD5vs+futb30q3bt2y1FJLZdCgQTn//POTJPvtt18uuuiiJCn7KLfrr78+6667bjp16pTOnTtn7bXXrtl3Qeb32XnllVey3377pWvXrunSpUv233//zJo16wuPd+KJJ6Z79+757W9/W6tYNc/QoUPz7W9/O0ny2Wef5aSTTsq6666bLl26ZKmllsomm2ySe++9t85+1dXVOf/887P22munXbt2WXbZZbPddttl4sSJdeb+6U9/ylprrZW2bdtm4MCBGTt2bJ05b731Vg444ID07NmzZt5vf/vbL7y+f/7zn/nNb36TLbfcslaxap7hw4dniy22yBVXXJF//vOfSZK11lorW2yxRdlrWn755fO9732v1tiYMWMycODAtGvXLj179syhhx6aDz74oNa+K620Ur797W9n3LhxWW+99dK+fftcdtllX5j/i6y00krZb7/9al7P+xl74IEHcuSRR2bZZZdN165dc+ihh+azzz7L9OnTs88++6Rbt27p1q1bfvrTn9Z5tGZ9rwkAAABo+pSrAAAAgMWuX79+2WefffLrX/86b7/9dqMee/fdd091dXXOPPPMbLDBBjn99NMzZsyYbLPNNll++eVz1llnpX///vnJT36SCRMm1Nn/jDPOyF//+tccd9xxOfLIIzN+/PhsvfXWtYpH99xzTzbddNPMmDEjJ598cn7xi19k+vTp2XLLLfPYY4/VOeb3v//9zJo1K7/4xS9y8MEHzzf7O++8kw033DDjxo3L4YcfnjPOOCOffvppdtppp9x6661Jkk033bTmkWfbbLNNfve739V5BNrn3XbbbUmSffbZp+z2Vq1a5Qc/+EE++OCDOuWwa665JhdccEGGDx+e448/Ps8//3y23HLLvPPOO/M9X/Kf93CfffbJgAEDct5552XEiBG5++67s+mmm2b69Ok188aPH59NN900L7zwQo466qice+652WKLLfKXv/wlSXLooYdmm222SZKa65x3rePHj88ee+yRbt265ayzzsqZZ56ZzTffvM41NMRuu+2Wjz76KKNHj85uu+2Wq666KqNGjVrgPpMnT86LL76YXXbZJZ06dfrCc8yYMSNXXHFFNt9885x11lk55ZRT8u9//ztDhw7N008/XWvugQcemBEjRqRPnz4566yzMnLkyLRr1y6PPPJIrXkPPPBADj/88AwbNixnn312Pv300+y66655//33a+a88847+eY3v5m77rorRxxxRM4///z0798/Bx54YMaMGbPAzHfccUfmzp07389Q8p/P15w5c2pKXbvvvnsmTJiQqVOn1sn69ttvZ9iwYTVjhx56aI499thstNFGOf/887P//vvn2muvzdChQ+vc8eull17KHnvskW222Sbnn39+Bg8evMDsX8aPfvSjTJ48OaNGjcpOO+2Uyy+/PCeeeGJ23HHHzJ07N7/4xS+y8cYb55e//GWdn8GGXBMAAADQxBUAAAAAi8mVV15ZJCkef/zx4tVXXy1atWpVHHnkkTXbN9tss2LgwIE1r6dMmVIkKa688so6x0pSnHzyyTWvTz755CJJccghh9SMzZkzp1hhhRWKUqlUnHnmmTXjH3zwQdG+ffti3333rRm79957iyTF8ssvX8yYMaNm/IYbbiiSFOeff35RFEVRXV1dDBgwoBg6dGhRXV1dM2/WrFlFv379im222aZOpj322KNe78+IESOKJMXf/va3mrGPPvqo6NevX7HSSisVc+fOrXX9w4cP/8Jj7rLLLkWS4oMPPpjvnFtuuaVIUlxwwQVFUfzf+96+ffvin//8Z828Rx99tEhSHH300XWucZ7XX3+9aNmyZXHGGWfUOsdzzz1XtGrVqmZ8zpw5Rb9+/Yq+ffvWyfb593X48OFFuf+EddRRRxWdO3cu5syZ84XvwX+b32fngAMOqDXvO9/5TrH00ksv8Fh//vOfiyTFr371q3qde86cOcXs2bNrjX3wwQdFz549a53/nnvuKZLU+vmY5/PvT5KiTZs2xSuvvFIz9swzzxRJigsvvLBm7MADDyx69+5dvPfee7WONWzYsKJLly7FrFmz5pt53ufyqaeemu+cJ598skhSHHPMMUVRFMVLL71UJ0NRFMXhhx9edOzYseZ8f/vb34okxbXXXltr3tixY+uM9+3bt0hSjB07dr455mfgwIHFZpttVnZb3759a/0umPd76r9/xocMGVKUSqXihz/8Yc3YvN8xnz92Q64JAAAAaPrcuQoAAACoiJVXXjl77713Lr/88vzrX/9qtOMedNBBNV+3bNky6623XoqiyIEHHlgz3rVr16y22mp57bXX6uy/zz771LoD0fe+97307t07t99+e5Lk6aefzuTJk/ODH/wg77//ft5777289957mTlzZrbaaqtMmDAh1dXVtY75wx/+sF7Zb7/99qy//vq1Hh3YsWPHHHLIIXn99dfzwgsv1O9N+JyPPvooSRZ4V6V522bMmFFrfJdddsnyyy9f83r99dfPBhtsUPNelHPLLbekuro6u+22W817895776VXr14ZMGBAzePvnnrqqUyZMiUjRoxI165dax1jQY85nKdr166ZOXNmxo8f/4Vz6+u/v0+bbLJJ3n///Trvy+fN21afu1Yl//lMtmnTJsl/Hh03bdq0zJkzJ+utt16efPLJmnk333xzSqVSTj755DrH+O/3Z+utt84qq6xS83rQoEHp3Llzzee7KIrcfPPN2XHHHVMURa3vy9ChQ/Phhx/WOvd/W5jP0KqrrprBgwfnj3/8Y82cuXPn5qabbsqOO+6Y9u3bJ0luvPHGdOnSJdtss02tXOuuu246duxY53GJ/fr1y9ChQ+ebozEdeOCBtd7rDTbYoM7vknm/Yz7/u6Sh1wQAAAA0ba0qHQAAAABYcv385z/P7373u5x55pk5//zzG+WYK664Yq3XXbp0Sbt27bLMMsvUGf/8Y9PmGTBgQK3XpVIp/fv3z+uvv57kP4+BS5J99913vhk+/PDDdOvWreZ1v3796pX9jTfeyAYbbFBnfI011qjZvtZaa9XrWPPMK7189NFHdUpM88yvPPPf70Xyn9LMDTfcMN/zTZ48OUVRlN03SVq3bp0kefXVV5Okwdczz+GHH54bbrgh22+/fZZffvlsu+222W233bLddtst1PGSup+ded/DDz74IJ07dy67z7zxee9hfVx99dU599xz8+KLL9Z6RNznPyevvvpqlltuuXTv3r3Buedl/+CDD5Ik//73vzN9+vRcfvnlufzyy8se4913353v8T//GZqfcp+h3XffPT/72c/y1ltvZfnll899992Xd999N7vvvnvNnMmTJ+fDDz9Mjx496pWrvj9LjaHc75Ik6dOnT53xee910vBrAgAAAJo25SoAAACgYlZeeeXstddeufzyyzNy5Mg62+d3B6O5c+fO95gtW7as11jynzv6NNS8u1L98pe/zODBg8vO6dixY63X8+7SUwlrrLFG/vSnP+XZZ5/NpptuWnbOs88+myRZc801v/T5qqurUyqVcscdd5R93//7vVlYPXr0yNNPP51x48bljjvuyB133JErr7wy++yzT66++uqFOubCfE5WX331JMlzzz1Xr3P8/ve/z3777Zdddtklxx57bHr06JGWLVtm9OjRNYWzhvqi3PM+s3vttdd8S4GDBg2a7/HnlfueffbZ+X7my32Gdt999xx//PG58cYbM2LEiNxwww3p0qVLrQJcdXV1evTokWuvvbbscZdddtlarxfnz9L83tdy45//jDT0mgAAAICmTbkKAAAAqKif//zn+f3vf5+zzjqrzrZ5dw6aPn16rfE33nhjkeWZd2eqeYqiyCuvvFJTPpn3+LXOnTtn6623btRz9+3bNy+99FKd8RdffLFme0N9+9vfzujRo3PNNdeULVfNnTs31113Xbp165aNNtqo1rb/fi+S5OWXX85KK6003/OtssoqKYoi/fr1y6qrrrrAeUny/PPPL/B9XNAjAtu0aZMdd9wxO+64Y6qrq3P44Yfnsssuy4knnpj+/fvPd7/GtOqqq2a11VbLn//855x//vlfWB676aabsvLKK+eWW26pdW3//fi/VVZZJePGjcu0adPqdfeqBVl22WXTqVOnzJ07d6E+s9tvv31atmyZ3/3ud9lnn33KzrnmmmvSqlWrWsWpfv36Zf31188f//jHHHHEEbnllluyyy67pG3btjVzVlllldx1113ZaKONKlpCbExfxWsCAACAJVmLSgcAAAAAlmyrrLJK9tprr1x22WWZOnVqrW2dO3fOMssskwkTJtQav/jiixdZnmuuuabW489uuumm/Otf/8r222+fJFl33XWzyiqr5JxzzsnHH39cZ/9///vfC33ub33rW3nsscfy8MMP14zNnDkzl19+eVZaaaWFurPUhhtumK233jpXXnll/vKXv9TZfsIJJ+Tll1/OT3/60zpFkD/96U956623al4/9thjefTRR2vei3K++93vpmXLlhk1alSdOz4VRVHzKMZ11lkn/fr1y5gxY+qU5z6/31JLLZWkbsHuvx/p2KJFi5oC3OzZs+ebb1EYNWpU3n///Rx00EGZM2dOne133nlnzXs/765Hn7/GRx99tNb3PEl23XXXFEWRUaNG1TleQ++41rJly+y66665+eab8/zzz9fZ/kWf2T59+mT//ffPXXfdlUsuuaTO9ksvvTT33HNPDjzwwKywwgq1tu2+++555JFH8tvf/jbvvfderUcCJsluu+2WuXPn5rTTTqtz3Dlz5tT5vjcHX8VrAgAAgCWZO1cBAAAAFXfCCSfkd7/7XV566aUMHDiw1raDDjooZ555Zg466KCst956mTBhQl5++eVFlqV79+7ZeOONs//+++edd97JmDFj0r9//xx88MFJ/lPiueKKK7L99ttn4MCB2X///bP88svnrbfeyr333pvOnTvntttuW6hzjxw5Mn/4wx+y/fbb58gjj0z37t1z9dVXZ8qUKbn55pvTosXC/X9y11xzTbbaaqvsvPPO+cEPfpBNNtkks2fPzi233JL77rsvu+++e4499tg6+/Xv3z8bb7xxDjvssMyePTtjxozJ0ksvnZ/+9KfzPdcqq6yS008/Pccff3xef/317LLLLunUqVOmTJmSW2+9NYccckh+8pOfpEWLFrnkkkuy4447ZvDgwdl///3Tu3fvvPjii/n73/+ecePGJflPmS1JjjzyyAwdOjQtW7bMsGHDctBBB2XatGnZcssts8IKK+SNN97IhRdemMGDB9c8xm5x2X333fPcc8/ljDPOyFNPPZU99tgjffv2zfvvv5+xY8fm7rvvznXXXZfkP3cSu+WWW/Kd73wnO+ywQ6ZMmZJLL700a665Zq2y3hZbbJG99947F1xwQSZPnpztttsu1dXV+dvf/pYtttgiRxxxRIMynnnmmbn33nuzwQYb5OCDD86aa66ZadOm5cknn8xdd92VadOmLXD/X/3qV3nxxRdz+OGHZ+zYsTV3qBo3blz+/Oc/Z7PNNsu5555bZ7/ddtstP/nJT/KTn/wk3bt3r3PnrM022yyHHnpoRo8enaeffjrbbrttWrduncmTJ+fGG2/M+eefn+9973sNutZK+ypeEwAAACzJlKsAAACAiuvfv3/22muvXH311XW2nXTSSfn3v/+dm266KTfccEO233773HHHHenRo8ciyfKzn/0szz77bEaPHp2PPvooW221VS6++OJ06NChZs7mm2+ehx9+OKeddlr+53/+Jx9//HF69eqVDTbYIIceeuhCn7tnz5556KGHctxxx+XCCy/Mp59+mkGDBuW2227LDjvssNDH7d27dx577LGce+65ufHGG3PzzTenVatWGTRoUK666qrss88+ZR+/t88++6RFixYZM2ZM3n333ay//vr5n//5n/Tu3XuB5xs5cmRWXXXV/OpXv6q581KfPn2y7bbbZqeddqqZN3To0Nx7770ZNWpUzj333FRXV2eVVVapKbIl/7kT1o9+9KNcf/31+f3vf5+iKDJs2LDstddeufzyy3PxxRdn+vTp6dWrV3bfffeccsopC11C+zJOP/30bLnllrngggtyySWXZNq0aenWrVu++c1v5s9//nPNde+3336ZOnVqLrvssowbNy5rrrlmfv/73+fGG2/MfffdV+uYV155ZQYNGpTf/OY3OfbYY9OlS5est9562XDDDRucr2fPnnnsscdy6qmn5pZbbsnFF1+cpZdeOgMHDiz7SM7/1rFjx9x99925+OKL8/vf/z7HHntsiqLI6quvnjFjxuTwww9P69at6+y3wgorZMMNN8yDDz6Ygw46qOycSy+9NOuuu24uu+yy/OxnP0urVq2y0korZa+99qrzqMrm4qt4TQAAALCkKhUNvY84AAAAAPx/J554YkaPHl32cXgAAAAA0Nwt/v+NDwAAAICvjH/9619ZZpllKh0DAAAAABYJjwUEAAAAoMFee+213Hrrrbnxxhvz7W9/u9JxAAAAAGCRcOcqAAAAABpswoQJGTVq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STJjxowce+yxOe+883L44YcvkpAAAAAAAAAAAACLW4v6TvzrX/+aI488MkcccUTeeuutTJ8+PdOnT89bb72Vww8/PEcddVRuv/32RZkVAAAAAAAAAABgsan3nat++ctfZuTIkTn99NNrjffu3TvnnXdeOnTokLPPPjvf+ta3Gj0kAAAAAAAAAADA4lbvO1c9+eST2Xvvvee7fe+9986TTz7ZKKEAAAAAAAAAAAAqrd7lqrlz56Z169bz3d66devMnTu3UUIBAAAAAAAAAABUWr3LVQMHDsyf//zn+W7/05/+lIEDBzZKKJYwRaUDAAAAAAAAAABAXa3qO3H48OE57LDD0rZt2xxyyCFp1eo/u86ZMyeXXXZZfv7zn+fiiy9eZEEBAAAAAAAAAAAWp3qXq/bdd98899xzOeKII3L88cdnlVVWSVEUee211/Lxxx/nyCOPzH777bcIo/JVVypVOgEAAAAAAAAAAPyfeperkuScc87J9773vfzhD3/I5MmTkySbbbZZhg0blm9+85uLJCAAAAAAAAAAAEAlNKhclSTf/OY3FakAAAAAAAAAAICvvBaVDgAAAAAAAAAAANAUKVcBAAAAAAAAAACUoVwFAAAAAAAAAABQhnIVAAAAAAAAAABAGcpVAAAAAAAAAAAAZbSqz6Svf/3rKZVK9Trgk08++aUCAQAAAAAAAAAANAX1KlftsssuNV9/+umnufjii7PmmmtmyJAhSZJHHnkkf//733P44YcvkpB8tRUpKh0BAAAAAAAAAADqqFe56uSTT675+qCDDsqRRx6Z0047rc6cN998s3HTAQAAAAAAAAAAVEiLhu5w4403Zp999qkzvtdee+Xmm29ulFAsmer34EkAAAAAAAAAAFg8Glyuat++fR588ME64w8++GDatWvXKKEAAAAAAAAAAAAqrV6PBfy8ESNG5LDDDsuTTz6Z9ddfP0ny6KOP5re//W1OPPHERg8IAAAAAAAAAABQCQ0uV40cOTIrr7xyzj///Pz+979Pkqyxxhq58sors9tuuzV6QAAAAAAAAAAAgEpocLkqSXbbbTdFKgAAAAAAAAAA4CutxcLsNH369FxxxRX52c9+lmnTpiVJnnzyybz11luNGg4AAAAAAAAAAKBSGnznqmeffTZbb711unTpktdffz0HHXRQunfvnltuuSX/+Mc/cs011yyKnAAAAAAAAAAAAItVg+9cdcwxx2S//fbL5MmT065du5rxb33rW5kwYUKjhgMAAAAAAAAAAKiUBperHn/88Rx66KF1xpdffvlMnTq1UUJ93ltvvZW99torSy+9dNq3b5+11147EydObPTzAAAAAAAAAAAAfF6DHwvYtm3bzJgxo874yy+/nGWXXbZRQs3zwQcfZKONNsoWW2yRO+64I8suu2wmT56cbt26Nep5qKyiqHQCAAAAAAAAAACoq8Hlqp122imnnnpqbrjhhiRJqVTKP/7xjxx33HHZddddGzXcWWedlT59+uTKK6+sGevXr1+jngMAAAAAAAAAAKCcBperzj333Hzve99Ljx498sknn2SzzTbL1KlTM2TIkJxxxhmNGu5///d/M3To0Hz/+9/P/fffn+WXXz6HH354Dj744PnuM3v27MyePbvm9by7bFVVVaWqqqpR89E45s6dW/O17xEANH3z1mvrNgA0D9ZuAGherN0A0LxYu6H5qu/PbakoFu6hbA888ECeffbZfPzxx1lnnXWy9dZbL8xhFqhdu3ZJkmOOOSbf//738/jjj+eoo47KpZdemn333bfsPqecckpGjRpVZ/y6665Lhw4dGj0jX97D75Ry/Wsts1a36hy8enWl4wAAAAAAAAAA8BU3a9as/OAHP8iHH36Yzp07z3feQperFoc2bdpkvfXWy0MPPVQzduSRR+bxxx/Pww8/XHafcneu6tOnT957770FvhFUzg0T/5kT/vxC1upWnRt+tFVat25d6UgAwAJUVVVl/Pjx2WabbazbANAMWLsBoHmxdgNA82LthuZrxowZWWaZZb6wXNXgxwImyd13352777477777bqqra99p6Le//e3CHLKs3r17Z80116w1tsYaa+Tmm2+e7z5t27ZN27Zt64y3bt3aL7ImqmXLljVf+z4BQPNh3QaA5sXaDQDNi7UbAJoXazc0P/X9mW1wuWrUqFE59dRTs95666V3794plUoNDldfG220UV566aVaYy+//HL69u27yM4JAAAAAAAAAACQLES56tJLL81VV12Vvffee1HkqeXoo4/OhhtumF/84hfZbbfd8thjj+Xyyy/P5ZdfvsjPDQAAAAAAAAAALNlaNHSHzz77LBtuuOGiyFLHN77xjdx66635wx/+kLXWWiunnXZaxowZkz333HOxnB8AAAAAAAAAAFhyNbhcddBBB+W6665bFFnK+va3v53nnnsun376aSZNmpSDDz54sZ0bAAAAAAAAAABYctXrsYDHHHNMzdfV1dW5/PLLc9ddd2XQoEFp3bp1rbnnnXde4yYEAAAAAAAAAACogHqVq5566qlarwcPHpwkef7552uNl0qlxknFEqWodAAAAAAAAAAAACijXuWqe++9d1HnAAAAAAAAAAAAaFJaNHSHDz/8MNOmTaszPm3atMyYMaNRQrFkct8zAAAAAAAAAACakgaXq4YNG5brr7++zvgNN9yQYcOGNUooAAAAAAAAAACASmtwuerRRx/NFltsUWd88803z6OPPtoooQAAAAAAAAAAACqtweWq2bNnZ86cOXXGq6qq8sknnzRKKAAAAAAAAAAAgEprcLlq/fXXz+WXX15n/NJLL826667bKKEAAAAAAAAAAAAqrVVDdzj99NOz9dZb55lnnslWW22VJLn77rvz+OOP584772z0gAAAAAAAAAAAAJXQ4DtXbbTRRnn44YfTp0+f3HDDDbntttvSv3//PPvss9lkk00WRUYAAAAAAAAAAIDFrsF3rkqSwYMH59prr23sLAAAAAAAAAAAAE3GQpWr5vn000/z2Wef1Rrr3LnzlwoEAAAAAAAAAADQFDT4sYCzZs3KEUcckR49emSppZZKt27dav2DhiqKSicAAAAAAAAAAIC6GlyuOvbYY3PPPffkkksuSdu2bXPFFVdk1KhRWW655XLNNdcsiowAAAAAAAAAAACLXYMfC3jbbbflmmuuyeabb579998/m2yySfr375++ffvm2muvzZ577rkocgIAAAAAAAAAACxWDb5z1bRp07LyyisnSTp37pxp06YlSTbeeONMmDChcdMBAAAAAAAAAABUSIPLVSuvvHKmTJmSJFl99dVzww03JPnPHa26du3aqOEAAAAAAAAAAAAqpcHlqv333z/PPPNMkmTkyJG56KKL0q5duxx99NE59thjGz0gAAAAAAAAAABAJbRq6A5HH310zddbb711XnzxxTzxxBPp379/Bg0a1KjhAAAAAAAAAAAAKqXB5ar/1rdv3/Tt27cxsgAAAAAAAAAAADQZ9X4s4D333JM111wzM2bMqLPtww8/zMCBA/O3v/2tUcMBAAAAAAAAAABUSr3LVWPGjMnBBx+czp0719nWpUuXHHrooTnvvPMaNRwAAAAAAAAAAECl1Ltc9cwzz2S77bab7/Ztt902TzzxRKOEAgAAAAAAAAAAqLR6l6veeeedtG7der7bW7VqlX//+9+NEoolS5Gi0hEAAAAAAAAAAKCOeperll9++Tz//PPz3f7ss8+md+/ejRIKAAAAAAAAAACg0updrvrWt76VE088MZ9++mmdbZ988klOPvnkfPvb327UcCxZSqVKJwAAAAAAAAAAgP/Tqr4Tf/7zn+eWW27JqquumiOOOCKrrbZakuTFF1/MRRddlLlz5+aEE05YZEEBAAAAAAAAAAAWp3qXq3r27JmHHnoohx12WI4//vgURZEkKZVKGTp0aC666KL07NlzkQUFAAAAAAAAAABYnOpdrkqSvn375vbbb88HH3yQV155JUVRZMCAAenWrduiygcAAAAAAAAAAFARDSpXzdOtW7d84xvfaOwsAAAAAAAAAAAATUaLSgcAAAAAAAAAAABoipSrAAAAAAAAAAAAylCuAgAAAAAAAAAAKKNe5ap11lknH3zwQZLk1FNPzaxZsxZpKAAAAAAAAAAAgEqrV7lq0qRJmTlzZpJk1KhR+fjjjxdpKJYsRVHpBAAAAAAAAAAAUFer+kwaPHhw9t9//2y88cYpiiLnnHNOOnbsWHbuSSed1KgBAQAAAAAAAAAAKqFe5aqrrroqJ598cv7yl7+kVCrljjvuSKtWdXctlUrKVQAAAAAAAAAAwFdCvcpVq622Wq6//vokSYsWLXL33XenR48eizQYAAAAAAAAAABAJdWrXPV51dXViyIHAAAAAAAAAABAk9LgclWSvPrqqxkzZkwmTZqUJFlzzTVz1FFHZZVVVmnUcAAAAAAAAAAAAJXSoqE7jBs3LmuuuWYee+yxDBo0KIMGDcqjjz6agQMHZvz48YsiIwAAAAAAAAAAwGLX4DtXjRw5MkcffXTOPPPMOuPHHXdcttlmm0YLBwAAAAAAAAAAUCkNvnPVpEmTcuCBB9YZP+CAA/LCCy80SigAAAAAAAAAAIBKa3C5atlll83TTz9dZ/zpp59Ojx49GiMTAAAAAAAAAABAxTX4sYAHH3xwDjnkkLz22mvZcMMNkyQPPvhgzjrrrBxzzDGNHhAAAAAAAAAAAKASGlyuOvHEE9OpU6ece+65Of7445Mkyy23XE455ZQceeSRjR6Qr76i0gEAAAAAAAAAAKCMBperSqVSjj766Bx99NH56KOPkiSdOnVq9GAAAAAAAAAAAACV1OBy1ecpVdGYSpUOAAAAAAAAAAAAn9Oi0gEAAAAAAAAAAACaIuUqAAAAAAAAAACAMpSrAAAAAAAAAAAAymhQuaqqqipbbbVVJk+evKjyAAAAAAAAAAAANAkNKle1bt06zz777KLKAgAAAAAAAAAA0GQ0+LGAe+21V37zm98siiwAAAAAAAAAAABNRquG7jBnzpz89re/zV133ZV11103Sy21VK3t5513XqOFAwAAAAAAAAAAqJQGl6uef/75rLPOOkmSl19+uda2UqnUOKkAAAAAAAAAAAAqrMHlqnvvvXdR5GBJVhSVTgAAAAAAAAAAAHW0WNgdX3nllYwbNy6ffPJJkqRQkAEAAAAAAAAAAL5CGlyuev/997PVVltl1VVXzbe+9a3861//SpIceOCB+fGPf9zoAVlyeKgkAAAAAAAAAABNSYPLVUcffXRat26df/zjH+nQoUPN+O67756xY8c2ajgAAAAAAAAAAIBKadXQHe68886MGzcuK6ywQq3xAQMG5I033mi0YAAAAAAAAAAAAJXU4DtXzZw5s9Ydq+aZNm1a2rZt2yihAAAAAAAAAAAAKq3B5apNNtkk11xzTc3rUqmU6urqnH322dliiy0aNRwAAAAAAAAAAEClNPixgGeffXa22mqrTJw4MZ999ll++tOf5u9//3umTZuWBx98cFFkBAAAAAAAAAAAWOwafOeqtdZaKy+//HI23njj7Lzzzpk5c2a++93v5qmnnsoqq6yyKDICAAAAAAAAAAAsdg2+c1WSdOnSJSeccEJjZwEAAAAAAAAAAGgyFqpc9cEHH+Q3v/lNJk2alCRZc801s//++6d79+6NGg4AAAAAAAAAAKBSGvxYwAkTJmSllVbKBRdckA8++CAffPBBLrjggvTr1y8TJkxYFBlrnHnmmSmVShkxYsQiPQ+LV1HpAAAAAAAAAAAAUEaD71w1fPjw7L777rnkkkvSsmXLJMncuXNz+OGHZ/jw4XnuuecaPWSSPP7447nssssyaNCgRXJ8moBSpQMAAAAAAAAAAMD/afCdq1555ZX8+Mc/rilWJUnLli1zzDHH5JVXXmnUcPN8/PHH2XPPPfPrX/863bp1WyTnAAAAAAAAAAAA+LwG37lqnXXWyaRJk7LaaqvVGp80aVK+9rWvNVqwzxs+fHh22GGHbL311jn99NMXOHf27NmZPXt2zesZM2YkSaqqqlJVVbVI8vHlzJ07t+Zr3yMAaPrmrdfWbQBoHqzdANC8WLsBoHmxdkPzVd+f23qVq5599tmar4888sgcddRReeWVV/LNb34zSfLII4/koosuyplnnrkQURfs+uuvz5NPPpnHH3+8XvNHjx6dUaNG1Rm/884706FDh8aORyP4+9RSkv/cCW38+PGVDQMA1Jt1GwCaF2s3ADQv1m4AaF6s3dD8zJo1q17zSkVRFF80qUWLFimVSvmiqaVSqdZdiL6sN998M+utt17Gjx+fQYMGJUk233zzDB48OGPGjCm7T7k7V/Xp0yfvvfdeOnfu3GjZaDy/f/QfGfWXFzN46epcN3yrtG7dutKRAIAFqKqqyvjx47PNNttYtwGgGbB2A0DzYu0GgObF2g3N14wZM7LMMsvkww8/XGCnqF53rpoyZUqjBWuIJ554Iu+++27WWWedmrG5c+dmwoQJ+Z//+Z/Mnj07LVu2rLVP27Zt07Zt2zrHat26tV9kTdTnv4e+TwDQfFi3AaB5sXYDQPNi7QaA5sXaDc1PfX9m61Wu6tu375cKs7C22mqrPPfcc7XG9t9//6y++uo57rjj6hSrAAAAAAAAAAAAGku9ylX/7e23384DDzyQd999N9XV1bW2HXnkkY0SLEk6deqUtdZaq9bYUkstlaWXXrrOOAAAAAAAAAAAQGNqcLnqqquuyqGHHpo2bdpk6aWXTqlUqtlWKpUatVwFAAAAAAAAAABQKQ0uV5144ok56aSTcvzxx6dFixaLItMC3XfffYv9nAAAAAAAAAAAwJKnwe2oWbNmZdiwYRUpVvHVVBSVTgAAAAAAAAAAAHU1uCF14IEH5sYbb1wUWVjClb54CgAAAAAAAAAALDYNfizg6NGj8+1vfztjx47N2muvndatW9faft555zVaOAAAAAAAAAAAgEpZqHLVuHHjstpqqyVJSqX/u9/Q578GAAAAAAAAAABozhpcrjr33HPz29/+Nvvtt98iiAMAAAAAAAAAANA0tGjoDm3bts1GG220KLIAAAAAAAAAAAA0GQ0uVx111FG58MILF0UWAAAAAAAAAACAJqPBjwV87LHHcs899+Qvf/lLBg4cmNatW9fafssttzRaOAAAAAAAAAAAgEppcLmqa9eu+e53v7sosgAAAAAAAAAAADQZDS5XXXnllYsiBwAAAAAAAAAAQJPSotIBAAAAAAAAAAAAmqIG37mqX79+KZVK893+2muvfalALHmKoqh0BAAAAAAAAAAAqKPB5aoRI0bUel1VVZWnnnoqY8eOzbHHHttYuQAAAAAAAAAAACqqweWqo446quz4RRddlIkTJ37pQAAAAAAAAAAAAE1Bi8Y60Pbbb5+bb765sQ4HAAAAAAAAAABQUY1WrrrpppvSvXv3xjocAAAAAAAAAABARTX4sYBf//rXUyqVal4XRZGpU6fm3//+dy6++OJGDQcAAAAAAAAAAFApDS5X7bLLLrVet2jRIssuu2w233zzrL766o2VCwAAAAAAAAAAoKIaXK46+eSTF0UOAAAAAAAAAACAJqVFpQMAAAAAAAAAAAA0RfW+c1WLFi1SKpUWOKdUKmXOnDlfOhQAAAAAAAAAAECl1btcdeutt85328MPP5wLLrgg1dXVjRIKAAAAAAAAAACg0updrtp5553rjL300ksZOXJkbrvttuy555459dRTGzUcS4ai0gEAAAAAAAAAAKCMFguz09tvv52DDz44a6+9dubMmZOnn346V199dfr27dvY+ViCLPihkwAAAAAAAAAAsHg1qFz14Ycf5rjjjkv//v3z97//PXfffXduu+22rLXWWosqHwAAAAAAAAAAQEXU+7GAZ599ds4666z06tUrf/jDH8o+JhAAAAAAAAAAAOCrot7lqpEjR6Z9+/bp379/rr766lx99dVl591yyy2NFg4AAAAAAAAAAKBS6l2u2meffVIqlRZlFgAAAAAAAAAAgCaj3uWqq666ahHGAAAAAAAAAAAAaFpaVDoAAAAAAAAAAABAU6RcBQAAAAAAAAAAUIZyFQAAAAAAAAAAQBnKVQAAAAAAAAAAAGUoV1FxRVHpBAAAAAAAAAAAUJdyFQAAAAAAAAAAQBnKVQAAAAAAAAAAAGUoVwEAAAAAAAAAAJShXAUAAAAAAAAAAFCGchUAAAAAAAAAAEAZylUAAAAAAAAAAABlKFcBAAAAAAAAAACUoVwFAAAAAAAAAABQhnIVAAAAAAAAAABAGcpVAAAAAAAAAAAAZShXUXFFpQMAAAAAAAAAAEAZylU0GaVSpRMAAAAAAAAAAMD/Ua4CAAAAAAAAAAAoQ7kKAAAAAAAAAACgDOUqAAAAAAAAAACAMpSrAAAAAAAAAAAAylCuAgAAAAAAAAAAKEO5CgAAAAAAAAAAoAzlKgAAAAAAAAAAgDKUqwAAAAAAAAAAAMpQrgIAAAAAAAAAAChDuYqKK4qi0hEAAAAAAAAAAKAO5SoAAAAAAAAAAIAylKsAAAAAAAAAAADKUK4CAAAAAAAAAAAoQ7kKAAAAAAAAAACgDOUqAAAAAAAAAACAMpSrAAAAAAAAAAAAylCuAgAAAAAAAAAAKEO5CgAAAAAAAAAAoAzlKgAAAAAAAAAAgDKUqwAAAAAAAAAAAMpo0uWq0aNH5xvf+EY6deqUHj16ZJdddslLL71U6VgAAAAAAAAAAMASoEmXq+6///4MHz48jzzySMaPH5+qqqpsu+22mTlzZqWjsQiUKh0AAAAAAAAAAAA+p1WlAyzI2LFja72+6qqr0qNHjzzxxBPZdNNNK5QKAAAAAAAAAABYEjTpctV/+/DDD5Mk3bt3n++c2bNnZ/bs2TWvZ8yYkSSpqqpKVVXVog3IQpk7d27N175HAND0zVuvrdsA0DxYuwGgebF2A0DzYu2G5qu+P7eloiiKRZylUVRXV2ennXbK9OnT88ADD8x33imnnJJRo0bVGb/uuuvSoUOHRRmRhXTv26X86Y2WWW+Z6uw9oLrScQAAAAAAAAAA+IqbNWtWfvCDH+TDDz9M586d5zuv2ZSrDjvssNxxxx154IEHssIKK8x3Xrk7V/Xp0yfvvffeAt8IKue3D76e0WNfznrLVOeaw7dK69atKx0JAFiAqqqqjB8/Pttss411GwCaAWs3ADQv1m4AaF6s3dB8zZgxI8sss8wXlquaxWMBjzjiiPzlL3/JhAkTFlisSpK2bdumbdu2dcZbt27tF1kT1bJly5qvfZ8AoPmwbgNA82LtBoDmxdoNAM2LtRuan/r+zDbpclVRFPnRj36UW2+9Nffdd1/69etX6UgAAAAAAAAAAMASokmXq4YPH57rrrsuf/7zn9OpU6dMnTo1SdKlS5e0b9++wukAAAAAAAAAAICvshaVDrAgl1xyST788MNsvvnm6d27d82/P/7xj5WOBgAAAAAAAAAAfMU16TtXFUVR6QgAAAAAAAAAAMASqknfuYolgw4dAAAAAAAAAABNkXIVTUap0gEAAAAAAAAAAOBzlKsAAAAAAAAAAADKUK4CAAAAAAAAAAAoQ7kKAAAAAAAAAACgDOUqAAAAAAAAAACAMpSrAAAAAAAAAAAAylCuAgAAAAAAAAAAKEO5CgAAAAAAAAAAoAzlKgAAAAAAAAAAgDKUqwAAAAAAAAAAAMpQrqLiihSVjgAAAAAAAAAAAHUoV9F0lCodAAAAAAAAAAAA/o9yFQAAAAAAAAAAQBnKVQAAAAAAAAAAAGUoVwEAAAAAAAAAAJShXAUAAAAAAAAAAFBGq0oHAAAAAAAAAACg+SuKInPmzMncuXMrHQXSsmXLtGrVKqVS6UsdR7kKAAAAAAAAAIAv5bPPPsu//vWvzJo1q9JRoEaHDh3Su3fvtGnTZqGPoVwFAAAAAAAAAMBCq66uzpQpU9KyZcsst9xyadOmzZe+WxB8GUVR5LPPPsu///3vTJkyJQMGDEiLFi0W6ljKVQAAAAAAAAAALLTPPvss1dXV6dOnTzp06FDpOJAkad++fVq3bp033ngjn332Wdq1a7dQx1m4ShYAAAAAAAAAAHzOwt4ZCBaVxvhM+lRTcUVR6QQAAAAAAAAAAFCXchVNhqetAgAAAAAAAADQlChXAQAAAAAAAADAAqy00koZM2bMl56zuDXFTPPTVLMqVwEAAAAAAAAAsER68803c8ABB2S55ZZLmzZt0rdv3xx11FF5//33G3ysxx9/PIccckijZWuMslFjZXrllVey//77Z4UVVkjbtm3Tr1+/7LHHHpk4ceKXPnZTp1wFAAAAAAAAAMAS57XXXst6662XyZMn5w9/+ENeeeWVXHrppbn77rszZMiQTJs2rUHHW3bZZdOhQ4dFlHbhNEamiRMnZt11183LL7+cyy67LC+88EJuvfXWrL766vnxj3/cSEmbLuUqAAAAAAAAAAAaTVEUmfXZnIr8K4qi3jmHDx+eNm3a5M4778xmm22WFVdcMdtvv33uuuuuvPXWWznhhBNqzf/oo4+yxx57ZKmllsryyy+fiy66qNb2/77T1PTp03PQQQdl2WWXTefOnbPlllvmmWeeqbXPbbfdlm984xtp165dlllmmXznO99Jkmy++eZ54403cvTRR6dUKqVUKiVJ3njjjey4447p1q1bllpqqQwcODC33377fK/xvzOVSqVcccUV+c53vpMOHTpkwIAB+d///d/57l8URfbbb78MGDAgf/vb37LDDjtklVVWyeDBg3PyySfnz3/+c83c4447Lquuumo6dOiQlVdeOSeeeGKqqqrqdb3zzJo1KwcccEA6deqUFVdcMZdffnmt7W+++WZ22223dO3aNd27d8/OO++c119/fb75G0OrRXp0AAAAAAAAAACWKJ9Uzc2aJ42ryLlfOHVoOrT54jrMtGnTMm7cuJxxxhlp3759rW29evXKnnvumT/+8Y+5+OKLa4pNv/zlL/Ozn/0so0aNyrhx43LUUUdl1VVXzTbbbFP2HN///vfTvn373HHHHenSpUsuu+yybLXVVnn55ZfTvXv3/PWvf813vvOdnHDCCbnmmmvy2Wef1RSlbrnllnzta1/LIYcckoMPPrjmmMOHD89nn32WCRMmZKmllsoLL7yQjh07Nug9GjVqVM4+++z88pe/zIUXXpg999wzb7zxRrp3715n7tNPP52///3vue6669KiRd17OHXt2rXm606dOuWqq67Kcsstl+eeey4HH3xwOnXqlJ/+9KdJssDrnefcc8/Naaedlp/97Ge56aabcthhh2WzzTbLaqutlqqqqgwdOjRDhgzJ3/72t7Rq1Sqnn356tttuuzz77LNp06ZNg96H+lKuAgAAAAAAAABgiTJ58uQURZE11lij7PY11lgjH3zwQf7973+nR48eSZKNNtooI0eOTJKsuuqqefDBB/OrX/2qbLnqgQceyGOPPZZ33303bdu2TZKcc845+dOf/pSbbrophxxySM4444wMGzYso0aNqtnva1/7WpKke/fuadmyZTp16pRevXrVbP/HP/6RXXfdNWuvvXaSZOWVV27wte+3337ZY489kiS/+MUvcsEFF+Sxxx7LdtttV/Z9SpLVV1/9C4/785//vObrlVZaKT/5yU9y/fXX15SrFnS983zrW9/K4YcfnuQ/d8L61a9+lXvvvTerrbZa/vjHP6a6ujpXXHFFTeHtyiuvTNeuXXPfffdl2223bcjbUG/KVQAAAAAAAAAANJr2rVvmhVOHVuzcDdGQxwgOGTKkzuvPP3Lv85555pl8/PHHWXrppWuNf/LJJ3n11VeT/OeuUJ+/K1V9HHnkkTnssMNy5513Zuutt86uu+6aQYMGNegYn5+/1FJLpXPnznn33XfLzm3I+/PHP/4xF1xwQV599dV8/PHHmTNnTjp37lyzvT7X+/lspVIpvXr1qsn2zDPP5JVXXkmnTp1q7fPpp5/WvKeLgnIVAAAAAAAAAACNplQq1evRfJXUv3//lEqlTJo0Kd/5znfqbJ80aVK6deuWZZdddqGO//HHH6d3796577776myb9yi9/34cYX0cdNBBGTp0aP7617/mzjvvzOjRo3PuuefmRz/6Ub2P0bp161qvS6VSqqury85dddVVkyQvvvhivv71r8/3mA8//HD23HPPjBo1KkOHDk2XLl1y/fXX59xzz62ZU5/rXVC2jz/+OOuuu26uvfbaOvst7PepPuo+DBEAAAAAAAAAAL7Cll566WyzzTa5+OKL88knn9TaNnXq1Fx77bXZfffdax4/lySPPPJIrXmPPPLIfB8ruM4662Tq1Klp1apV+vfvX+vfMsssk+Q/d2m6++6755uxTZs2mTt3bp3xPn365Ic//GFuueWW/PjHP86vf/3rel93Qw0ePDhrrrlmzj333LIFrOnTpydJHnroofTt2zcnnHBC1ltvvQwYMCBvvPFGrblfdL1fZJ111snkyZPTo0ePOu9ply5dFvq4X0S5iorbYVDvXL3futlyufItSAAAAAAAAACAxvY///M/mT17doYOHZoJEybkzTffzNixY7PNNttk+eWXzxlnnFFr/oMPPpizzz47L7/8ci666KLceOONOeqoo8oee+utt86QIUOyyy675M4778zrr7+ehx56KCeccEImTpyYJDn55JPzhz/8ISeffHImTZqU5557LmeddVbNMVZaaaVMmDAhb731Vt57770kyYgRIzJu3LhMmTIlTz75ZO699975FrwaQ6lUypVXXpmXX345m2yySW6//fa89tprefbZZ3PGGWdk5513TpIMGDAg//jHP3L99dfn1VdfzQUXXJBbb7211rG+6Hq/yJ577pllllkmO++8c/72t79lypQpue+++3LkkUfmn//8Z6Ne9+cpV1FxK3TrkA1XWTq9O1Q6CQAAAAAAAACwpBgwYEAmTpyYlVdeObvttltWWWWVHHLIIdliiy3y8MMPp3v37rXm//jHP87EiRPz9a9/PaeffnrOO++8DB06tOyxS6VSbr/99my66abZf//9s+qqq2bYsGF544030rNnzyTJ5ptvnhtvvDH/+7//m8GDB2fLLbfMY489VnOMU089Na+//npWWWWVmsfezZ07N8OHD88aa6yR7bbbLquuumouvvjiRfQO/cf666+fiRMnpn///jn44IOzxhprZKeddsrf//73jBkzJkmy00475eijj84RRxyRwYMH56GHHsqJJ55Y6zhfdL1fpEOHDpkwYUJWXHHFfPe7380aa6yRAw88MJ9++mk6d+7cmJdcS6koimKRHb0JmDFjRrp06ZIPP/xwkb6RfDlVVVW5/fbb861vfavO8zMBgKbFug0AzYu1GwCaF2s3ADQv1u7/+PTTTzNlypT069cv7dq1q3Sciundu3dOO+20HHTQQZWOwv+3oM9mfTtFrRZ1SAAAAAAAAAAA+KqaNWtWHnzwwbzzzjsZOHBgpePQyDwWEAAAAAAAAAAAFtLll1+eYcOGZcSIERkyZEil49DI3LkKAAAAAAAAAAAW0ogRIzJixIhKx2ARcecqAAAAAAAAAACAMpSrAAAAAAAAAAD40oqiqHQEqKUxPpPKVQAAAAAAAAAALLTWrVsnSWbNmlXhJFDbvM/kvM/owmjVWGEAAAAAAAAAAFjytGzZMl27ds27776bJOnQoUNKpVKFU7EkK4ois2bNyrvvvpuuXbumZcuWC30s5SoAAAAAAAAAAL6UXr16JUlNwQqagq5du9Z8NheWchUAAAAAAAAAAF9KqVRK796906NHj1RVVVU6DqR169Zf6o5V8yhXAQAAAAAAAADQKFq2bNkohRZoKlpUOgAAAAAAAAAAAEBTpFwFAAAAAAAAAABQhnIVAAAAAAAAAABAGa0qHWBRK4oiSTJjxowKJ2FBqqqqMmvWrMyYMSOtW7eudBwAYAGs2wDQvFi7AaB5sXYDQPNi7Ybma16XaF63aH6+8uWqjz76KEnSp0+fCicBAAAAAAAAAACako8++ihdunSZ7/ZS8UX1q2auuro6b7/9djp16pRSqVTpOMzHjBkz0qdPn7z55pvp3LlzpeMAAAtg3QaA5sXaDQDNi7UbAJoXazc0X0VR5KOPPspyyy2XFi1azHfeV/7OVS1atMgKK6xQ6RjUU+fOnS04ANBMWLcBoHmxdgNA82LtBoDmxdoNzdOC7lg1z/xrVwAAAAAAAAAAAEsw5SoAAAAAAAAAAIAylKtoEtq2bZuTTz45bdu2rXQUAOALWLcBoHmxdgNA82LtBoDmxdoNX32loiiKSocAAAAAAAAAAABoaty5CgAAAAAAAAAAoAzlKgAAAAAAAAAAgDKUqwAAAAAAAAAAAMpQrgIAAAAAAAAAAChDuYqKu+iii7LSSiulXbt22WCDDfLYY49VOhIAfOVMmDAhO+64Y5ZbbrmUSqX86U9/qrW9KIqcdNJJ6d27d9q3b5+tt946kydPrjVn2rRp2XPPPdO5c+d07do1Bx54YD7++ONac5599tlssskmadeuXfr06ZOzzz67TpYbb7wxq6++etq1a5e11147t99+e6NfLwA0Z6NHj843vvGNdOrUKT169Mguu+ySl156qdacTz/9NMOHD8/SSy+djh07Ztddd80777xTa84//vGP7LDDDunQoUN69OiRY489NnPmzKk157777ss666yTtm3bpn///rnqqqvq5PF3OwAs2CWXXJJBgwalc+fO6dy5c4YMGZI77rijZrt1GwCatjPPPDOlUikjRoyoGbN+A5+nXEVF/fGPf8wxxxyTk08+OU8++WS+9rWvZejQoXn33XcrHQ0AvlJmzpyZr33ta7nooovKbj/77LNzwQUX5NJLL82jjz6apZZaKkOHDs2nn35aM2fPPffM3//+94wfPz5/+ctfMmHChBxyyCE122fMmJFtt902ffv2zRNPPJFf/vKXOeWUU3L55ZfXzHnooYeyxx575MADD8xTTz2VXXbZJbvsskuef/75RXfxANDM3H///Rk+fHgeeeSRjB8/PlVVVdl2220zc+bMmjlHH310brvtttx44425//778/bbb+e73/1uzfa5c+dmhx12yGeffZaHHnooV199da666qqcdNJJNXOmTJmSHXbYIVtssUWefvrpjBgxIgcddFDGjRtXM8ff7QDwxVZYYYWceeaZeeKJJzJx4sRsueWW2XnnnfP3v/89iXUbAJqyxx9/PJdddlkGDRpUa9z6DdRSQAWtv/76xfDhw2tez507t1huueWK0aNHVzAVAHy1JSluvfXWmtfV1dVFr169il/+8pc1Y9OnTy/atm1b/OEPfyiKoiheeOGFIknx+OOP18y54447ilKpVLz11ltFURTFxRdfXHTr1q2YPXt2zZzjjjuuWG211Wpe77bbbsUOO+xQK88GG2xQHHrooY16jQDwVfLuu+8WSYr777+/KIr/rNOtW7cubrzxxpo5kyZNKpIUDz/8cFEURXH77bcXLVq0KKZOnVoz55JLLik6d+5cs1b/9Kc/LQYOHFjrXLvvvnsxdOjQmtf+bgeAhdOtW7fiiiuusG4DQBP20UcfFQMGDCjGjx9fbLbZZsVRRx1VFIW/u4G63LmKivnss8/yxBNPZOutt64Za9GiRbbeeus8/PDDFUwGAEuWKVOmZOrUqbXW5C5dumSDDTaoWZMffvjhdO3aNeutt17NnK233jotWrTIo48+WjNn0003TZs2bWrmDB06NC+99FI++OCDmjmfP8+8OdZ+AJi/Dz/8MEnSvXv3JMkTTzyRqqqqWmvq6quvnhVXXLHW2r322munZ8+eNXOGDh2aGTNm1NxF44vWZX+3A0DDzZ07N9dff31mzpyZIUOGWLcBoAkbPnx4dthhhzprrPUb+G+tKh2AJdd7772XuXPn1lpwkqRnz5558cUXK5QKAJY8U6dOTZKya/K8bVOnTk2PHj1qbW/VqlW6d+9ea06/fv3qHGPetm7dumXq1KkLPA8AUFt1dXVGjBiRjTbaKGuttVaS/6yrbdq0SdeuXWvN/e+1u9yaO2/bgubMmDEjn3zyST744AN/twNAPT333HMZMmRIPv3003Ts2DG33npr1lxzzTz99NPWbQBogq6//vo8+eSTefzxx+ts83c38N+UqwAAAACaqOHDh+f555/PAw88UOkoAMACrLbaann66afz4Ycf5qabbsq+++6b+++/v9KxAIAy3nzzzRx11FEZP3582rVrV+k4QDPgsYBUzDLLLJOWLVvmnXfeqTX+zjvvpFevXhVKBQBLnnnr7oLW5F69euXdd9+ttX3OnDmZNm1arTnljvH5c8xvjrUfAOo64ogj8pe//CX33ntvVlhhhZrxXr165bPPPsv06dNrzf/vtXth1+XOnTunffv2/m4HgAZo06ZN+vfvn3XXXTejR4/O1772tZx//vnWbQBogp544om8++67WWedddKqVau0atUq999/fy644IK0atUqPXv2tH4DtShXUTFt2rTJuuuum7vvvrtmrLq6OnfffXeGDBlSwWQAsGTp169fevXqVWtNnjFjRh599NGaNXnIkCGZPn16nnjiiZo599xzT6qrq7PBBhvUzJkwYUKqqqpq5owfPz6rrbZaunXrVjPn8+eZN8faDwD/pyiKHHHEEbn11ltzzz331Hns7rrrrpvWrVvXWlNfeuml/OMf/6i1dj/33HO1ytHjx49P586ds+aaa9bMWdC67O92AFh41dXVmT17tnUbAJqgrbbaKs8991yefvrpmn/rrbde9txzz5qvrd9ALQVU0PXXX1+0bdu2uOqqq4oXXnihOOSQQ4quXbsWU6dOrXQ0APhK+eijj4qnnnqqeOqpp4okxXnnnVc89dRTxRtvvFEURVGceeaZRdeuXYs///nPxbPPPlvsvPPORb9+/YpPPvmk5hjbbbdd8fWvf7149NFHiwceeKAYMGBAsccee9Rsnz59etGzZ89i7733Lp5//vni+uuvLzp06FBcdtllNXMefPDBolWrVsU555xTTJo0qTj55JOL1q1bF88999ziezMAoIk77LDDii5duhT33Xdf8a9//avm36xZs2rm/PCHPyxWXHHF4p577ikmTpxYDBkypBgyZEjN9jlz5hRrrbVWse222xZPP/10MXbs2GLZZZctjj/++Jo5r732WtGhQ4fi2GOPLSZNmlRcdNFFRcuWLYuxY8fWzPF3OwB8sZEjRxb3339/MWXKlOLZZ58tRo4cWZRKpeLOO+8sisK6DQDNwWabbVYcddRRNa+t38DnKVdRcRdeeGGx4oorFm3atCnWX3/94pFHHql0JAD4yrn33nuLJHX+7bvvvkVRFEV1dXVx4oknFj179izatm1bbLXVVsVLL71U6xjvv/9+scceexQdO3YsOnfuXOy///7FRx99VGvOM888U2y88cZF27Zti+WXX74488wz62S54YYbilVXXbVo06ZNMXDgwOKvf/3rIrtuAGiOyq3ZSYorr7yyZs4nn3xSHH744UW3bt2KDh06FN/5zneKf/3rX7WO8/rrrxfbb7990b59+2KZZZYpfvzjHxdVVVW15tx7773F4MGDizZt2hQrr7xyrXPM4+92AFiwAw44oOjbt2/Rpk2bYtllly222mqrmmJVUVi3AaA5+O9ylfUb+LxSURRFZe6ZBQAAAAAAAAAA0HS1qHQAAAAAAAAAAACApki5CgAAAAAAAAAAoAzlKgAAAAAAAAAAgDKUqwAAAAAAAAAAAMpQrgIAAAAAAAAAAChDuQoAAAAAAAAAAKAM5SoAAAAAAAAAAIAylKsAAAAAAAAAAADKUK4CAAAAoEnbb7/9sssuu1Ts/HvvvXd+8Ytf1GvusGHDcu655y7iRAAAAAAsLqWiKIpKhwAAAABgyVQqlRa4/eSTT87RRx+doijStWvXxRPqc5555plsueWWeeONN9KxY8cvnP/8889n0003zZQpU9KlS5fFkBAAAACARUm5CgAAAICKmTp1as3Xf/zjH3PSSSflpZdeqhnr2LFjvUpNi8pBBx2UVq1a5dJLL633Pt/4xjey3377Zfjw4YswGQAAAACLg8cCAgAAAFAxvXr1qvnXpUuXlEqlWmMdO3as81jAzTffPD/60Y8yYsSIdOvWLT179syvf/3rzJw5M/vvv386deqU/v3754477qh1rueffz7bb799OnbsmJ49e2bvvffOe++9N99sc+fOzU033ZQdd9yx1vjFF1+cAQMGpF27dunZs2e+973v1dq+44475vrrr//ybw4AAAAAFadcBQAAAECzc/XVV2eZZZbJY489lh/96Ec57LDD8v3vfz8bbrhhnnzyyWy77bbZe++9M2vWrCTJ9OnTs+WWW+brX/96Jk6cmLFjx+add97JbrvtNt9zPPvss/nwww+z3nrr1YxNnDgxRx55ZE499dS89NJLGTt2bDbddNNa+63//9q7Y5eq2jgO4N+bSYkFQtckCQxJVMLsOgStkU71BzQ0CDoF0RqEY4NjBTU1CCIt1dBSQw7WUhAm1qAI2RgqDaElqE1duHTifYf32mt8PvDAOb/zPJzntx6+POfs2bx+/Trfv3+vT/MAAAAA7BrhKgAAAAD2nP7+/ty8eTNdXV25ceNGDh48mHK5nNHR0XR1dWVsbCyrq6uZm5tLkty9ezeVSiW3bt1KT09PKpVKHjx4kOnp6SwsLBS+Y3l5OQ0NDTl69Gi19unTpzQ3N+fixYvp6OhIpVLJtWvXata1t7dnc3Oz5peHAAAAAOxNwlUAAAAA7DmnT5+uXjc0NOTIkSPp6+ur1tra2pIknz9/TpK8e/cu09PTOXToUHX09PQkSZaWlgrfsbGxkQMHDqRUKlVrg4OD6ejoSGdnZ65cuZLJycnq6Vg/NTU1JckvdQAAAAD2HuEqAAAAAPacxsbGmvtSqVRT+xmI2t7eTpJ8/fo1ly5dyuzsbM1YXFz85bd+P5XL5ayvr2dzc7NaO3z4cN6+fZupqakcO3YsY2Nj6e/vz5cvX6pz1tbWkiStra3/Sa8AAAAA/DnCVQAAAAD89QYGBvL+/fucOHEiJ0+erBnNzc2Fa86cOZMk+fDhQ019//79uXDhQsbHxzM3N5ePHz/mxYsX1efz8/M5fvx4yuVy3foBAAAAYHcIVwEAAADw17t69WrW1tZy+fLlvHnzJktLS3n27FmGh4eztbVVuKa1tTUDAwN5+fJltfb06dPcvn07s7OzWV5ezsTERLa3t9Pd3V2dMzMzk6Ghobr3BAAAAED9CVcBAAAA8Ndrb2/Pq1evsrW1laGhofT19eX69etpaWnJvn2//0Q2MjKSycnJ6n1LS0sePXqU8+fPp7e3N/fv38/U1FROnTqVJPn27VuePHmS0dHRuvcEAAAAQP2VdnZ2dv70JgAAAADg/2hjYyPd3d15+PBhzp0794/z7927l8ePH+f58+e7sDsAAAAA6s3JVQAAAADwG01NTZmYmMjKysq/mt/Y2Jg7d+7UeVcAAAAA7BYnVwEAAAAAAAAAABRwchUAAAAAAAAAAEAB4SoAAAAAAAAAAIACwlUAAAAAAAAAAAAFhKsAAAAAAAAAAAAKCFcBAAAAAAAAAAAUEK4CAAAAAAAAAAAoIFwFAAAAAAAAAABQQLgKAAAAAAAAAACggHAVAAAAAAAAAABAgR86JiW4TSvSbgAAAABJRU5ErkJggg==",
"text/plain": [
"<Figure size 3000x500 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Extract recorded data for plotting\n",
"times, cache_sizes = zip(*cache.cache_size_over_time)\n",
"\n",
"# Plot the cache size over time\n",
"plt.figure(figsize=(30, 5))\n",
"plt.plot(times, cache_sizes, label=\"Objects in Cache\")\n",
"plt.xlabel(\"Time (s)\")\n",
"plt.ylabel(\"Number of Cached Objects\")\n",
"plt.title(\"Number of Objects in Cache Over Time\")\n",
"plt.legend()\n",
"plt.grid(True)\n",
"plt.savefig(f\"{TEMP_BASE_DIR}/objects_in_cache_over_time.pdf\")\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "f30a0497-9b2e-4ea9-8ebf-6687de19aaa9",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAArMAAAIjCAYAAAAQgZNYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABDjklEQVR4nO3de1xVVf7/8fcB5YAaF0NBkMRb3hPTkbDUalA0s5zpoo4TaFbWWGZMF+0iXvqKpZVTWX7zm1p9pzQvWTOZZqSZSlpe8j4ZaZYJispFTVBYvz/6eb4dAYUjcFz6ej4e5zGetdfe+7PX2R7fs1t7H4cxxggAAACwkI+3CwAAAAA8RZgFAACAtQizAAAAsBZhFgAAANYizAIAAMBahFkAAABYizALAAAAaxFmAQAAYC3CLAAAAKxFmAWAKvD111+rS5cuql27thwOhzZt2uTtktxER0fr5ptv9nh9h8OhsWPHVl5B5bRnzx45HA5NmTLF420MHjxY0dHRlVZTZW+vLLNnz5bD4dCePXtcbef7OQIXA8IsYLFt27bpr3/9qyIjI+V0OhUREaFBgwZp27Zt3i7tknby5EndcccdOnz4sF566SW98847atSokbfLAoCLUg1vFwDAMwsXLtTAgQNVt25dDR06VI0bN9aePXv05ptvav78+ZozZ47+9Kc/ebvMS1JGRoZ+/PFHzZgxQ/fcc4+3y0EVmzFjhoqLi71dBnDJIswCFsrIyNBdd92lJk2aaOXKlapXr55r2cMPP6yuXbvqrrvu0ubNm9WkSRMvVlrS8ePHVatWLW+XUaUOHDggSQoODvZuIagWNWvW9HYJwCWNaQaAhSZPnqzjx4/rjTfecAuykhQaGqr//u//1rFjx/T888+7Ldu3b5+GDh2qiIgIOZ1ONW7cWA888IAKCwtdfXJycvTII48oOjpaTqdTDRs2VGJiorKzsyWVPm9PklasWCGHw6EVK1a42q6//nq1bdtW69evV7du3VSrVi09+eSTkqQPP/xQffr0cdXStGlTTZgwQUVFRW7bPb2N7du364YbblCtWrUUGRlZ4tgk6cSJExo7dqyuvPJK+fv7q0GDBvrzn/+sjIwMV5/i4mJNnTpVbdq0kb+/v8LCwjRs2DAdOXKkXGP/+eefq2vXrqpdu7aCg4N16623aseOHa7lgwcPVvfu3SVJd9xxhxwOh66//vqzbjMnJ0cjR45UVFSUnE6nmjVrpueee67E1b4pU6aoS5cuuvzyyxUQEKCOHTtq/vz5pW7zf//3f9W5c2fVqlVLISEh6tatmz799NMS/VatWqXOnTvL399fTZo00dtvv12ucTjTjz/+qL/97W9q0aKFAgICdPnll+uOO+4ocZ6cPn9WrVqlESNGqF69egoODtawYcNUWFionJwcJSYmKiQkRCEhIXr88cdljCl1ny+99JIaNWqkgIAAde/eXVu3bi3RZ9GiRWrbtq38/f3Vtm1bffDBB6VuqyJje6Yz58z+fl7vG2+8oaZNm8rpdOoPf/iDvv7663Jtc9u2bbrxxhsVEBCghg0b6tlnnz3r1d9PP/1UMTEx8vf3V+vWrbVw4cJy7Qe4GHBlFrDQv/71L0VHR6tr166lLu/WrZuio6P18ccfu9p++eUXde7cWTk5ObrvvvvUsmVL7du3T/Pnz9fx48fl5+eno0ePqmvXrtqxY4fuvvtuXX311crOztZHH32kn3/+WaGhoRWu9dChQ+rdu7cGDBigv/71rwoLC5P0W6ipU6eOkpOTVadOHX3++ecaM2aM8vLyNHnyZLdtHDlyRL169dKf//xn3XnnnZo/f76eeOIJtWvXTr1795YkFRUV6eabb1ZaWpoGDBighx9+WPn5+Vq2bJm2bt2qpk2bSpKGDRum2bNna8iQIRoxYoR2796tV199VRs3btTq1avPepXts88+U+/evdWkSRONHTtWv/76q1555RVde+212rBhg6KjozVs2DBFRkZq4sSJGjFihP7whz+4jrk0x48fV/fu3bVv3z4NGzZMV1xxhdasWaPRo0dr//79mjp1qqvvP/7xD91yyy0aNGiQCgsLNWfOHN1xxx3697//rT59+rj6jRs3TmPHjlWXLl00fvx4+fn5ae3atfr888/Vs2dPV7/vv/9et99+u4YOHaqkpCTNnDlTgwcPVseOHdWmTZvyf8j67Ya3NWvWaMCAAWrYsKH27Nmj119/Xddff722b99e4mr8Qw89pPDwcI0bN05fffWV3njjDQUHB2vNmjW64oorNHHiRC1evFiTJ09W27ZtlZiY6Lb+22+/rfz8fA0fPlwnTpzQP/7xD914443asmWLa7w//fRT3XbbbWrdurVSU1N16NAhDRkyRA0bNixRf3nHtiLeffdd5efna9iwYXI4HHr++ef15z//WT/88MNZz7PMzEzdcMMNOnXqlEaNGqXatWvrjTfeUEBAQKn9d+3apf79++v+++9XUlKSZs2apTvuuENLlixRjx49PKodsIoBYJWcnBwjydx6661n7XfLLbcYSSYvL88YY0xiYqLx8fExX3/9dYm+xcXFxhhjxowZYySZhQsXltln1qxZRpLZvXu32/Lly5cbSWb58uWutu7duxtJZvr06SW2d/z48RJtw4YNM7Vq1TInTpwosY23337b1VZQUGDCw8PNbbfd5mqbOXOmkWRefPHFMmv/8ssvjSTzz3/+0235kiVLSm0/U0xMjKlfv745dOiQq+3bb781Pj4+JjEx0dV2eizmzZt31u0ZY8yECRNM7dq1zXfffefWPmrUKOPr62v27t3rajtzzAoLC03btm3NjTfe6GrbtWuX8fHxMX/6059MUVGRW//T42CMMY0aNTKSzMqVK11tBw4cME6n0/z9738/Z92STEpKSpm1GWNMenp6ic/u9PmTkJDgVk9cXJxxOBzm/vvvd7WdOnXKNGzY0HTv3t3Vtnv3biPJBAQEmJ9//tnVvnbtWiPJPPLII662mJgY06BBA5OTk+Nq+/TTT40k06hRI7dayzO2ZUlKSnLb3ukaL7/8cnP48GFX+4cffmgkmX/9619n3d7IkSONJLN27VpX24EDB0xQUFCJv3unP8cFCxa42nJzc02DBg1Mhw4dzlk7cDFgmgFgmfz8fEnSZZdddtZ+p5fn5eWpuLhYixYtUt++fdWpU6cSfR0OhyRpwYIFat++fak3jp3uU1FOp1NDhgwp0f77q0z5+fnKzs5W165ddfz4ce3cudOtb506dfTXv/7V9d7Pz0+dO3fWDz/84GpbsGCBQkND9dBDD5VZ+7x58xQUFKQePXooOzvb9erYsaPq1Kmj5cuXl3kc+/fv16ZNmzR48GDVrVvX1X7VVVepR48eWrx4cTlGo6R58+apa9euCgkJcaspPj5eRUVFWrlypavv78fsyJEjys3NVdeuXbVhwwZX+6JFi1RcXKwxY8bIx8f9K/7Mz7B169ZuV/fr1aunFi1auI1ref2+tpMnT+rQoUNq1qyZgoOD3eo7bejQoW71xMbGyhijoUOHutp8fX3VqVOnUuvp16+fIiMjXe87d+6s2NhY1+dw+vNKSkpSUFCQq1+PHj3UunXrs9Zf1thWVP/+/RUSEuJ6f3qszzW+ixcv1jXXXKPOnTu72urVq6dBgwaV2j8iIsLt72xgYKASExO1ceNGZWZmelw/YAumGQCWOR1ST4fasvw+9B48eFB5eXlq27btWdfJyMjQbbfdVjmF/n+RkZHy8/Mr0b5t2zY9/fTT+vzzz5WXl+e2LDc31+19w4YNSwSxkJAQbd682fU+IyNDLVq0UI0aZX+t7dq1S7m5uapfv36py0/fuFWaH3/8UZLUokWLEstatWqlpUuX6tixY6pdu3aZ2yirps2bN5eY+1xaTf/+97/17LPPatOmTSooKHC1/35sMjIy5OPjU2pgO9MVV1xRoi0kJKTc84d/79dff1VqaqpmzZqlffv2uc1zPfPzLG3fpwNnVFRUifbS6mnevHmJtiuvvFLvv/++pP/7vErr16JFixIhtTxjW1FnHuPpYHuu8f3xxx8VGxtbor20c0+SmjVrVqLOK6+8UtJv83fDw8PLXTNgI8IsYJmgoCA1aNDALciVZvPmzYqMjFRgYKB+/fXXStt/Wf+4n3nj1mmlzfPLyclR9+7dFRgYqPHjx6tp06by9/fXhg0b9MQTT5S40cXX17fUbZsybgwqS3FxserXr69//vOfpS4vK1BWpeLiYvXo0UOPP/54qctPh5Ivv/xSt9xyi7p166bXXntNDRo0UM2aNTVr1iy9++67Hu27ssZV+m0O7KxZszRy5EjFxcUpKChIDodDAwYMKPXGpbL2XVq7J/VURFWMrVS54wugbIRZwEI333yzZsyYoVWrVum6664rsfzLL7/Unj17NGzYMEm/hbTAwMBS7/b+vaZNm56zz+mrSzk5OW7tp6+ElceKFSt06NAhLVy4UN26dXO17969u9zbOFPTpk21du1anTx5ssyba5o2barPPvtM1157bZk305Tl9I8e/Oc//ymxbOfOnQoNDa3wVdnTNR09elTx8fFn7bdgwQL5+/tr6dKlcjqdrvZZs2aV2F5xcbG2b9+umJiYCtfjqfnz5yspKUkvvPCCq+3EiRMlzpPKsmvXrhJt3333neupAqc/r9L6nfkZlndsq0ujRo3KVfdp33//vYwxbv9H87vvvpOkavllMsDbmDMLWOixxx5TQECAhg0bpkOHDrktO3z4sO6//37VqlVLjz32mCTJx8dH/fr107/+9S998803JbZ3+krRbbfdpm+//bbUxxed7nP6qQC/n8tZVFSkN954o9z1n75i9fsrVIWFhXrttdfKvY0z3XbbbcrOztarr75aYtnp/dx5550qKirShAkTSvQ5derUWYNXgwYNFBMTo7feesut39atW/Xpp5/qpptu8qjuO++8U+np6Vq6dGmJZTk5OTp16pSk38bM4XC4XQHfs2ePFi1a5LZOv3795OPjo/Hjx5e4IlqVVwR9fX1LbP+VV14p84r9+Vq0aJH27dvner9u3TqtXbvW9XSL339ev5/msGzZMm3fvr1E7eUZ2+py00036auvvtK6detcbQcPHizzvyj88ssvbn9n8/Ly9PbbbysmJoYpBrgkcGUWsFDz5s311ltvadCgQWrXrl2JXwDLzs7We++95wqekjRx4kR9+umn6t69u+677z61atVK+/fv17x587Rq1SoFBwfrscce0/z583XHHXfo7rvvVseOHXX48GF99NFHmj59utq3b682bdrommuu0ejRo3X48GHVrVtXc+bMcYWu8ujSpYtCQkKUlJSkESNGyOFw6J133jmvsJWYmKi3335bycnJWrdunbp27apjx47ps88+09/+9jfdeuut6t69u4YNG6bU1FRt2rRJPXv2VM2aNbVr1y7NmzdP//jHP3T77beXuY/Jkyerd+/eiouL09ChQ12P5goKCtLYsWM9qvuxxx7TRx99pJtvvtn1WKxjx45py5Ytmj9/vvbs2aPQ0FD16dNHL774onr16qW//OUvOnDggKZNm6ZmzZq5TTlp1qyZnnrqKU2YMEFdu3bVn//8ZzmdTn399deKiIhQamqqR3Wey80336x33nlHQUFBat26tdLT0/XZZ5/p8ssvr5L9NWvWTNddd50eeOABFRQUaOrUqbr88svdpmukpqaqT58+uu6663T33Xfr8OHDeuWVV9SmTRsdPXrU1a+8Y1tdHn/8cb3zzjvq1auXHn74YdejuRo1alRqPVdeeaWGDh2qr7/+WmFhYZo5c6aysrK8dmUZqHbeeIQCgMqxefNmM3DgQNOgQQNTs2ZNEx4ebgYOHGi2bNlSav8ff/zRJCYmmnr16hmn02maNGlihg8fbgoKClx9Dh06ZB588EETGRlp/Pz8TMOGDU1SUpLJzs529cnIyDDx8fHG6XSasLAw8+STT5ply5aV+miuNm3alFrL6tWrzTXXXGMC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STHt27evvMIuEVyZvYilp6crODhYnTp1crXFx8fLx8dHa9euLfd2cnNzFRgYqBo13H9jY/jw4QoNDVXnzp01c+ZMGYsfWVxYWKj169crPj7e1ebj46P4+Hilp6eXuk56erpbf0lKSEhw9d+9e7cyMzPd+gQFBSk2NrbMbdrIk7E70/Hjx3Xy5EnVrVvXrX3FihWqX7++WrRooQceeECHDh2q1NovBJ6O39GjR9WoUSNFRUXp1ltv1bZt21zLOPfKf+69+eabGjBggGrXru3WfimcexV1ru+8yvg8LiXFxcXKz88v8b23a9cuRUREqEmTJho0aJD27t3rpQrtQZi9iGVmZqp+/fpubTVq1FDdunWVmZlZrm1kZ2drwoQJJaYmjB8/Xu+//76WLVum2267TX/729/0yiuvVFrt1S07O1tFRUWuX547LSwsrMyxyszMPGv/0/9bkW3ayJOxO9MTTzyhiIgIt38Ee/XqpbfffltpaWl67rnn9MUXX6h3794qKiqq1Pq9zZPxa9GihWbOnKkPP/xQ//u//6vi4mJ16dJFP//8syTOvfIe57p167R161bdc889bu2XyrlXUWV95+Xl5enXX3+tlO+CS8mUKVN09OhR3Xnnna622NhY1/S+119/Xbt371bXrl2Vn5/vxUovfF7/OVtU3KhRo/Tcc8+dtc+OHTvOez95eXnq06ePWrdurbFjx7ote+aZZ1x/7tChg44dO6bJkydrxIgR571fXFomTZqkOXPmaMWKFW43MQ0YMMD153bt2umqq65S06ZNtWLFCv3xj3/0RqkXjLi4OMXFxbned+nSRa1atdJ///d/a8KECV6szC5vvvmm2rVrp86dO7u1c+6hqr377rsaN26cPvzwQ7eLTr1793b9+aqrrlJsbKwaNWqk999/v9R7V/Abrsxa6O9//7t27Nhx1leTJk0UHh5eYtL9qVOndPjwYYWHh591H/n5+erVq5cuu+wyffDBB6pZs+ZZ+8fGxurnn39WQUHBeR+fN4SGhsrX11dZWVlu7VlZWWWOVXh4+Fn7n/7fimzTRp6M3WlTpkzRpEmT9Omnn+qqq646a98mTZooNDRU33///XnXfCE5n/E7rWbNmurQoYNrbDj3zn2cx44d05w5c8oVEC7Wc6+iyvrOCwwMVEBAQKWcy5eCOXPm6J577tH7779fYtrGmYKD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",
"text/plain": [
"<Figure size 800x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from collections import Counter\n",
"# Count occurrences of each number\n",
"count = Counter([l.lambda_value for l in db.data.values()])\n",
"\n",
"# Separate the counts into two lists for plotting\n",
"x = list(count.keys()) # List of unique numbers\n",
"y = list(count.values()) # List of their respective counts\n",
"\n",
"# Plot the data\n",
"plt.figure(figsize=(8, 6))\n",
"plt.bar(x, y, color='skyblue')\n",
"\n",
"# Adding labels and title\n",
"plt.xlabel('Number')\n",
"plt.ylabel('Occurrences')\n",
"plt.title('Occurance of each lambda in db')\n",
"plt.savefig(f\"{TEMP_BASE_DIR}/lambda_distribution.pdf\")\n",
"\n",
"# Show the plot\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "c192564b-d3c6-40e1-a614-f7a5ee787c4e",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAs0AAAIoCAYAAACSxtawAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABla0lEQVR4nO3de1yUZf7/8ffMwDAIgogInsE841lTsNIsi1zXLdc2LddMLTXpYGZu/iotrbV1O5dmVmpnTcva7GBqHjLIykMqqVspaikeOShynLl+f7jMVwIdQGRwfD0fDx7r3Pc193xmPtC+ubnu67YYY4wAAAAAnJHV2wUAAAAA1R2hGQAAAPCA0AwAAAB4QGgGAAAAPCA0AwAAAB4QmgEAAAAPCM0AAACAB4RmAAAAwANCMwAAAOABoRnARem2226TxWJRamrqOR8rNTVVFotFt9122zkfCwBQPRGaAVSqogB53XXXebsUVIK1a9fKYrHIYrFo0aJF3i6n2klPT9fjjz+u+Ph4hYeHy9/fXxEREerTp49efPFFnThxwtslntWVV14pi8Xi7TKACwKhGQBwRq+//rokyWKxaO7cuV6upnpZuXKlmjVrpkceeUSZmZn629/+pokTJ2rgwIHav3+/7rnnHnXo0MHbZQKoJH7eLgAAUD1lZWVp8eLFat++vSIjI/Xll19q3759atSokbdL87off/xR/fv3lyS9/fbbGjJkSIkxq1ev1qRJk6q6NADnCWeaAXhNZmam/vWvf6lXr16qX7++7Ha76tevr1tvvVW//vprifGPPvqoLBaLVq9erXnz5qldu3YKDAxUTEyMXnjhBUmSMUZPP/20WrZsKYfDoebNm+vNN988Yw0ul0szZsxQ8+bN5XA4FBMTo6lTp6qgoKDEWKfTqX/9619q1qyZHA6HmjVrpunTp8vlcpV67FWrVmnEiBFq2bKlgoODFRwcrK5du2rOnDll/oyuvvpqWa1W7dmzp9T999xzjywWi5YvX+7e9sEHH6hXr16qW7euHA6H6tevrz59+uiDDz4o8+tK0nvvvaeTJ0/q1ltv1a233iqXy6X58+efcfyhQ4d0//33q2XLlgoMDFTt2rXVvXt3PfXUUyXG/vjjjxoyZIgaNmyogIAA1atXT9ddd50++eSTEmM//vhjXX311QoLC5PD4VDbtm311FNPyel0Fhvncrn02muvqVu3bqpdu7YCAwPVsGFD9e/fX6tXry429lw/o3vuuUc5OTl68cUXSw3M0qmpD398XUmaN2+eunfv7v6e6N69e6mf6/z582WxWErdt3r1alksFj366KPFtlssFl155ZU6ePCghg0bpjp16igwMFBxcXElarFYLFqzZo3730VfzM0HzsAAQCXavXu3kWQSEhI8jk1OTjZ2u90kJCSYsWPHmgceeMD079/f2Gw2U7t2bZOamlps/JQpU4wkc/3115vQ0FBz6623mnvuucc0aNDASDKvvvqqGTt2rImMjDQjR440d955pwkLCzOSzJo1a4oda9iwYUaS6d+/v6ldu7YZM2aMmTBhgmnZsqWRZAYOHFii3hEjRhhJJiYmxowfP96MHTvW1KlTx/z5z382ksywYcOKjU9ISDCXXHKJGTJkiPnHP/5hRo8ebZo0aWIkmfHjx5fp85w3b56RZJ544okS+woKCkxERISpX7++cTqdxhhjZs2aZSSZevXqmVGjRplJkyaZ4cOHm9jYWDNkyJAyvWaRSy+91NhsNnPgwAGTnZ1tgoODTUxMjHG5XCXG7tixw9SrV89IMpdffrmZOHGiSUxMNFdeeaUJCwsrNnbx4sXGbrcbf39/89e//tVMmjTJjBw50rRt29Zcf/31xcY++OCDRpJp0KCBGTFihLnvvvtM165djSRz4403Fhs7ceJEI8lccsklJjEx0Tz44INm6NChJiYmxjz00EPucef6Gf38889GkmnUqJH7cy+ru+++2/1+7rnnnmLfv/fcc0+xsUW9nzdvXonjrFq1ykgyU6ZMKbZdkunQoYNp1qyZ6dKlixk3bpy55ZZbjM1mM3a73WzdutU9dsqUKe7vxylTpri/lixZUq73BFwsCM0AKlV5QnNGRoY5evRoie1fffWVsVqt5vbbby+2vSg0165d2/z666/u7Xv37jV2u92EhoaaFi1amEOHDrn3ffvtt+5wfLqi0BwREWH27dvn3p6Xl2d69uxpJJnFixe7txeFlA4dOpgTJ064t//222+mTp06pYbmXbt2lXhvBQUF5pprrjE2m83s2bPHwydkTFZWlgkMDDRt2rQpse+TTz4xksyECRPc2zp37mzsdrs5ePBgifFHjhzx+HpFtmzZUqKPt956q5FkVqxYUWJ8UZCdM2dOiX2nf75paWkmKCjIBAUFmY0bN5517Jdffumu4fTP3OVymTFjxpToUe3atU39+vVNdnZ2ieOe/n12rp/R/PnzjSTz97//3ePY061Zs8ZIMq1btzYZGRnu7ceOHTMtWrQwkszatWvd2ysamiWZsWPHFgv0r732mpFkRo8eXWx8r169DOfPgLJhegYArwkNDVXt2rVLbO/du7diY2O1YsWKUp937733qmnTpu7HjRo10uWXX67MzEw99NBDioiIcO/r3r27mjZtqh9//PGMx2rYsKH7sd1u1xNPPCFJxf4sXjTFY/LkyQoKCnJvb9Cgge69995Sjx0TE1Nim5+fn8aMGSOn06lVq1aV+rzT1axZUzfccIN++uknbdy4sdi+t956S5L097//vdh2f39/+fv7lzhWeHi4x9crUnQB4K233ureVvTvon1FvvvuO/3www/q2bOn7rjjjhLHOv3zfeONN5Sdna37779fnTp1OuvYl156SZI0Z86cYp+5xWLRk08+KYvFovfee6/Y8+12u2w2W4nj/vH77Fw+o7S0tBK1lsUbb7wh6dQ0o9DQUPf2sLAwTZkyRZLOOv2lrIKCgvSvf/1LVuv//V/8sGHD5Ofnp++///6cjw9crLgQEIBXrV69Ws8995zWr1+vI0eOqLCw0L3PbreX+pyOHTuW2FavXr2z7lu/fn2px7riiitKbIuPj5efn582bdrk3lYUuksbX9o2STp+/LieeuopffTRR/r111+VnZ1dbP/+/ftLfd4fDR06VO+9957eeustde7cWdKpi/Q++eQTtWvXrtgKDYMHD9bEiRPVtm1b3XLLLerdu7cuv/xyhYSElOm1JCkvL09vv/22atasqQEDBri39+7dW40aNdKSJUuUnp6usLAwSadCsyRde+21Ho9dnrHffvutgoKCzrhqR2BgoHbs2OF+PHjwYM2aNUtt27bV4MGD1bt3b8XHxyswMLDY8yrjM6qIou+nK6+8ssS+3r17S5I2b958zq/TokULBQcHF9vm5+enyMhIZWRknPPxgYsVoRmA1yxatEiDBg1ScHCwEhISFB0drRo1argvfjrTxW+lhRs/P7+z7js9jJ8uMjKyxDabzabw8HBlZma6t2VmZspqtapOnTplOkZ+fr6uvPJKbdy4UZ06ddLQoUMVHh4uPz8/paam6o033lBeXl6pNf3Rtddeq8jISC1YsEBPPfWUbDabFi9erJycHA0dOrTY2AkTJig8PFwvv/yynn76aT311FPy8/NTv3799Oyzz5Z69vuPPvroIx09elTDhw8vFjitVquGDBmiJ598Uu+++64SExPdn4106qy7J+UZe+zYMRUWFuqxxx4745jTfxF5/vnnFRMTo3nz5unxxx/X448/LofDoZtuuklPP/20u3fn+hlFRUVJkn7//XeP7+F0WVlZslqtxf4SUiQyMlIWi0VZWVnlOmZpzhT+/fz8Slw8CaDsCM0AvObRRx+Vw+HQhg0b1Lx582L7FixYUCU1HDx4UC1btiy2zel06ujRo8XCcGhoqFwul44cOVIi9Bw8eLDEcT/++GNt3LhRI0eO1GuvvVZs34IFC9x/qi8Lm82mm2++Wc8995xWrFihhIQEvfXWW7JarbrllluKjbVYLBoxYoRGjBiho0eP6uuvv9Z7772n999/Xz///LO2bNlS6vSF0xVNv5g3b57mzZt3xjFFoblWrVqSyhYiTx8bHR191rEhISGyWCw6cuSIx+NKp0LhhAkTNGHCBO3fv19r1qzRvHnz9OabbyotLU3Lli2TdO6f0WWXXSbp1F9JXC5XsWkQnt6Py+XS4cOHVbdu3WL7Dh06JGNMscBbdNzSfuE7/Rc6AFWDOc0AvObXX39V69atSwTmAwcOaNeuXVVSw9dff11iW3JysgoLC4vNuS2aAlHa+NK2FS2Zd/3115dpvCdFZ5Tffvtt7du3T2vWrFHv3r3PesY2PDxcN9xwgxYuXKirrrpKP/30k3755Zezvs6ePXu0cuVKRUZGauTIkaV+xcTEaNOmTe7pBt26dZMkffnllx7fR3nGdu/eXUePHtXPP//scewf1a9fXzfffLO++OILNWvWTCtWrFBOTk6JcRX5jJo1a6aePXtq3759Hn/5Of2vCUXfT6UtQ1e07fTpRUXTX0r7ZeT0qUPnouiXA85AA54RmgF4TZMmTfTLL78UO1Obm5urO++8s9R1ks+H559/Xr/99pv7cX5+vh566CFJKrZebVFonTp1arEpAb///ruef/75Esdt0qSJJGndunXFtq9Zs0avvvpquevs3Lmz2rRpoyVLluiVV16RMabE1AzpVPgyxhTbVlBQoGPHjkmSHA7HWV9n3rx5crlcGj16tF577bVSvx588EFJ/3dG+tJLL9Wll16qtWvXlvreTg99w4YN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aMa0yWoZlKs9X7yqXSvf1Z4vXlWr4DzNmDa52qzTzM1NAAAA4FU9evRQXFyctm3bpkOHDqlu3bpq27ZttTjDXITQDAAAAK+zWq1q27atOzRXp8AsMT0DAAAA8IjQDAAAAHhAaAYAAAA8IDQDAAAAHhCaAQAAAA8IzQAAAIAHhGYAAADAA0IzAAAA4AGhGQAAAPCA0AwAAAB4QGgGAAAAPCA0AwAAAB4QmgEAAAAPCM0AAACAB4RmAAAAwANCMwAAAOABoRkAAADwgNAMAAAAeEBoBgAAADyotqH5ySeflMVi0bhx49zbcnNzlZiYqPDwcAUHB2vgwIE6ePBgseft3btX/fr1U40aNVS3bl098MADKiwsLDZm9erV6ty5swICAtSsWTPNnz+/xOvPnDlT0dHRcjgc6t69u7777rvz8TYBAABwAaiWofn777/XK6+8ovbt2xfbft999+mTTz7RokWLtGbNGu3fv19//etf3fudTqf69eun/Px8JSUl6Y033tD8+fM1efJk95jdu3erX79+6t27tzZv3qxx48bp9ttv17Jly9xjFi5cqPHjx2vKlCnauHGjOnTooISEBB06dOj8v3kAAABUO9UuNJ84cUJDhgzRq6++qrCwMPf2zMxMvf7663rmmWd01VVXqUuXLpo3b56SkpL07bffSpK+/PJL/fTTT3r77bfVsWNH9e3bV9OmTdPMmTOVn58vSZo9e7ZiYmL09NNPq3Xr1rrrrrt044036tlnn3W/1jPPPKM77rhDw4cPV5s2bTR79mzVqFFDc+fOrdoPAwAAANWCn7cL+KPExET169dPffr00eOPP+7evmHDBhUUFKhPnz7uba1atVLjxo2VnJysuLg4JScnq127doqMjHSPSUhI0J133qmUlBR16tRJycnJxY5RNKZoGkh+fr42bNigSZMmufdbrVb16dNHycnJZ6w7Ly9PeXl57sdZWVmSJJfLJZfLVeb373K5ZIwp13NQPdFL30I/fQv99B300rdUdT/L8zrVKjQvWLBAGzdu1Pfff19iX1pamux2u2rVqlVse2RkpNLS0txjTg/MRfuL9p1tTFZWlnJycpSeni6n01nqmB07dpyx9unTp+uxxx4rsf3w4cPKzc094/P+yOVyKTMzU8YYWa3V7g8BKAd66Vvop2+hn76DXvqWqu7n8ePHyzy22oTmffv2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qvj4eOXm5io4OFhLlixRmzZttHnzZtntdtWqVavY+MjISKWlpUmS0tLSigXmov1F+842JisrSzk5OUpPT5fT6Sx1zI4dO85Y9/Tp0/XYY4+V2H748GHl5uaW7c3rVPMyMzNljJHVWq1mz6Cc6KVvoZ++hX76DnrpW6q6n8ePHy/z2GoXmlu2bKnNmzcrMzNTixcv1rBhw7RmzRpvl+XRpEmTNH78ePfjrKwsNWrUSBEREQoJCSnzcVwulywWiyIiIvjhv8DRS99CP30L/fQd9NK3VHU/HQ5HmcdWu9Bst9vVrFkzSVKXLl30/fff6/nnn9egQYOUn5+vjIyMYmebDx48qKioKElSVFRUiVUuilbXOH3MH1fcOHjwoEJCQhQYGCibzSabzVbqmKJjlCYgIEABAQEltlut1nI33WKxVOh5qH7opW+hn76FfvoOeulbqrKf5XmNav/d5XK5lJeXpy5dusjf318rV65079u5c6f27t2r+Ph4SVJ8fLy2bt1abJWL5cuXKyQkRG3atHGPOf0YRWOKjmG329WlS5diY1wul1auXOkeAwAAgItLtTrTPGnSJPXt21eNGzfW8ePH9e6772r16tVatmyZQkNDNXLkSI0fP161a9dWSEiI7r77bsXHxysuLk6SdO2116pNmzYaOnSoZsyYobS0ND388MNKTEx0nwUeM2aMXnrpJU2cOFEjRozQV199pffff1+ffvqpu47x48dr2LBh6tq1q7p166bnnntO2dnZGj58uFc+FwAAAHhXtQrNhw4d0q233qoDBw4oNDRU7du317Jly3TNNddIkp599llZrVYNHDhQeXl5SkhI0KxZs9zPt9lsWrp0qe68807Fx8crKChIw4YN09SpU91jYmJi9Omnn+q+++7T888/r4YNG+q1115TQkKCe8ygQYN0+PBhTZ48WWlpaerYsaO++OKLEhcHAgAA4OJgMcYYbxfhi7KyshQaGqrMzMxyXwh46NAh1a1bl7lZFzh66Vvop2+hn76DXvqWqu5nefIa310AAACAB4RmAAAAwANCMwAAAOABoRkAAADwgNAMAAAAeEBoBgAAADwgNAMAAAAeEJoBAAAADwjNAAAAgAeEZgAAAMADQjMAAADgAaEZAAAA8IDQDAAAAHhAaAYAAAA8IDQDAAAAHhCaAQAAAA8IzQAAAIAHhGYAAADAgwqF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rVs3devWrbJrAQAAAKqlMs9pBgAAAC5WhGYAAADAA0IzAAAA4AGhGQAAAPCA0AwAAAB4UKmhedeuXdq+fXtlHhIAAADwugqF5hdeeEGDBw8utm348OFq3ry52rZtq65du+rQoUOVUiAAAADgbRUKza+99poiIyPdj5ctW6Y33nhDo0aN0osvvqhdu3bpscceq7QiAQAAAG+q0M1N9uzZo9atW7sfv//++4qJidHLL78sSUpLS9Nbb71VORUCAAAAXlahM83GmGKPv/zyS/Xt29f9ODo6WmlpaedWGQAAAFBNVCg0t2jRQkuWLJF0amrG/v37i4Xm3377TbVq1aqUAgEAAABvq9D0jAkTJuiWW25RWFiYsrOz1bp1ayUkJLj3f/XVV+rYsWNl1QgAAAB4VYVC8+DBgxUeHq7PPvtMtWrV0tixY+Xnd+pQx44dU+3atTV06NBKLRQAAADwlgqFZkm65pprdM0115TYXrt2bX344YfnVBQAAABQnVQ4NP/RyZMntWDBAuXl5elPf/qTmjRpUlmHBgAAALyqQqF55MiRWr9+vbZt2yZJys/PV1xcnPtxaGiovvrqK3Xq1KnyKgUAAAC8pEKrZ6xatUp//etf3Y/fffddbdu2Te+88462bdumqKgobm4CAAAAn1Gh0JyWlqbo6Gj3448++khdu3bVzTffrDZt2uiOO+7Q+vXrK6tGAAAAwKsqFJqDgoKUkZEhSSosLNTq1auLLTlXs2ZNZWZmVkqBAAAAgLdVaE5z586d9eqrr6p37976z3/+o+PHj6t///7u/b/++qsiIyMrrUgAAADAmyoUmp944gklJCSoa9euMsboxhtvVLdu3dz7lyxZossuu6zSigQAAAC8qULTM7p27aodO3boww8/1KpVq/T++++792VkZGjs2LGaMGFCuY87ffp0XXrppapZs6bq1q2rG264QTt37iw2Jjc3V4mJiQoPD1dwcLAGDhyogwcPFhuzd+9e9evXTzVq1FDdunX1wAMPqLCwsNiY1atXq3PnzgoICFCzZs00f/78EvXMnDlT0dHRcjgc6t69u7777rtyvycAAABc+CoUmiUpIiJC119/vXr16lVse61atXTvvfdW6Dbaa9asUWJior799lstX75cBQUFuvbaa5Wdne0ec9999+mTTz7RokWLtGbNGu3fv7/YSh5Op1P9+vVT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",
"text/plain": [
"<Figure size 800x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Plotting lambda against access_count.\n",
"\n",
"plt.figure(figsize=(8, 6))\n",
"plt.scatter(merged['lambda'], merged['access_count'], alpha=0.7, edgecolor='k')\n",
"plt.title('Lambda vs Access Count', fontsize=14)\n",
"plt.xlabel('Lambda', fontsize=12)\n",
"plt.ylabel('Access Count', fontsize=12)\n",
"plt.grid(alpha=0.3)\n",
"\n",
"plt.savefig(f\"{TEMP_BASE_DIR}/lambda_vs_access_count.pdf\")\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "00a12eea-c805-4209-9143-48fa65619873",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAArcAAAIjCAYAAAAZajMiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABEDElEQVR4nO3dfXzO9f////sxsxMnO3OyGYuZ5TxENHLy0aJSUStv3sppUZGkE1ahkCHvEpG33oV6V4qik/cbCRFJYs6iYm0RNiezE/Y2bM/vH/12/Drapu3YseOYV7fr5XJcLh3P19njeHiNe689X6/DZowxAgAAACzAy9MFAAAAAK5CuAUAAIBlEG4BAABgGYRbAAAAWAbhFgAAAJZBuAUAAIBlEG4BAABgGYRbAAAAWAbhFgAAAJZBuAXwl7Z9+3Z17NhRVatWlc1m065duzxdkoMGDRrotttu83QZTrPZbHruuefK/TjdunVTt27d7O+//PJL2Ww2LV++vEz7nTlzppo0aaL8/PwyVugZzz33nGw2m0v3+cde79+/X97e3tq3b59LjwM4i3ALuMH333+ve++9V3Xr1pWvr6/Cw8M1YMAAff/9954u7S/t4sWLuueee5Senq6XX35Zb7/9turXr+/pslBBZGVlacaMGRo3bpy8vPjnsjjNmjVTr169NHHiRE+XAkiSvD1dAGB1H330kfr376+QkBANGzZMkZGRSklJ0RtvvKHly5dr6dKluvPOOz1d5l9SUlKSfvnlF73++uu6//77PV2OJf3vf/+Tt/eV+U/Nm2++qUuXLql///6eLqXCe/DBB3XrrbcqKSlJUVFRni4Hf3FX5t84wBUiKSlJ9913nxo2bKhNmzapVq1a9mWPPvqoOnfurPvuu0979uxRw4YNPVhpYTk5OapSpYqnyyhXJ06ckCQFBQV5thAL8/Pz83QJTlu0aJHuuOOOP/0Mly5dUn5+vnx8fNxUWcUTGxur4OBgLVmyRJMnT/Z0OfiL4/csQDl68cUXlZOTo4ULFzoEW0mqWbOm/vnPf+rcuXOaOXOmw7KjR49q2LBhCg8Pl6+vryIjI/XQQw/pwoUL9nUyMjL02GOPqUGDBvL19VW9evU0cOBAnTp1SpK0ePFi2Ww2paSkOOy7YC7il19+aR/r1q2bWrRooR07dqhLly6qUqWKnn76aUnSxx9/rF69etlriYqK0pQpU5SXl+ew34J97N+/X//3f/+nKlWqqG7duoU+mySdP39ezz33nK6++mr5+fmpTp06uuuuu5SUlGRfJz8/X7Nnz1bz5s3l5+en0NBQjRgxQmfOnClR79evX6/OnTuratWqCgoKUu/evXXgwAH78sGDB6tr166SpHvuuUc2m81hHmFRMjIyNGbMGEVERMjX11eNGjXSjBkzCs3HnDVrljp27KgaNWrI399fbdu2LXbu57///W+1b99eVapUUXBwsLp06aLPP/+80HqbN29W+/bt5efnp4YNG+qtt9760x6kpKTIZrNp1qxZmjdvnho2bKgqVaqoR48eOnLkiIwxmjJliurVqyd/f3/17t1b6enpDvsobs5sgwYNNHjw4D+t4Y/bF8wBPXTokAYPHqygoCAFBgZqyJAhysnJ+dP9SdLChQsVFRUlf39/tW/fXl999VWx6+bl5enpp59WWFiYqlatqjvuuENHjhz502MkJydrz549io2NdRj/fU9nz56tqKgo+fr6av/+/ZL+/LyTfjv3GjRoUOiYRc2PtdlsGjVqlFauXKkWLVrI19dXzZs31+rVqwttv3nzZl133XXy8/NTVFSU/vnPfxb7+f7973+rbdu28vf3V0hIiPr161dkX0ra68qVK6tbt276+OOPiz0m4C5cuQXK0aeffqoGDRqoc+fORS7v0qWLGjRooP/85z/2sWPHjql9+/bKyMjQ8OHD1aRJEx09elTLly9XTk6OfHx8dPbsWXXu3FkHDhzQ0KFDde211+rUqVP65JNP9Ouvv6pmzZqlrvX06dO65ZZb1K9fP917770KDQ2V9FtIrlatmsaOHatq1app/fr1mjhxorKysvTiiy867OPMmTO6+eabddddd6lv375avny5xo0bp5YtW+qWW26R9FvYuO2227Ru3Tr169dPjz76qLKzs7V27Vrt27fP/ivNESNGaPHixRoyZIhGjx6t5ORkvfrqq0pMTNSWLVtUuXLlYj/LF198oVtuuUUNGzbUc889p//973+aO3euOnXqpJ07d6pBgwYaMWKE6tatq2nTpmn06NG67rrr7J+5KDk5OeratauOHj2qESNG6KqrrtLXX3+t+Ph4HT9+XLNnz7av+8orr+iOO+7QgAEDdOHCBS1dulT33HOPPvvsM/Xq1cu+3vPPP6/nnntOHTt21OTJ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3XffderYZe1rcduXZb/FnUcl5apzpbzUqFFDly5dUnZ2dpG/fbncfN0/U9qfTVf2Kj8/XzabTatWrSpyv8VdqS6JgrDtzJx/wJUIt0A5uu222/T6669r8+bNuuGGGwot/+qrr5SSkqIRI0ZI+i20BQQE/Ok3/URFRf3pOgVXwjIyMhzGC67IlMSXX36p06dP66OPPlKXLl3s48nJySXexx9FRUVp27ZtunjxYrE3hUVFRemLL75Qp06dSh0iCr6E4ccffyy07IcfflDNmjWdunoWFRWls2fPFrp7/o8+/PBD+fn5ac2aNfL19bWPL1q0qND+8vPztX//frVu3brU9bhLcHBwoXPowoULOn78uNtrKfizPXjwoLp3724fv3jxopKTk9WqVatC2xw8eNDhvTFGhw4d0jXXXHPZYzVp0kTSb+f6n637+9pKct4V1VOpdD+bv1erVi35+/sX+qxF1RMVFSVjjCIjI+3/Q1YUZ3qdnJwsLy+vy+4XcAfm3ALl6Mknn5S/v79GjBih06dPOyxLT0/Xgw8+qCpVqujJJ5+UJHl5ealPnz769NNP9d133xXaX8GVmri4OO3evVsrVqwodp2COXq/nwual5enhQsXlrj+gis7v79CdOHCBc2fP7/E+/ijuLg4nTp1Sq+++mqhZQXH6du3r/Ly8jRlypRC61y6dKnIYFCgTp06at26tZYsWeKw3r59+/T555/r1ltvdaruvn37auvWrVqzZk2hZRkZGbp06ZKk33pms9kcrsKlpKRo5cqVDtv06dNHXl5emjx5cqEr4BXl6qX023n0+3NI+u3xUGW9cuuMdu3aqVatWlqwYIHDY/EWL15c7Dnx1ltvOUwNWr58uY4fP25/ekdxYmJiJKnIn8OilOa8i4qKUmZmpsNvdY4fP17kz3NJVKpUST179tTKlSt1+PBh+/iBAwcKna933XWXKlWqpOeff77QeWaMsf895Uyvd+zYoebNmxc7JxdwF67cAuUoOjpaS5Ys0YABA9SyZctC31B26tQpvffeew43i0ybNk2ff/65unbtquHDh6tp06Y6fvy4li1bps2bNysoKEhPPvmkli9frnvuuUdDhw5V27ZtlZ6erk8++UQLFixQq1at1Lx5c11//fWKj49Xenq6QkJCtHTpUnsIK4mOHTsqODhYgwYN0ujRo2Wz2fT222+XKXwN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hx/UtGlTbd26Vddff32J9puVlaXAwEBlZmYqICCgPD+C3fTEU245DgAAQHkY36amW49X0rxWoa/c/lFmZqYkKSQkRJK0Y8cOXbx4UbGxsfZ1mjRpoquuukpbt24tdj+5ubnKyspyeAEAAODKd8WE2/z8fI0ZM0adOnVSixYtJEmpqany8fFRUFCQw7qhoaFKTU0tdl8JCQkKDAy0vyIiIsqzdAAAALjJFRNuR44cqX379mnp0qVl3ld8fLwyMzPtryNHjrigQgAAAHiat6cLKIlRo0bps88+06ZNm1SvXj37eFhYmC5cuKCMjAyHq7dpaWkKCwsrdn++vr7y9fUtz5IBAADgARX6yq0xRqNGjdKKFSu0fv16RUZGOixv27atKleurHXr1tnHfvzxRx0+fFgxMTHuLhcAAAAeVqGv3I4cOVLvvvuuPv74Y1WvXt0+jzYwMFD+/v4KDAzUsGHDNHbsWIWEhCggIECPPPKIYmJiSvykBAAAAFhHhQ63r732miSpW7duDuOLFi3S4MGDJUkvv/yyvLy8FBcXp9zcXPXs2VPz5893c6UAAACoCCp0uC3JI3j9/Pw0b948zZs3zw0VAQAAoCKr0HNuAQAAgNIg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIAAMAyCLcAAACwDMItAAAALINwCwAAAMsg3AIA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yb5d27ZtnaoZAMqbt6cLAIC/qi5duqhnz56Kj4/X4MGD7eNeXl4yxjise/HixULbV65c2eG9zWYrciw/P7/ENZ09e1a33367ZsyYUWhZnTp17P9dtWrVEu8TANyJcAsAHjR9+nS1bt3a4Vf7tWrVUmpqqowx9seDufLZtN988426dOkiSbp06ZJ27NihUaNGSZKuvfZaffjhh2rQoIG8vfknAsCVh2kJAOBBLVu21IABAzRnzhz7WLdu3XTy5EnNnDlTSUlJmjdvnlatWuWyY86bN08rVqzQDz/8oJEjR+rMmTMaOnSoJGnkyJFKT09X//79tX37diUlJWnNmjUaMmSI8vLyXFYDAJQXwi0AeNjkyZMdpg40bdpU8+fP17x589SqVSt9++23euKJJ1x2vOnTp2v69Olq1aqVNm/erE8++UQ1a9aUJIWHh2vLli3Ky8tTjx491LJlS40ZM0ZBQUEO83sBoKKymT9O7AIAAACuUPxvOAAAACyDcAsAAADLINwCAADAMgi3AAAAsAzCLQAAACyDcAsAAADLINwCAADAMgi3AAAAsAzCLQAAACyDcAsAAADLINwCAADAMv4fVvQCaHUDfkEAAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 800x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from collections import Counter\n",
"# Count occurrences of each number\n",
"count = Counter(np.array([l.mu_value if l.mu_value is not None else 0.0 for l in db.data.values() ]).round(0))\n",
"\n",
"# Separate the counts into two lists for plotting\n",
"x = list(count.keys()) # List of unique numbers\n",
"y = list(count.values()) # List of their respective counts\n",
"\n",
"# Plot the data\n",
"plt.figure(figsize=(8, 6))\n",
"plt.bar(x, y, color='skyblue')\n",
"\n",
"# Adding labels and title\n",
"plt.xlabel('Number')\n",
"plt.ylabel('Occurrences')\n",
"plt.title('Occurance of each mu in db (rounded)')\n",
"\n",
"# Show the plot\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "adbfeb40-76bd-4224-ac45-65c7b2b2cb7b",
"metadata": {},
"outputs": [],
"source": [
"def plot_requests(object_id: int):\n",
" mu = db.mu_values[object_id]\n",
" lmb = db.lambda_values[object_id]\n",
" rq_log = np.array(cache.request_log[object_id])\n",
" df = rq_log[1:] - rq_log[:-1]\n",
" pd.DataFrame(df, columns=[f\"{object_id}, mu:{mu:.2f}, lambda: {lmb:.2f}\"]).plot()"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "1f550686-3463-4e50-be83-ceafb27512b0",
"metadata": {},
"outputs": [],
"source": [
"def print_rate(object_id: int):\n",
" # Calculate time intervals between consecutive events\n",
" intervals = np.diff(np.array(cache.request_log[object_id])) # Differences between each event time\n",
" \n",
" # Calculate the rate per second for each interval\n",
" rates = 1 / intervals # Inverse of the time interval gives rate per second\n",
" \n",
" # Optional: Calculate the average event rate over all intervals\n",
" average_rate = np.mean(rates)\n",
" print(\"Average event rate per second:\", average_rate)\n",
" print(\"The mu is: \", db.lambda_values[object_id])"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "135f4a26-a666-4fd5-8f71-1f62abd4bb81",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[LRUSimulation] Database Object Count: 100, Cache Size: 10, Eviction Strategy: EvictionStrategy.LRU\n"
]
}
],
"source": [
"print(config)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "b47990b1-0231-43ac-8bc5-8340abe4a8b3",
"metadata": {},
"outputs": [],
"source": [
"# os.makedirs(EXPERIMENT_BASE_DIR, exist_ok=True)\n",
"# folder_name = experiment_name.replace(\" \", \"_\").replace(\"(\", \"\").replace(\")\", \"\").replace(\".\", \"_\")\n",
"# folder_path = os.path.join(EXPERIMENT_BASE_DIR, folder_name)\n",
"# os.makedirs(folder_path, exist_ok=True)\n"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "db83cad4-7cc6-4702-ae3a-d1af30a561d2",
"metadata": {},
"outputs": [],
"source": [
"# file_names = os.listdir(TEMP_BASE_DIR)\n",
" \n",
"# for file_name in file_names:\n",
"# shutil.move(os.path.join(TEMP_BASE_DIR, file_name), folder_path)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "graphs",
"language": "python",
"name": "graphs"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}