Skip to content

Instantly share code, notes, and snippets.

@itzmeanjan
Last active July 23, 2021 15:20
Show Gist options
  • Save itzmeanjan/0a67b696ecd18b6812e806a7410d5ca6 to your computer and use it in GitHub Desktop.
Save itzmeanjan/0a67b696ecd18b6812e806a7410d5ca6 to your computer and use it in GitHub Desktop.
Using Genetic Algorithm for Random Linear Network Coding ( RLNC )'s cost-over-wire Optimisation
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "d739a48e",
"metadata": {},
"source": [
"## What's it ?\n",
"\n",
"Generates all of possible combinations of piece, generation sizes, along with cost associated with it. Cost in terms of how many bytes of data ( at min ) need to be sent over wire for transferring whole data chunk to other party, while *Random Linear Network Coding* is used."
]
},
{
"cell_type": "code",
"execution_count": 38,
"id": "d98709ac",
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"from typing import List, Tuple\n",
"from rich.console import Console\n",
"from rich.table import Table"
]
},
{
"cell_type": "code",
"execution_count": 39,
"id": "e788176e",
"metadata": {},
"outputs": [],
"source": [
"def piece_sizes(data_size: int) -> List[Tuple[int, int, int]]:\n",
" sizes = []\n",
" for piece_size in range(1, math.ceil(data_size/2)+1):\n",
" piece_count = math.ceil(data_size/piece_size)\n",
" pad_size = piece_size*piece_count - data_size\n",
" if pad_size * 2 > piece_size:\n",
" continue\n",
" sizes.append((piece_size, piece_count, pad_size))\n",
" return sizes"
]
},
{
"cell_type": "code",
"execution_count": 40,
"id": "bd1e561b",
"metadata": {},
"outputs": [],
"source": [
"def generation_sizes(piece_count: int) -> List[Tuple[int, int]]:\n",
" def is_prime(num: int) -> bool:\n",
" for i in range(2, math.ceil(num/2)+1):\n",
" if num % i == 0:\n",
" return False\n",
" return True\n",
"\n",
" if is_prime(piece_count):\n",
" return [(piece_count, 1)]\n",
" \n",
" sizes = []\n",
" for gen_size in range(2, piece_count+1):\n",
" if piece_count % gen_size != 0:\n",
" continue\n",
" sizes.append((gen_size, int(piece_count/gen_size)))\n",
" return sizes"
]
},
{
"cell_type": "code",
"execution_count": 41,
"id": "2202c387",
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"def generation_combs(data_size: int) -> List[Tuple[int, int, int, int, int]]:\n",
" comb = []\n",
" for piece_size, piece_count, pad_size in piece_sizes(data_size):\n",
" comb.extend([(piece_size, piece_count, pad_size, *i) for i in generation_sizes(piece_count)])\n",
" return comb\n",
"\n",
"def over_wire(piece_size: int, piece_count: int, pad_size: int, gen_size: int, gen_count: int) -> float:\n",
" return (gen_size + piece_size) * (gen_size + 1.6) * gen_count\n",
"\n",
"def generation_combs_with_cost(data: List[Tuple[int, int, int, int, int]]):\n",
" return [(*i, over_wire(*i)) for i in data]"
]
},
{
"cell_type": "code",
"execution_count": 42,
"id": "a27557b6",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\"> Top-N Low Cost RLNC Configurations </span>\n",
"┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n",
"┃<span style=\"font-weight: bold\"> Piece Size </span>┃<span style=\"font-weight: bold\"> Piece Count </span>┃<span style=\"font-weight: bold\"> Pad Size </span>┃<span style=\"font-weight: bold\"> Generation </span>┃<span style=\"font-weight: bold\"> Generation </span>┃<span style=\"font-weight: bold\"> Cost over </span>┃\n",
"┃<span style=\"font-weight: bold\"> (bytes) </span>┃ ┃<span style=\"font-weight: bold\"> (bytes) </span>┃<span style=\"font-weight: bold\"> Size </span>┃<span style=\"font-weight: bold\"> Count </span>┃<span style=\"font-weight: bold\"> Wire (bytes) </span>┃\n",
"┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n",
"│ 114 │ 9 │ 2 │ 9 │ 1 │ 1303.80 │\n",
"│ 128 │ 8 │ 0 │ 8 │ 1 │ 1305.60 │\n",
"│ 103 │ 10 │ 6 │ 10 │ 1 │ 1310.80 │\n",
"│ 115 │ 9 │ 11 │ 9 │ 1 │ 1314.40 │\n",
"│ 129 │ 8 │ 8 │ 8 │ 1 │ 1315.20 │\n",
"│ 104 │ 10 │ 16 │ 10 │ 1 │ 1322.40 │\n",
"│ 94 │ 11 │ 10 │ 11 │ 1 │ 1323.00 │\n",
"│ 147 │ 7 │ 5 │ 7 │ 1 │ 1324.40 │\n",
"│ 130 │ 8 │ 16 │ 8 │ 1 │ 1324.80 │\n",
"│ 116 │ 9 │ 20 │ 9 │ 1 │ 1325.00 │\n",
"└───────────────┴─────────────┴───────────────┴──────────────┴───────────────┴──────────────┘\n",
"</pre>\n"
],
"text/plain": [
"\u001b[3m Top-N Low Cost RLNC Configurations \u001b[0m\n",
"┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n",
"┃\u001b[1m \u001b[0m\u001b[1mPiece Size \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mPiece Count\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mPad Size \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mGeneration \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mGeneration \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mCost over \u001b[0m\u001b[1m \u001b[0m┃\n",
"┃\u001b[1m \u001b[0m\u001b[1m(bytes) \u001b[0m\u001b[1m \u001b[0m┃ ┃\u001b[1m \u001b[0m\u001b[1m(bytes) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mSize \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mCount \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mWire (bytes)\u001b[0m\u001b[1m \u001b[0m┃\n",
"┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n",
"│ 114 │ 9 │ 2 │ 9 │ 1 │ 1303.80 │\n",
"│ 128 │ 8 │ 0 │ 8 │ 1 │ 1305.60 │\n",
"│ 103 │ 10 │ 6 │ 10 │ 1 │ 1310.80 │\n",
"│ 115 │ 9 │ 11 │ 9 │ 1 │ 1314.40 │\n",
"│ 129 │ 8 │ 8 │ 8 │ 1 │ 1315.20 │\n",
"│ 104 │ 10 │ 16 │ 10 │ 1 │ 1322.40 │\n",
"│ 94 │ 11 │ 10 │ 11 │ 1 │ 1323.00 │\n",
"│ 147 │ 7 │ 5 │ 7 │ 1 │ 1324.40 │\n",
"│ 130 │ 8 │ 16 │ 8 │ 1 │ 1324.80 │\n",
"│ 116 │ 9 │ 20 │ 9 │ 1 │ 1325.00 │\n",
"└───────────────┴─────────────┴───────────────┴──────────────┴───────────────┴──────────────┘\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"combs = generation_combs_with_cost(generation_combs(1024))\n",
"\n",
"# top-N low cost configs\n",
"table = Table(title='Top-N Low Cost RLNC Configurations')\n",
"\n",
"table.add_column('Piece Size (bytes)')\n",
"table.add_column('Piece Count')\n",
"table.add_column('Pad Size (bytes)')\n",
"table.add_column('Generation Size')\n",
"table.add_column('Generation Count')\n",
"table.add_column('Cost over Wire (bytes)')\n",
"\n",
"for i in sorted(combs, key=lambda e: e[-1])[:10]:\n",
" table.add_row(*[f'{k:3d}' if j != 5 else f'{k:.2f}' for j, k in enumerate(i)])\n",
" \n",
"console = Console()\n",
"console.print(table)"
]
},
{
"cell_type": "code",
"execution_count": 43,
"id": "a070c986",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 4 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"%matplotlib inline\n",
"\n",
"from matplotlib import pyplot as plt\n",
"\n",
"fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)\n",
"\n",
"ax1.plot([i[-1] for i in combs], color='red')\n",
"ax2.plot([i[1] for i in combs], color='green')\n",
"ax3.plot([i[-2] for i in combs], color='blue')\n",
"ax4.plot([i[2] for i in combs], color='black')\n",
"\n",
"ax1.set(xlabel='RLNC Configuration', ylabel='Bytes over Wire')\n",
"ax2.set(xlabel='RLNC Configuration', ylabel='Piece Count')\n",
"ax3.set(xlabel='RLNC Configuration', ylabel='#-of Generations')\n",
"ax4.set(xlabel='RLNC Configuration', ylabel='Pad Bytes')\n",
"\n",
"fig.suptitle('RLNC Cost Distribution')\n",
"fig.tight_layout()\n",
"plt.show(fig)"
]
},
{
"cell_type": "markdown",
"id": "ea6f17a6",
"metadata": {},
"source": [
"## What's it ?\n",
"\n",
"**Genetic Algorithm** implementation for determining how to split a chunk of data into symbols, pieces & generations so that at cost of least extra data being sent over wire, *Random Linear Network Coding* can be applied on it."
]
},
{
"cell_type": "code",
"execution_count": 93,
"id": "e0ba26cc",
"metadata": {},
"outputs": [],
"source": [
"# dependencies\n",
"\n",
"import random\n",
"from pyeasyga import pyeasyga\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 106,
"id": "01f65e0b",
"metadata": {},
"outputs": [],
"source": [
"# initial setup\n",
"\n",
"data_size = 1024 # read bytes\n",
"generations = generation_combs(data_size)\n",
"pieces = piece_sizes(data_size)\n",
"max_pad_data = max(pieces, key=lambda e: e[-1])[-1]\n",
"min_pad_data = min(pieces, key=lambda e: e[-1])[-1]\n",
"piece_sizes_ = [i[0] for i in pieces]\n",
"population_metrics = []\n",
"\n",
"# catch current population after each iteration\n",
"def population_capture_middleware(population):\n",
" fitness = [i.fitness for i in population]\n",
" population_metrics.append({\n",
" 'max': np.max(fitness),\n",
" 'min': np.min(fitness),\n",
" 'mean': np.mean(fitness),\n",
" 'median': np.median(fitness)\n",
" })\n",
"\n",
"ga = pyeasyga.GeneticAlgorithm(data_size, \n",
" population_size=50,\n",
" generations=50,\n",
" crossover_probability=0.8, \n",
" mutation_probability=0.1,\n",
" elitism=True,\n",
" maximise_fitness=False,\n",
" capture_generation=population_capture_middleware)"
]
},
{
"cell_type": "code",
"execution_count": 107,
"id": "8ece388d",
"metadata": {},
"outputs": [],
"source": [
"# each chromosome of initial population is created\n",
"# by invoking this method\n",
"\n",
"def create_individual(data):\n",
" return random.choice(generations)\n",
"\n",
"ga.create_individual = create_individual"
]
},
{
"cell_type": "code",
"execution_count": 108,
"id": "0a27ebc1",
"metadata": {},
"outputs": [],
"source": [
"# crosses over two chromosomes of current population\n",
"# so that two child chromosomes of future population \n",
"# can be generated\n",
"\n",
"def crossover(parent_1, parent_2):\n",
" # random shift\n",
" rnd_shift = random.randrange(0, len(piece_sizes_))\n",
"\n",
" # random shift, with wrapping around\n",
" # rightwards/ leftwards, also randomly selected\n",
" idx = piece_sizes_.index(parent_1[0])\n",
" idx = ((idx + rnd_shift) if random.randint(0, 1) == 0 else (idx - rnd_shift)) % len(piece_sizes_)\n",
"\n",
" piece_size = piece_sizes_[idx]\n",
" piece_count = math.ceil(data_size/piece_size)\n",
" pad_size = piece_size*piece_count - data_size\n",
" child_1 = (piece_size, piece_count, pad_size, *random.choice(generation_sizes(piece_count)))\n",
" \n",
" # random shift, with wrapping around\n",
" # rightwards/ leftwards, also randomly selected\n",
" idx = piece_sizes_.index(parent_2[0])\n",
" idx = ((idx + rnd_shift) if random.randint(0, 1) == 0 else (idx - rnd_shift)) % len(piece_sizes_)\n",
"\n",
" piece_size = piece_sizes_[idx]\n",
" piece_count = math.ceil(data_size/piece_size)\n",
" pad_size = piece_size*piece_count - data_size\n",
" child_2 = (piece_size, piece_count, pad_size, *random.choice(generation_sizes(piece_count)))\n",
"\n",
" return child_1, child_2\n",
"\n",
"ga.crossover_function = crossover"
]
},
{
"cell_type": "code",
"execution_count": 109,
"id": "aaae7a66",
"metadata": {},
"outputs": [],
"source": [
"# randomly mutate one chromosome\n",
"\n",
"def mutate(individual):\n",
" # random shift\n",
" rnd_shift = random.randrange(0, len(piece_sizes_))\n",
"\n",
" # randomly select whether to shift leftwards/ rightwards, while also wrapping around\n",
" idx = piece_sizes_.index(individual[0])\n",
" idx = ((idx + rnd_shift) if random.randint(0, 1) == 0 else (idx - rnd_shift)) % len(piece_sizes_)\n",
"\n",
" piece_size = piece_sizes_[1]\n",
" piece_count = math.ceil(data_size/piece_size)\n",
" pad_size = piece_size*piece_count - data_size\n",
" mutated = (piece_size, piece_count, pad_size, *random.choice(generation_sizes(piece_count)))\n",
" individual = mutated\n",
" return\n",
"\n",
"ga.mutate_function = mutate"
]
},
{
"cell_type": "code",
"execution_count": 110,
"id": "a5934ee4",
"metadata": {},
"outputs": [],
"source": [
"# fitness of each chromosome is calculated\n",
"# by summing total bytes need to be sent over wire ( at min )\n",
"# & padding bytes added at end for making all pieces equally sized\n",
"\n",
"def fitness(individual, data):\n",
" return over_wire(*individual) + individual[2]\n",
"\n",
"ga.fitness_function = fitness"
]
},
{
"cell_type": "code",
"execution_count": 111,
"id": "b0faf913",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(1305.6, (128, 8, 0, 8, 1))"
]
},
"execution_count": 111,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ga.run()\n",
"ga.best_individual()"
]
},
{
"cell_type": "code",
"execution_count": 112,
"id": "d2323423",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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++233H777aSkpDB69Oiwu/jmzp3LmWeeWS90PxBNWVAtEajFixeTlZVFenp6UBZUVVUV69atY+TIkUDkLSgwT3a7MamHRjY25YWleViBSkxU9X+q+hPgDFX9ied1g6p+Ee36hcrtt99OTk4ON954I/PnzyczM5NBgwZFRaA2btxITU1NnRyALZ1YceHChRxyyCFcf/31iAiPPPIIjz76KDfddBNTp07loosuAmD06NGsW7eOfWHsWHYDFdavX9/kuq5ANWTxtMTFt3jxYg466CB69epVT6AeeeQRn4XcEPn5+VRVVTF+/Pg6dQuG5oSZ/wX4HPgtJsXRr4Bbgj6iBbAClaiIiOsev1BEHvJ/RbVyYSAzM5M//elPfP755zz77LMceuihJCUlkZ2dTVpaWqsKlPvZm3uvJQJVXV3Nd999xyGHHEJubi7nnXceDz/8MNdddx1Tp07l5ZdfJiXF5DoYNWoUYMZEhQvXInMDFhojXC6+mpoali5dypgxY+jVq1cdwauqquKmm27innsaD+J2+58OO+ywOnULhuZYUGcBw1T1dFU903lNCfqIFsAKVAKz3Hmfj8ki4f+Key677DIOPfRQysvLGTduHGDcf/369Yu4QK1du9YXbedaHoEsqOZEE65atYqysjIOPdQMT/vFL35BRUWFT5zatWvnW3f06NEADfZDff755y0eINwSgXKtlL179wa04nbv3t0sgdqwYQN79uzhoIMOomfPnnUsqE2bNlFdXc1HH33U6MzJbgSfa0FFWqDWAaHlq7DUo0MHK1AJyrkAqvqs++59Rbdq4SE5OZmHHnqI5ORkjjvuOF95awhUaWmpb3DpunXrSE1N9UUXghGo8vLyZg1cdQMkDjnkEAAOP/xwVq1axcyZM+uIE8CgQYNo3759wH6opUuXcswxx7Qo24SqBmVBQX0rqqKigr179zbLxecGSLgWlFegXIt07969fPXVVw3uY/Xq1XTu3JnBgwfXq1tLaY5AlQGLROSxtuSKiDbWgkpYxng+3xi1WkSYo48+mh07dnD66af7ylpDoKC2IV2/fj39+/cnOTnZt8w/1HzOnDl069bN99TvZeHChaSlpTFixAhf2eDBg31uPS/JycmMGDEioAW1Zs0aAN59990G679///4644W2b99OcXExItIiCwrqC5Qrxq4FtXfv3gYjA91+r1Gj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\n",
"text/plain": [
"<Figure size 432x288 with 4 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"%matplotlib inline\n",
"\n",
"fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)\n",
"\n",
"y_max = [i['max'] for i in population_metrics]\n",
"y_mean = [i['mean'] for i in population_metrics]\n",
"y_median = [i['median'] for i in population_metrics]\n",
"y_min = [i['min'] for i in population_metrics]\n",
"\n",
"ax1.plot(y_max, color='red')\n",
"ax2.plot(y_mean, color='green')\n",
"ax3.plot(y_min, color='blue')\n",
"ax4.plot(y_median, color='black')\n",
"\n",
"ax1.set(xlabel='Generations', ylabel='Max Fitness')\n",
"ax2.set(xlabel='Generations', ylabel='Mean Fitness')\n",
"ax3.set(xlabel='Generations', ylabel='Min Fitness')\n",
"ax4.set(xlabel='Generations', ylabel='Median Fitness')\n",
"\n",
"fig.suptitle('Overtime Population Fitness Convergence')\n",
"fig.tight_layout()\n",
"plt.show(fig)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6eeba852",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "playground",
"language": "python",
"name": "playground"
},
"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.7.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@itzmeanjan
Copy link
Author

You may want to read this, if you've not yet to get a better context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment