├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── How to generate states.ipynb ├── How to use environment.ipynb ├── How_To_Create_Clusters.ipynb ├── LICENSE ├── README.md ├── drqn.py ├── multi_user_network_env.py ├── poster.pdf ├── state_input_dqn.png └── train.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /drqn.pyc 2 | -------------------------------------------------------------------------------- /How to generate states.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How to generate states\n", 8 | "1. “Layer 1 Input”: The variable x n (t) to “the DDQN is size 2K + 2 vector”. Initial K + 1\n", 9 | "input entries indicate the action taken at time t − 1. Specifically, if the user has not\n", 10 | "transmitted at time slot t − 1, the first entrance is set to 1 and the subsequent K entries are\n", 11 | "set to 0. The following K input entries are the capacity of each channel. The last input is 1\n", 12 | "if ACK signal has been received. Else, if communication has failed or no communication\n", 13 | "has been made, and it is set to 0.\n", 14 | "\n", 15 | "Since i have to feed the states in the DQN , first I created state for an user\n", 16 | "![alt text](state_input_dqn.png \"Title\")\n", 17 | "This is the state format for a user in one time slot in the environment with 3 users and 2 channels" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "# imports\n", 27 | "from multi_user_network_env import env_network\n", 28 | "import numpy as np\n", 29 | "import matplotlib.pyplot as plt" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 2, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "NUM_USERS = 3\n", 39 | "NUM_CHANNELS = 2\n", 40 | "ATTEMPT_PROB = 1" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "#initializing the environment\n", 50 | "env = env_network(NUM_USERS,NUM_CHANNELS,ATTEMPT_PROB)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 5, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "#It creates a one hot vector of a number as num with size as len\n", 60 | "def one_hot(num,len):\n", 61 | " assert num >=0 and num < len ,\"error\"\n", 62 | " vec = np.zeros([len],np.int32)\n", 63 | " vec[num] = 1\n", 64 | " return vec" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 6, 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "data": { 74 | "text/plain": [ 75 | "array([0, 0, 0, 0, 0, 1], dtype=int32)" 76 | ] 77 | }, 78 | "execution_count": 6, 79 | "metadata": {}, 80 | "output_type": "execute_result" 81 | } 82 | ], 83 | "source": [ 84 | "one_hot(5,6) " 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "One-hot vector of ```len (6)``` having ```value (5)```" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 7, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "# generates states from action and obs as returned from environment\n", 101 | "def state_generator(action,obs):\n", 102 | " input_vector = []\n", 103 | " if action is None:\n", 104 | " print ('None')\n", 105 | " sys.exit()\n", 106 | " for user_i in range(action.size):\n", 107 | " input_vector_i = one_hot(action[user_i],NUM_CHANNELS+1)\n", 108 | " channel_alloc = obs[-1]\n", 109 | " input_vector_i = np.append(input_vector_i,channel_alloc)\n", 110 | " input_vector_i = np.append(input_vector_i,int(obs[user_i][0])) #ACK\n", 111 | " input_vector.append(input_vector_i)\n", 112 | " return input_vector" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 8, 118 | "metadata": {}, 119 | "outputs": [ 120 | { 121 | "name": "stdout", 122 | "output_type": "stream", 123 | "text": [ 124 | "[(0, 0.0), (0, 0.0), (1, 1.0), array([1, 0], dtype=int32)]\n" 125 | ] 126 | } 127 | ], 128 | "source": [ 129 | "# user 1 and 2 takes action 1 and user 3 takes action 2 \n", 130 | "action = np.array([1,1,2])\n", 131 | "obs = env.step(action)\n", 132 | "print obs" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 11, 138 | "metadata": {}, 139 | "outputs": [ 140 | { 141 | "name": "stdout", 142 | "output_type": "stream", 143 | "text": [ 144 | "[array([0, 1, 0, 1, 0, 0]), array([0, 1, 0, 1, 0, 0]), array([0, 0, 1, 1, 0, 1])]\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "states = state_generator(action, obs)\n", 150 | "print states\n", 151 | "assert len(states) == NUM_USERS" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "```[array([0, 1, 0, 1, 0, 0])```\n", 159 | "```array([0, 1, 0, 1, 0, 0])```\n", 160 | "```array([0, 0, 1, 1, 0, 1])]```\n", 161 | "\n", 162 | "Each array represents states for each user in th format given above\n", 163 | "\n", 164 | "Since there are 3 users , ```state_generator(action, obs)``` returned 3 arrays" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "Rest of the training is in ```train.py``` file in this repository" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [] 180 | } 181 | ], 182 | "metadata": { 183 | "kernelspec": { 184 | "display_name": "Python 3", 185 | "language": "python", 186 | "name": "python3" 187 | }, 188 | "language_info": { 189 | "codemirror_mode": { 190 | "name": "ipython", 191 | "version": 2 192 | }, 193 | "file_extension": ".py", 194 | "mimetype": "text/x-python", 195 | "name": "python", 196 | "nbconvert_exporter": "python", 197 | "pygments_lexer": "ipython2", 198 | "version": "2.7.13" 199 | } 200 | }, 201 | "nbformat": 4, 202 | "nbformat_minor": 2 203 | } 204 | -------------------------------------------------------------------------------- /How to use environment.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# import the env_network from multi_user_network_env.py\n", 10 | "from multi_user_network_env import env_network\n", 11 | "import numpy as np\n", 12 | "import matplotlib.pyplot as plt" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 3, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "NUM_USERS = 3\n", 22 | "NUM_CHANNELS = 2\n", 23 | "ATTEMPT_PROB = 1" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 4, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "#initializing the environment\n", 33 | "env = env_network(NUM_USERS,NUM_CHANNELS,ATTEMPT_PROB)\n" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 43, 39 | "metadata": {}, 40 | "outputs": [ 41 | { 42 | "name": "stdout", 43 | "output_type": "stream", 44 | "text": [ 45 | "[2 0 1]\n" 46 | ] 47 | } 48 | ], 49 | "source": [ 50 | "# To sample random actions from action_space\n", 51 | "action = env.sample()\n", 52 | "print action" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "# How to take actions\n", 60 | "Here action = ``` [1 2 0] ``` means 1st channel will be accessed by user 1, 2nd channel will be accessed by user 2 and user 3 will not take any action. \n", 61 | "Since we have just sampled the actions uniformly for each user, now we have to take these actions .For that\n", 62 | "use ```env.step()``` function and pass these actions" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 45, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "[(1, 1.0), (0, 0.0), (1, 1.0), array([0, 0], dtype=int32)]\n" 75 | ] 76 | } 77 | ], 78 | "source": [ 79 | "obs = env.step(action)\n", 80 | "print obs" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "## Observation Format\n", 88 | "\n", 89 | "For ```action = [1,2,0]``` obs is ```[(1, 1.0), (1, 1.0), (0, 0.0), array([0, 0], dtype=int32)]```\n", 90 | "\n", 91 | "The format of obs is ```[(ACK1,REWARD1),(ACK2,REWARD2),(ACK3,REWARD3), ...,(ACKn,REWARDn) , (CAP_CHANNEL1,CAP_CHANNEL2,...,CAP_CHANNEL_k)]```.\n", 92 | "\n", 93 | "When we pass actions to the evironment , it takes these actions and returns the immediate reward as well as acknowledgement of the channel.Finally it also returns the residual capacity of the channel(remaining capacity).\n", 94 | "\n", 95 | "\n", 96 | "Here 1,2,3 represents user 1, user 2 and user 3 respectively for the first n tuples where n is number of users and k is number of channels.\n", 97 | "Last element is an array ```[CAP_CHANNEL1, CAP_CHANNEL2, CAP_CHANNEL_k]``` denotes the remaining channel capacity or the fact that channel is available or not.\n", 98 | "\n", 99 | "Since both channels were available at the beginning , user 1 and 2 allocates channel 1 and 2 respectively and user 3 remains idle. This can be concluded by the resulting output where there is (ACK, REWARD) pair as ```(1 , 1.0)``` for user 1 and 2 and is ```(0 , 0.0)``` for user 3. \n", 100 | "Both the channels are allocated by user 1 and 2 therefore last element is ```array([0,0])```.\n", 101 | "\n" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 11, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "[(0, 0.0), (0, 0.0), (1, 1.0), array([1, 0], dtype=int32)]\n" 114 | ] 115 | } 116 | ], 117 | "source": [ 118 | "# user 1 and 2 takes action 1 and user 3 takes action 2 \n", 119 | "action = np.array([1,1,2])\n", 120 | "obs = env.step(action)\n", 121 | "print obs\n" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "We can also take our own actions.But first make sure to convert it to numpy array" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 35, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "name": "stdout", 138 | "output_type": "stream", 139 | "text": [ 140 | "[1.0, 1.0, 0.0]\n" 141 | ] 142 | } 143 | ], 144 | "source": [ 145 | "# to take reward from obs\n", 146 | "rewards = [i[1] for i in obs[:NUM_USERS]]\n", 147 | "print rewards" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 38, 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "name": "stdout", 157 | "output_type": "stream", 158 | "text": [ 159 | "[1 1 1]\n", 160 | "[(0, 0.0), (0, 0.0), (0, 0.0), array([1, 1], dtype=int32)]\n", 161 | "0.0\n", 162 | "***************\n", 163 | "[1 1 0]\n", 164 | "[(0, 0.0), (0, 0.0), (0, 0.0), array([1, 1], dtype=int32)]\n", 165 | "0.0\n", 166 | "***************\n", 167 | "[2 2 0]\n", 168 | "[(0, 0.0), (0, 0.0), (0, 0.0), array([1, 1], dtype=int32)]\n", 169 | "0.0\n", 170 | "***************\n", 171 | "[1 1 2]\n", 172 | "[(0, 0.0), (0, 0.0), (1, 1.0), array([1, 0], dtype=int32)]\n", 173 | "1.0\n", 174 | "***************\n", 175 | "[2 2 0]\n", 176 | "[(0, 0.0), (0, 0.0), (0, 0.0), array([1, 1], dtype=int32)]\n", 177 | "0.0\n", 178 | "***************\n" 179 | ] 180 | } 181 | ], 182 | "source": [ 183 | "TIME_SLOTS = 5\n", 184 | "total_rewards = []\n", 185 | "for i in range(TIME_SLOTS):\n", 186 | " action = env.sample()\n", 187 | " print action\n", 188 | " obs = env.step(action)\n", 189 | " print obs\n", 190 | " rewards = [i[1] for i in obs[:NUM_USERS]]\n", 191 | " reward_sum = np.sum(rewards)\n", 192 | " print reward_sum\n", 193 | " total_rewards.append(reward_sum)\n", 194 | " print \"***************\"" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 50, 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "#Lets take the action for 50 more time slots\n", 204 | "TIME_SLOTS = 50\n", 205 | "total_rewards = []\n", 206 | "for i in range(TIME_SLOTS):\n", 207 | " action = env.sample()\n", 208 | " #print action\n", 209 | " obs = env.step(action)\n", 210 | " #print obs\n", 211 | " rewards = [i[1] for i in obs[:NUM_USERS]]\n", 212 | " reward_sum = np.sum(rewards)\n", 213 | " #print reward_sum\n", 214 | " total_rewards.append(reward_sum)\n", 215 | " #print \"***************\"" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": 51, 221 | "metadata": {}, 222 | "outputs": [ 223 | { 224 | "data": { 225 | "image/png": "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\ngg==\n", 226 | "text/plain": [ 227 | "" 228 | ] 229 | }, 230 | "metadata": {}, 231 | "output_type": "display_data" 232 | } 233 | ], 234 | "source": [ 235 | "plt.plot(np.arange(TIME_SLOTS), total_rewards,'r+')\n", 236 | "plt.show()" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": {}, 242 | "source": [ 243 | "The graph shows total reward generated per time_slot for 50 slots.\n", 244 | "\n", 245 | "Here reward ```0.0``` means no user was able to send the packet and both the channels were free while reward ```2.0``` means both the channels were being used without collision and any one user was not sending the packets. " 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [] 252 | } 253 | ], 254 | "metadata": { 255 | "kernelspec": { 256 | "display_name": "Python 2", 257 | "language": "python", 258 | "name": "python2" 259 | }, 260 | "language_info": { 261 | "codemirror_mode": { 262 | "name": "ipython", 263 | "version": 2 264 | }, 265 | "file_extension": ".py", 266 | "mimetype": "text/x-python", 267 | "name": "python", 268 | "nbconvert_exporter": "python", 269 | "pygments_lexer": "ipython2", 270 | "version": "2.7.13" 271 | } 272 | }, 273 | "nbformat": 4, 274 | "nbformat_minor": 2 275 | } 276 | -------------------------------------------------------------------------------- /How_To_Create_Clusters.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | " # CLUSTERING ALGORITHM FOR WIRELESS SENSORY NODES FOR DISTRIBUTED COMPUTATION OF DRQN\n", 8 | " \n", 9 | " ### This algo works in following way:\n", 10 | " #### 1] Pick a random node from the set.\n", 11 | " #### 2] This node connects to the nearest 3 neighbors.\n", 12 | " #### 3] Set the virtual distance to infinity for these nodes.\n", 13 | " #### 4] Repeat step 2 and step 3 untill all the nodes are assigned.\n", 14 | " \n", 15 | " ### There are two methods to execute this; DFS and BFS\n", 16 | " " 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 77, 22 | "metadata": { 23 | "collapsed": true 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "import random \n", 28 | "import matplotlib.pyplot as plt\n", 29 | "import numpy as np\n", 30 | "import operator\n", 31 | "from collections import deque" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "## set parameters here: " 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 104, 44 | "metadata": { 45 | "collapsed": true 46 | }, 47 | "outputs": [], 48 | "source": [ 49 | "nodes = 10 #number of nodes on the canvas\n", 50 | "infi = 10000 #infinity: set this value more than W* root(2) i.e. max possible distance on canvas\n", 51 | "W = 80 #width of the canvas\n", 52 | "CoverageRadius = 6 #coverage radius of a node, this is not taken into computation but used for plotting \n", 53 | "PosOfNodes = [] #creates list of nodes" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "## function : generate_nodes on random positions on canvas of width 'W'" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 105, 66 | "metadata": { 67 | "collapsed": false 68 | }, 69 | "outputs": [], 70 | "source": [ 71 | "def generate_nodes():\n", 72 | " for i in range((nodes)):\n", 73 | " x = random.randint(0,W)\n", 74 | " y = random.randint(0,W)\n", 75 | " pnt = (x,y)\n", 76 | " PosOfNodes.append(pnt)\n", 77 | " \n", 78 | " print(PosOfNodes)\n", 79 | " " 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 106, 85 | "metadata": { 86 | "collapsed": false 87 | }, 88 | "outputs": [ 89 | { 90 | "name": "stdout", 91 | "output_type": "stream", 92 | "text": [ 93 | "[(80, 58), (46, 31), (41, 48), (5, 20), (37, 27), (27, 37), (25, 64), (67, 9), (67, 58), (17, 39)]\n" 94 | ] 95 | } 96 | ], 97 | "source": [ 98 | "generate_nodes()" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "## Run the cell below to generate a StartNode which act as propogating point of clustering" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 107, 111 | "metadata": { 112 | "collapsed": false 113 | }, 114 | "outputs": [ 115 | { 116 | "data": { 117 | "text/plain": [ 118 | "8" 119 | ] 120 | }, 121 | "execution_count": 107, 122 | "metadata": {}, 123 | "output_type": "execute_result" 124 | } 125 | ], 126 | "source": [ 127 | "StartNode = random.randint(1,nodes)\n", 128 | "StartNode" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "## Function : convert list of points to dictionary" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 108, 141 | "metadata": { 142 | "collapsed": true 143 | }, 144 | "outputs": [], 145 | "source": [ 146 | "def generate_position_dict():\n", 147 | " positions = dict()\n", 148 | " for i in PosOfNodes:\n", 149 | " p = dict()\n", 150 | " for j in PosOfNodes:\n", 151 | " if i[0] == j[0]:\n", 152 | " p[j[1]] = 0\n", 153 | " positions[i[0]] = p\n", 154 | " \n", 155 | " print(positions)\n", 156 | " \n" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "## function call : generate_position_dict" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 109, 169 | "metadata": { 170 | "collapsed": false 171 | }, 172 | "outputs": [ 173 | { 174 | "name": "stdout", 175 | "output_type": "stream", 176 | "text": [ 177 | "{80: {58: 0}, 17: {39: 0}, 67: {9: 0, 58: 0}, 5: {20: 0}, 25: {64: 0}, 41: {48: 0}, 27: {37: 0}, 46: {31: 0}, 37: {27: 0}}\n" 178 | ] 179 | } 180 | ], 181 | "source": [ 182 | "generate_position_dict()" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "## Function : plot fully connected graph for all nodes" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 110, 195 | "metadata": { 196 | "collapsed": false 197 | }, 198 | "outputs": [], 199 | "source": [ 200 | "def plot_fc():\n", 201 | " N = nodes\n", 202 | " x = np.zeros(N)\n", 203 | " y = np.zeros(N)\n", 204 | " for i in range(nodes):\n", 205 | " x[i] = PosOfNodes[i][0]\n", 206 | " y[i] = PosOfNodes[i][1]\n", 207 | "\n", 208 | " colors = np.random.rand(N)\n", 209 | " area = (CoverageRadius*2)**2 # 0 to 15 point radii\n", 210 | "\n", 211 | "\n", 212 | "\n", 213 | " for i in PosOfNodes:\n", 214 | " plt.text(i[0],i[1],i)\n", 215 | " for j in PosOfNodes:\n", 216 | " plt.plot([i[0], j[0]], [i[1], j[1]],'black',lw='0.01')\n", 217 | "\n", 218 | " #print(PosOfNodes[2],PosOfNodes[3])\n", 219 | " #plt.plot([PosOfNodes[2][0], PosOfNodes[3][0]], [PosOfNodes[2][1], PosOfNodes[3][1]])\n", 220 | " plt.scatter(x, y, s=area, c=colors, alpha=0.5)\n", 221 | " plt.show()\n" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "## plots fully connected graph" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": 111, 234 | "metadata": { 235 | "collapsed": false 236 | }, 237 | "outputs": [ 238 | { 239 | "data": { 240 | "image/png": "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\n", 241 | "text/plain": [ 242 | "" 243 | ] 244 | }, 245 | "metadata": {}, 246 | "output_type": "display_data" 247 | } 248 | ], 249 | "source": [ 250 | "plot_fc()" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 113, 256 | "metadata": { 257 | "collapsed": true 258 | }, 259 | "outputs": [], 260 | "source": [ 261 | "q = deque()\n" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 114, 267 | "metadata": { 268 | "collapsed": true 269 | }, 270 | "outputs": [], 271 | "source": [ 272 | "def dist(a,b):\n", 273 | " return ((a[0]-b[0])**2 + (a[1]-b[1])**2) ** (1/2.0) \n", 274 | "\n", 275 | "# a and b are in form of:\n", 276 | "#a = (3,4)\n", 277 | "#b = (5,8)\n" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": { 283 | "collapsed": true 284 | }, 285 | "source": [ 286 | "## function : create distance matrix " 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 115, 292 | "metadata": { 293 | "collapsed": false 294 | }, 295 | "outputs": [], 296 | "source": [ 297 | "distance_matrix = {}\n", 298 | "row = {}\n", 299 | "\n", 300 | "def create_dist_mat(PosOfNodes):\n", 301 | " print(PosOfNodes)\n", 302 | " for i in PosOfNodes:\n", 303 | " print(i)\n", 304 | " row = {}\n", 305 | " for j in PosOfNodes:\n", 306 | " row[j] = dist(i,j)\n", 307 | " distance_matrix[i] = row\n", 308 | " \n", 309 | " print(distance_matrix)" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "## function call : create_dist_mat" 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": 116, 322 | "metadata": { 323 | "collapsed": false 324 | }, 325 | "outputs": [ 326 | { 327 | "name": "stdout", 328 | "output_type": "stream", 329 | "text": [ 330 | "[(80, 58), (46, 31), (41, 48), (5, 20), (37, 27), (27, 37), (25, 64), (67, 9), (67, 58), (17, 39)]\n", 331 | "(80, 58)\n", 332 | "(46, 31)\n", 333 | "(41, 48)\n", 334 | "(5, 20)\n", 335 | "(37, 27)\n", 336 | "(27, 37)\n", 337 | "(25, 64)\n", 338 | "(67, 9)\n", 339 | "(67, 58)\n", 340 | "(17, 39)\n", 341 | "{(5, 20): {(5, 20): 0.0, (25, 64): 48.33218389437829, (46, 31): 42.44997055358225, (80, 58): 84.07734534343957, (17, 39): 22.47220505424423, (37, 27): 32.7566787083184, (67, 58): 72.71863585079137, (27, 37): 27.80287754891569, (67, 9): 62.96824596572466, (41, 48): 45.60701700396552}, (25, 64): {(5, 20): 48.33218389437829, (25, 64): 0.0, (46, 31): 39.11521443121589, (80, 58): 55.326304774492215, (17, 39): 26.248809496813376, (37, 27): 38.897300677553446, (67, 58): 42.42640687119285, (27, 37): 27.073972741361768, (67, 9): 69.20260110718382, (41, 48): 22.627416997969522}, (46, 31): {(5, 20): 42.44997055358225, (25, 64): 39.11521443121589, (46, 31): 0.0, (80, 58): 43.41658669218482, (17, 39): 30.083217912982647, (37, 27): 9.848857801796104, (67, 58): 34.20526275297414, (27, 37): 19.924858845171276, (67, 9): 30.4138126514911, (41, 48): 17.72004514666935}, (80, 58): {(5, 20): 84.07734534343957, (25, 64): 55.326304774492215, (46, 31): 43.41658669218482, (80, 58): 0.0, (17, 39): 65.80273550544841, (37, 27): 53.009433122794285, (67, 58): 13.0, (27, 37): 57.0087712549569, (67, 9): 50.695167422546305, (41, 48): 40.26164427839479}, (17, 39): {(5, 20): 22.47220505424423, (25, 64): 26.248809496813376, (46, 31): 30.083217912982647, (80, 58): 65.80273550544841, (17, 39): 0.0, (37, 27): 23.323807579381203, (67, 58): 53.48831648126533, (27, 37): 10.198039027185569, (67, 9): 58.309518948453004, (41, 48): 25.632011235952593}, (37, 27): {(5, 20): 32.7566787083184, (25, 64): 38.897300677553446, (46, 31): 9.848857801796104, (80, 58): 53.009433122794285, (17, 39): 23.323807579381203, (37, 27): 0.0, (67, 58): 43.139309220245984, (27, 37): 14.142135623730951, (67, 9): 34.9857113690718, (41, 48): 21.37755832643195}, (67, 58): {(5, 20): 72.71863585079137, (25, 64): 42.42640687119285, (46, 31): 34.20526275297414, (80, 58): 13.0, (17, 39): 53.48831648126533, (37, 27): 43.139309220245984, (67, 58): 0.0, (27, 37): 45.17742799230607, (67, 9): 49.0, (41, 48): 27.85677655436824}, (27, 37): {(5, 20): 27.80287754891569, (25, 64): 27.073972741361768, (46, 31): 19.924858845171276, (80, 58): 57.0087712549569, (17, 39): 10.198039027185569, (37, 27): 14.142135623730951, (67, 58): 45.17742799230607, (27, 37): 0.0, (67, 9): 48.82622246293481, (41, 48): 17.804493814764857}, (67, 9): {(5, 20): 62.96824596572466, (25, 64): 69.20260110718382, (46, 31): 30.4138126514911, (80, 58): 50.695167422546305, (17, 39): 58.309518948453004, (37, 27): 34.9857113690718, (67, 58): 49.0, (27, 37): 48.82622246293481, (67, 9): 0.0, (41, 48): 46.87216658103186}, (41, 48): {(5, 20): 45.60701700396552, (25, 64): 22.627416997969522, (46, 31): 17.72004514666935, (80, 58): 40.26164427839479, (17, 39): 25.632011235952593, (37, 27): 21.37755832643195, (67, 58): 27.85677655436824, (27, 37): 17.804493814764857, (67, 9): 46.87216658103186, (41, 48): 0.0}}\n" 342 | ] 343 | } 344 | ], 345 | "source": [ 346 | "create_dist_mat(PosOfNodes)" 347 | ] 348 | }, 349 | { 350 | "cell_type": "markdown", 351 | "metadata": {}, 352 | "source": [ 353 | "## set values of cluster_assigned and cluster_head to 1 with length of nodes" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 117, 359 | "metadata": { 360 | "collapsed": true 361 | }, 362 | "outputs": [], 363 | "source": [ 364 | "cluster_head = np.ones(nodes)\n", 365 | "cluster_assigned = np.ones(nodes)" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": {}, 371 | "source": [ 372 | "## set value of StartNode to infinity (this positions the node to virtually infinite distance)" 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": 118, 378 | "metadata": { 379 | "collapsed": false 380 | }, 381 | "outputs": [], 382 | "source": [ 383 | "def reset():\n", 384 | " cluster_head = np.ones(nodes)\n", 385 | " cluster_assigned = np.ones(nodes)\n", 386 | " \n", 387 | " cluster_head[StartNode] = infi\n", 388 | " cluster_assigned[StartNode] = infi\n", 389 | "\n", 390 | " #cluster_assigned, cluster_head\n", 391 | " \n", 392 | " " 393 | ] 394 | }, 395 | { 396 | "cell_type": "code", 397 | "execution_count": 119, 398 | "metadata": { 399 | "collapsed": true 400 | }, 401 | "outputs": [], 402 | "source": [ 403 | "reset()" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 120, 409 | "metadata": { 410 | "collapsed": true 411 | }, 412 | "outputs": [], 413 | "source": [ 414 | "sums = 0\n", 415 | "cluster = {}" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": 121, 421 | "metadata": { 422 | "collapsed": false 423 | }, 424 | "outputs": [], 425 | "source": [ 426 | "def connect_dfs(q,sums):\n", 427 | " \n", 428 | " check_it = (infi*nodes)\n", 429 | " if(int(sums) == int(check_it)):\n", 430 | " return None\n", 431 | " print(check_it)\n", 432 | " while(len(q)>0):\n", 433 | " \n", 434 | " var = q.popleft()\n", 435 | " print(var)\n", 436 | " connected = deque()\n", 437 | " #sorted_x = sorted(distance_matrix[var].items(), key=operator.itemgetter(1))\n", 438 | " d = {}\n", 439 | " for i in range(nodes):\n", 440 | " d[i] = distance_matrix[PosOfNodes[var]][PosOfNodes[i]]\n", 441 | " #print(d) \n", 442 | " \n", 443 | " for i in range(nodes):\n", 444 | " d[i] = d[i] * cluster_assigned[i]\n", 445 | " #print(d)\n", 446 | " \n", 447 | " sorted_x = sorted(d.items(), key=operator.itemgetter(1))\n", 448 | " #print(sorted_x)\n", 449 | " \n", 450 | " connected.append(sorted_x[1][0])\n", 451 | " connected.append(sorted_x[2][0])\n", 452 | " connected.append(sorted_x[3][0])\n", 453 | " cluster[var] = list(connected)\n", 454 | " cluster_assigned[sorted_x[1][0]] = infi\n", 455 | " cluster_assigned[sorted_x[2][0]] = infi\n", 456 | " cluster_assigned[sorted_x[3][0]] = infi\n", 457 | " print(cluster_assigned)\n", 458 | " sums = np.sum(cluster_assigned)\n", 459 | " print(connected)\n", 460 | " print('sums',sums)\n", 461 | " if(int(sums) == int(check_it)):\n", 462 | " break\n", 463 | " connect_dfs(connected,sums)\n", 464 | " \n", 465 | " \n", 466 | " return sums\n", 467 | " " 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": 122, 473 | "metadata": { 474 | "collapsed": false 475 | }, 476 | "outputs": [ 477 | { 478 | "data": { 479 | "text/plain": [ 480 | "deque([8])" 481 | ] 482 | }, 483 | "execution_count": 122, 484 | "metadata": {}, 485 | "output_type": "execute_result" 486 | } 487 | ], 488 | "source": [ 489 | "q.append(StartNode)\n", 490 | "q" 491 | ] 492 | }, 493 | { 494 | "cell_type": "code", 495 | "execution_count": 123, 496 | "metadata": { 497 | "collapsed": true 498 | }, 499 | "outputs": [], 500 | "source": [ 501 | "def connect_bfs(q):\n", 502 | " sums = 0\n", 503 | " check_it = float(infi*nodes)\n", 504 | " print('check_it',check_it)\n", 505 | " var = 0\n", 506 | " print('len of q', len(q))\n", 507 | " while(len(q)>0):\n", 508 | " print('bfs')\n", 509 | " connected = deque()\n", 510 | " \n", 511 | " var = q.popleft()\n", 512 | " print('var',var)\n", 513 | " \n", 514 | " d = {}\n", 515 | " for i in range(nodes):\n", 516 | " d[i] = distance_matrix[PosOfNodes[var]][PosOfNodes[i]]\n", 517 | " #print('d',d) \n", 518 | " \n", 519 | " for i in range(nodes):\n", 520 | " d[i] = d[i] * cluster_assigned[i]\n", 521 | " #print('d',d)\n", 522 | " \n", 523 | " sorted_x = sorted(d.items(), key=operator.itemgetter(1))\n", 524 | " #print(sorted_x)\n", 525 | " \n", 526 | " connected.append(sorted_x[1][0]) \n", 527 | " connected.append(sorted_x[2][0])\n", 528 | " connected.append(sorted_x[3][0])\n", 529 | " \n", 530 | " cluster[var] = list(connected)\n", 531 | " print(cluster)\n", 532 | " cluster_assigned[sorted_x[1][0]] = infi\n", 533 | " cluster_assigned[sorted_x[2][0]] = infi\n", 534 | " cluster_assigned[sorted_x[3][0]] = infi\n", 535 | " print('CA',cluster_assigned)\n", 536 | " sums = np.sum(cluster_assigned)\n", 537 | " \n", 538 | " print('connected',connected)\n", 539 | " print('sums',sums)\n", 540 | " if(int(sums) == int(check_it)):\n", 541 | " break\n", 542 | " \n", 543 | " if(int(sums) == int(check_it)):\n", 544 | " return None\n", 545 | " \n", 546 | " connect_bfs(connected)\n", 547 | " print('recc')\n", 548 | " #return sums\n", 549 | " " 550 | ] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": 143, 555 | "metadata": { 556 | "collapsed": false 557 | }, 558 | "outputs": [], 559 | "source": [ 560 | "def plot_cluster():\n", 561 | " N = nodes\n", 562 | " x = np.zeros(N)\n", 563 | " y = np.zeros(N)\n", 564 | " for i in range(nodes):\n", 565 | " x[i] = PosOfNodes[i][0]\n", 566 | " y[i] = PosOfNodes[i][1]\n", 567 | "\n", 568 | " colors = np.random.rand(N)\n", 569 | " area = (CoverageRadius*2)**2 # 0 to 15 point radii\n", 570 | "\n", 571 | "\n", 572 | "\n", 573 | " for i in cluster:\n", 574 | " plt.text(PosOfNodes[i][0],PosOfNodes[i][1],i, fontsize=20)\n", 575 | " for j in cluster[i]:\n", 576 | " plt.text(PosOfNodes[j][0],PosOfNodes[j][1],j, fontsize=12)\n", 577 | " plt.plot([PosOfNodes[i][0], PosOfNodes[j][0]], [PosOfNodes[i][1], PosOfNodes[j][1]],'black',lw='0.5')\n", 578 | "\n", 579 | " #print(PosOfNodes[2],PosOfNodes[3])\n", 580 | " #plt.plot([PosOfNodes[2][0], PosOfNodes[3][0]], [PosOfNodes[2][1], PosOfNodes[3][1]])\n", 581 | " plt.scatter(x, y, s=area, c=colors, alpha=0.5)\n", 582 | " plt.show()\n" 583 | ] 584 | }, 585 | { 586 | "cell_type": "code", 587 | "execution_count": 125, 588 | "metadata": { 589 | "collapsed": true 590 | }, 591 | "outputs": [], 592 | "source": [ 593 | "def run_dfs_cluster():\n", 594 | " \n", 595 | " q.append(StartNode)\n", 596 | " reset()\n", 597 | " connect_dfs(q,sums)\n", 598 | " plot_cluster()\n", 599 | " \n" 600 | ] 601 | }, 602 | { 603 | "cell_type": "code", 604 | "execution_count": 126, 605 | "metadata": { 606 | "collapsed": true 607 | }, 608 | "outputs": [], 609 | "source": [ 610 | "def run_bfs_cluster():\n", 611 | " \n", 612 | " q.append(StartNode)\n", 613 | " reset()\n", 614 | " connect_bfs(q)\n", 615 | " plot_cluster()" 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": 144, 621 | "metadata": { 622 | "collapsed": false 623 | }, 624 | "outputs": [ 625 | { 626 | "name": "stdout", 627 | "output_type": "stream", 628 | "text": [ 629 | "check_it 100000.0\n", 630 | "len of q 1\n", 631 | "bfs\n", 632 | "var 8\n", 633 | "{8: [0, 2, 1], 4: [7, 1, 5], 6: [9, 8, 3]}\n", 634 | "CA [ 10000. 10000. 10000. 10000. 10000. 10000. 10000. 10000. 10000.\n", 635 | " 10000.]\n", 636 | "connected deque([0, 2, 1])\n", 637 | "sums 100000.0\n" 638 | ] 639 | }, 640 | { 641 | "data": { 642 | "image/png": "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\n", 643 | "text/plain": [ 644 | "" 645 | ] 646 | }, 647 | "metadata": {}, 648 | "output_type": "display_data" 649 | } 650 | ], 651 | "source": [ 652 | "run_bfs_cluster()" 653 | ] 654 | }, 655 | { 656 | "cell_type": "code", 657 | "execution_count": null, 658 | "metadata": { 659 | "collapsed": false 660 | }, 661 | "outputs": [], 662 | "source": [ 663 | "run_dfs_cluster()" 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": null, 669 | "metadata": { 670 | "collapsed": true 671 | }, 672 | "outputs": [], 673 | "source": [] 674 | } 675 | ], 676 | "metadata": { 677 | "anaconda-cloud": {}, 678 | "kernelspec": { 679 | "display_name": "Python [conda root]", 680 | "language": "python", 681 | "name": "conda-root-py" 682 | }, 683 | "language_info": { 684 | "codemirror_mode": { 685 | "name": "ipython", 686 | "version": 3 687 | }, 688 | "file_extension": ".py", 689 | "mimetype": "text/x-python", 690 | "name": "python", 691 | "nbconvert_exporter": "python", 692 | "pygments_lexer": "ipython3", 693 | "version": "3.5.2" 694 | } 695 | }, 696 | "nbformat": 4, 697 | "nbformat_minor": 1 698 | } 699 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Shikhar Tiwari 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep-Reinforcement-Learning-for-Dynamic-Spectrum-Access 2 | 3 | ## Dependencies 4 | 5 | 6 | 1. python [link](https://www.python.org) 7 | 2. matplotlib [link](https://matplotlib.org/) 8 | 3. tensorflow > 1.0 [link](https://www.tensorflow.org/) 9 | 4. numpy [link](https://www.numpy.org/) 10 | 5. jupyter [link](http://jupyter.org/) 11 | 12 | We recommend to install with [Anaconda](https://anaconda.org/anaconda/python) 13 | 14 | 15 | ### To train the DQN ,run on terminal 16 | ```bash 17 | git clone https://github.com/shkrwnd/Deep-Reinforcement-Learning-for-Dynamic-Spectrum-Access.git 18 | cd Deep-Reinforcement-Learning-for-Dynamic-Spectrum-Access 19 | python train.py 20 | ``` 21 | 22 | To understand the code , I have provided jupyter notebooks: 23 | 1. How to use environment.ipynb 24 | 2. How to generate states.ipynb 25 | 3. How_to_create_cluster.ipynb 26 | 27 | To run notebook,run on terminal 28 | ```bash 29 | jupyter notebook 30 | ``` 31 | Default browser will open ipynb files. Run each command one by one 32 | 33 | 34 | This work is an inspiration from the paper 35 | ``` 36 | O. Naparstek and K. Cohen, “Deep multi-user reinforcement learning for dynamic spectrum access in multichannel wireless 37 | networks,” to appear in Proc. of the IEEE Global Communications Conference (GLOBECOM), Dec. 2017 38 | ``` 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /drqn.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | class QNetwork: 4 | def __init__(self, learning_rate=0.01, state_size=4, 5 | action_size=2, hidden_size=10, step_size=1 , 6 | name='QNetwork'): 7 | 8 | with tf.variable_scope(name): 9 | self.inputs_ = tf.placeholder(tf.float32, [None,step_size, state_size], name='inputs_') 10 | self.actions_ = tf.placeholder(tf.int32, [None], name='actions') 11 | one_hot_actions = tf.one_hot(self.actions_, action_size) 12 | 13 | 14 | self.targetQs_ = tf.placeholder(tf.float32, [None], name='target') 15 | ########################################## 16 | 17 | self.lstm = tf.contrib.rnn.BasicLSTMCell(hidden_size) 18 | 19 | self.lstm_out, self.state = tf.nn.dynamic_rnn(self.lstm,self.inputs_,dtype=tf.float32) 20 | 21 | self.reduced_out = self.lstm_out[:,-1,:] 22 | self.reduced_out = tf.reshape(self.reduced_out,shape=[-1,hidden_size]) 23 | 24 | ######################################### 25 | 26 | #self.w1 = tf.Variable(tf.random_uniform([state_size,hidden_size])) 27 | #self.b1 = tf.Variable(tf.constant(0.1,shape=[hidden_size])) 28 | #self.h1 = tf.matmul(self.inputs_,self.w1) + self.b1 29 | #self.h1 = tf.nn.relu(self.h1) 30 | #self.h1 = tf.contrib.layers.layer_norm(self.h1) 31 | #''' 32 | 33 | self.w2 = tf.Variable(tf.random_uniform([hidden_size,hidden_size])) 34 | self.b2 = tf.Variable(tf.constant(0.1,shape=[hidden_size])) 35 | self.h2 = tf.matmul(self.reduced_out,self.w2) + self.b2 36 | self.h2 = tf.nn.relu(self.h2) 37 | self.h2 = tf.contrib.layers.layer_norm(self.h2) 38 | 39 | self.w3 = tf.Variable(tf.random_uniform([hidden_size,action_size])) 40 | self.b3 = tf.Variable(tf.constant(0.1,shape=[action_size])) 41 | self.output = tf.matmul(self.h2,self.w3) + self.b3 42 | 43 | 44 | #self.output = tf.contrib.layers.layer_norm(self.output) 45 | 46 | 47 | ''' 48 | self.fc1 = tf.contrib.layers.fully_connected(self.inputs_, hidden_size) 49 | self.fc2 = tf.contrib.layers.fully_connected(self.fc1, hidden_size) 50 | 51 | 52 | self.output = tf.contrib.layers.fully_connected(self.fc2, action_size,activation_fn=None) 53 | 54 | ''' 55 | self.Q = tf.reduce_sum(tf.multiply(self.output, one_hot_actions), axis=1) 56 | 57 | self.loss = tf.reduce_mean(tf.square(self.targetQs_ - self.Q)) 58 | self.opt = tf.train.AdamOptimizer(learning_rate).minimize(self.loss) 59 | 60 | 61 | 62 | 63 | 64 | from collections import deque 65 | 66 | class Memory(): 67 | def __init__(self, max_size=1000): 68 | self.buffer = deque(maxlen=max_size) 69 | 70 | def add(self, experience): 71 | self.buffer.append(experience) 72 | 73 | def sample(self, batch_size,step_size): 74 | idx = np.random.choice(np.arange(len(self.buffer)-step_size), 75 | size=batch_size, replace=False) 76 | 77 | res = [] 78 | 79 | for i in idx: 80 | temp_buffer = [] 81 | for j in range(step_size): 82 | temp_buffer.append(self.buffer[i+j]) 83 | res.append(temp_buffer) 84 | return res 85 | 86 | 87 | -------------------------------------------------------------------------------- /multi_user_network_env.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | import sys 4 | import os 5 | 6 | 7 | TIME_SLOTS = 1 8 | NUM_CHANNELS = 3 9 | NUM_USERS = 5 10 | ATTEMPT_PROB = 0.6 11 | GAMMA = 0.90 12 | 13 | class env_network: 14 | def __init__(self,num_users,num_channels,attempt_prob): 15 | self.ATTEMPT_PROB = attempt_prob 16 | self.NUM_USERS = num_users 17 | self.NUM_CHANNELS = num_channels 18 | self.REWARD = 1 19 | 20 | #self.channel_alloc_freq = 21 | self.action_space = np.arange(self.NUM_CHANNELS+1) 22 | self.users_action = np.zeros([self.NUM_USERS],np.int32) 23 | self.users_observation = np.zeros([self.NUM_USERS],np.int32) 24 | def reset(self): 25 | pass 26 | def sample(self): 27 | x = np.random.choice(self.action_space,size=self.NUM_USERS) 28 | return x 29 | def step(self,action): 30 | #print 31 | assert (action.size) == self.NUM_USERS, "action and user should have same dim {}".format(action) 32 | channel_alloc_frequency = np.zeros([self.NUM_CHANNELS + 1],np.int32) #0 for no chnnel access 33 | obs = [] 34 | reward = np.zeros([self.NUM_USERS]) 35 | j = 0 36 | for each in action: 37 | prob = random.uniform(0,1) 38 | if prob <= self.ATTEMPT_PROB: 39 | self.users_action[j] = each # action 40 | 41 | channel_alloc_frequency[each]+=1 42 | j+=1 43 | 44 | for i in range(1,len(channel_alloc_frequency)): 45 | if channel_alloc_frequency[i] > 1: 46 | channel_alloc_frequency[i] = 0 47 | #print channel_alloc_frequency 48 | for i in range(len(action)): 49 | 50 | self.users_observation[i] = channel_alloc_frequency[self.users_action[i]] 51 | if self.users_action[i] ==0: #accessing no channel 52 | self.users_observation[i] = 0 53 | if self.users_observation[i] == 1: 54 | reward[i] = 1 55 | obs.append((self.users_observation[i],reward[i])) 56 | residual_channel_capacity = channel_alloc_frequency[1:] 57 | residual_channel_capacity = 1-residual_channel_capacity 58 | obs.append(residual_channel_capacity) 59 | return obs 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shkrwnd/Deep-Reinforcement-Learning-for-Dynamic-Spectrum-Access/2219c28b6fd12c416dfd32763641b0786c32bafc/poster.pdf -------------------------------------------------------------------------------- /state_input_dqn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shkrwnd/Deep-Reinforcement-Learning-for-Dynamic-Spectrum-Access/2219c28b6fd12c416dfd32763641b0786c32bafc/state_input_dqn.png -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | from multi_user_network_env import env_network 2 | from drqn import QNetwork,Memory 3 | import numpy as np 4 | import sys 5 | import matplotlib.pyplot as plt 6 | from collections import deque 7 | import os 8 | import tensorflow as tf 9 | import time 10 | 11 | TIME_SLOTS = 100000 # number of time-slots to run simulation 12 | NUM_CHANNELS = 2 # Total number of channels 13 | NUM_USERS = 3 # Total number of users 14 | ATTEMPT_PROB = 1 # attempt probability of ALOHA based models 15 | 16 | #It creates a one hot vector of a number as num with size as len 17 | def one_hot(num,len): 18 | assert num >=0 and num < len ,"error" 19 | vec = np.zeros([len],np.int32) 20 | vec[num] = 1 21 | return vec 22 | 23 | 24 | 25 | #generates next-state from action and observation 26 | def state_generator(action,obs): 27 | input_vector = [] 28 | if action is None: 29 | print ('None') 30 | sys.exit() 31 | for user_i in range(action.size): 32 | input_vector_i = one_hot(action[user_i],NUM_CHANNELS+1) 33 | channel_alloc = obs[-1] 34 | input_vector_i = np.append(input_vector_i,channel_alloc) 35 | input_vector_i = np.append(input_vector_i,int(obs[user_i][0])) #ACK 36 | input_vector.append(input_vector_i) 37 | return input_vector 38 | 39 | memory_size = 1000 #size of experience replay deque 40 | batch_size = 6 # Num of batches to train at each time_slot 41 | pretrain_length = batch_size #this is done to fill the deque up to batch size before training 42 | hidden_size = 128 #Number of hidden neurons 43 | learning_rate = 0.0001 #learning rate 44 | explore_start = .02 #initial exploration rate 45 | explore_stop = 0.01 #final exploration rate 46 | decay_rate = 0.0001 #rate of exponential decay of exploration 47 | gamma = 0.9 #discount factor 48 | noise = 0.1 49 | step_size=1+2+2 #length of history sequence for each datapoint in batch 50 | state_size = 2 *(NUM_CHANNELS + 1) #length of input (2 * k + 2) :k = NUM_CHANNELS 51 | action_size = NUM_CHANNELS+1 #length of output (k+1) 52 | alpha=0 #co-operative fairness constant 53 | beta = 1 #Annealing constant for Monte - Carlo 54 | 55 | # reseting default tensorflow computational graph 56 | tf.reset_default_graph() 57 | 58 | #initializing the environment 59 | env = env_network(NUM_USERS,NUM_CHANNELS,ATTEMPT_PROB) 60 | 61 | #initializing deep Q network 62 | mainQN = QNetwork(name='main',hidden_size=hidden_size,learning_rate=learning_rate,step_size=step_size,state_size=state_size,action_size=action_size) 63 | 64 | #this is experience replay buffer(deque) from which each batch will be sampled and fed to the neural network for training 65 | memory = Memory(max_size=memory_size) 66 | 67 | #this is our input buffer which will be used for predicting next Q-values 68 | history_input = deque(maxlen=step_size) 69 | 70 | #to sample random actions for each user 71 | action = env.sample() 72 | 73 | # 74 | obs = env.step(action) 75 | state = state_generator(action,obs) 76 | reward = [i[1] for i in obs[:NUM_USERS]] 77 | 78 | 79 | ############################################## 80 | for ii in range(pretrain_length*step_size*5): 81 | 82 | action = env.sample() 83 | obs = env.step(action) # obs is a list of tuple with [[(ACK,REW) for each user] ,CHANNEL_RESIDUAL_CAPACITY_VECTOR] 84 | next_state = state_generator(action,obs) 85 | reward = [i[1] for i in obs[:NUM_USERS]] 86 | memory.add((state,action,reward,next_state)) 87 | state = next_state 88 | history_input.append(state) 89 | 90 | ############################################## 91 | 92 | def get_states(batch): 93 | states = [] 94 | for i in batch: 95 | states_per_batch = [] 96 | for step_i in i: 97 | states_per_step = [] 98 | for user_i in step_i[0]: 99 | states_per_step.append(user_i) 100 | states_per_batch.append(states_per_step) 101 | states.append(states_per_batch) 102 | 103 | return states 104 | 105 | def get_actions(batch): 106 | actions = [] 107 | for each in batch: 108 | actions_per_batch = [] 109 | for step_i in each: 110 | actions_per_step = [] 111 | for user_i in step_i[1]: 112 | actions_per_step.append(user_i) 113 | actions_per_batch.append(actions_per_step) 114 | actions.append(actions_per_batch) 115 | 116 | return actions 117 | 118 | def get_rewards(batch): 119 | rewards = [] 120 | for each in batch: 121 | rewards_per_batch = [] 122 | for step_i in each: 123 | rewards_per_step = [] 124 | for user_i in step_i[2]: 125 | rewards_per_step.append(user_i) 126 | rewards_per_batch.append(rewards_per_step) 127 | rewards.append(rewards_per_batch) 128 | return rewards 129 | 130 | def get_next_states(batch): 131 | next_states = [] 132 | for each in batch: 133 | next_states_per_batch = [] 134 | for step_i in each: 135 | next_states_per_step = [] 136 | for user_i in step_i[3]: 137 | next_states_per_step.append(user_i) 138 | next_states_per_batch.append(next_states_per_step) 139 | next_states.append(next_states_per_batch) 140 | return next_states 141 | 142 | def get_states_user(batch): 143 | states = [] 144 | for user in range(NUM_USERS): 145 | states_per_user = [] 146 | for each in batch: 147 | states_per_batch = [] 148 | for step_i in each: 149 | 150 | try: 151 | states_per_step = step_i[0][user] 152 | 153 | except IndexError: 154 | print (step_i) 155 | print ("-----------") 156 | 157 | print ("eror") 158 | 159 | '''for i in batch: 160 | print i 161 | print "**********"''' 162 | sys.exit() 163 | states_per_batch.append(states_per_step) 164 | states_per_user.append(states_per_batch) 165 | states.append(states_per_user) 166 | #print len(states) 167 | return np.array(states) 168 | 169 | def get_actions_user(batch): 170 | actions = [] 171 | for user in range(NUM_USERS): 172 | actions_per_user = [] 173 | for each in batch: 174 | actions_per_batch = [] 175 | for step_i in each: 176 | actions_per_step = step_i[1][user] 177 | actions_per_batch.append(actions_per_step) 178 | actions_per_user.append(actions_per_batch) 179 | actions.append(actions_per_user) 180 | return np.array(actions) 181 | 182 | def get_rewards_user(batch): 183 | rewards = [] 184 | for user in range(NUM_USERS): 185 | rewards_per_user = [] 186 | for each in batch: 187 | rewards_per_batch = [] 188 | for step_i in each: 189 | rewards_per_step = step_i[2][user] 190 | rewards_per_batch.append(rewards_per_step) 191 | rewards_per_user.append(rewards_per_batch) 192 | rewards.append(rewards_per_user) 193 | return np.array(rewards) 194 | 195 | 196 | 197 | 198 | # 199 | def get_next_states_user(batch): 200 | next_states = [] 201 | for user in range(NUM_USERS): 202 | next_states_per_user = [] 203 | for each in batch: 204 | next_states_per_batch = [] 205 | for step_i in each: 206 | next_states_per_step = step_i[3][user] 207 | next_states_per_batch.append(next_states_per_step) 208 | next_states_per_user.append(next_states_per_batch) 209 | next_states.append(next_states_per_user) 210 | return np.array(next_states) 211 | 212 | 213 | 214 | interval = 1 # debug interval 215 | 216 | # saver object to save the checkpoints of the DQN to disk 217 | saver = tf.train.Saver() 218 | 219 | #initializing the session 220 | sess = tf.Session() 221 | 222 | #initialing all the tensorflow variables 223 | sess.run(tf.global_variables_initializer()) 224 | 225 | 226 | #list of total rewards 227 | total_rewards = [] 228 | 229 | # cumulative reward 230 | cum_r = [0] 231 | 232 | # cumulative collision 233 | cum_collision = [0] 234 | 235 | ########################################################################## 236 | #### main simulation loop ######## 237 | 238 | 239 | for time_step in range(TIME_SLOTS): 240 | 241 | # changing beta at every 50 time-slots 242 | if time_step %50 == 0: 243 | if time_step < 5000: 244 | beta -=0.001 245 | 246 | #curent exploration probability 247 | explore_p = explore_stop + (explore_start - explore_stop) * np.exp(-decay_rate*time_step) 248 | 249 | 250 | # Exploration 251 | if explore_p > np.random.rand(): 252 | #random action sampling 253 | action = env.sample() 254 | print ("explored") 255 | 256 | # Exploitation 257 | else: 258 | #initializing action vector 259 | action = np.zeros([NUM_USERS],dtype=np.int32) 260 | 261 | #converting input history into numpy array 262 | state_vector = np.array(history_input) 263 | 264 | #print np.array(history_input) 265 | print ("///////////////") 266 | 267 | for each_user in range(NUM_USERS): 268 | 269 | #feeding the input-history-sequence of (t-1) slot for each user seperately 270 | feed = {mainQN.inputs_:state_vector[:,each_user].reshape(1,step_size,state_size)} 271 | 272 | #predicting Q-values of state respectively 273 | Qs = sess.run(mainQN.output,feed_dict=feed) 274 | #print Qs 275 | 276 | # Monte-carlo sampling from Q-values (Boltzmann distribution) 277 | ################################################################################## 278 | prob1 = (1-alpha)*np.exp(beta*Qs) 279 | 280 | # Normalizing probabilities of each action with temperature (beta) 281 | prob = prob1/np.sum(np.exp(beta*Qs)) + alpha/(NUM_CHANNELS+1) 282 | #print prob 283 | 284 | # This equation is as given in the paper : 285 | # Deep Multi-User Reinforcement Learning for 286 | # Distributed Dynamic Spectrum Access : 287 | # @Oshri Naparstek and Kobi Cohen (equation 12) 288 | ######################################################################################## 289 | 290 | # choosing action with max probability 291 | action[each_user] = np.argmax(prob,axis=1) 292 | 293 | #action[each_user] = np.argmax(Qs,axis=1) 294 | if time_step % interval == 0: 295 | print (state_vector[:,each_user]) 296 | print (Qs) 297 | print (prob, np.sum(np.exp(beta*Qs))) 298 | 299 | # taking action as predicted from the q values and receiving the observation from thr envionment 300 | obs = env.step(action) # obs is a list of tuple with [(ACK,REW) for each user ,(CHANNEL_RESIDUAL_CAPACITY_VECTOR)] 301 | 302 | print (action) 303 | print (obs) 304 | 305 | # Generate next state from action and observation 306 | next_state = state_generator(action,obs) 307 | print (next_state) 308 | 309 | # reward for all users given by environment 310 | reward = [i[1] for i in obs[:NUM_USERS]] 311 | 312 | # calculating sum of rewards 313 | sum_r = np.sum(reward) 314 | 315 | #calculating cumulative reward 316 | cum_r.append(cum_r[-1] + sum_r) 317 | 318 | #If NUM_CHANNELS = 2 , total possible reward = 2 , therefore collision = (2 - sum_r) or (NUM_CHANNELS - sum_r) 319 | collision = NUM_CHANNELS - sum_r 320 | 321 | #calculating cumulative collision 322 | cum_collision.append(cum_collision[-1] + collision) 323 | 324 | 325 | ############################# 326 | # for co-operative policy we will give reward-sum to each user who have contributed 327 | # to play co-operatively and rest 0 328 | for i in range(len(reward)): 329 | if reward[i] > 0: 330 | reward[i] = sum_r 331 | ############################# 332 | 333 | 334 | total_rewards.append(sum_r) 335 | print (reward) 336 | 337 | 338 | # add new experiences into the memory buffer as (state, action , reward , next_state) for training 339 | memory.add((state,action,reward,next_state)) 340 | 341 | 342 | state = next_state 343 | #add new experience to generate input-history sequence for next state 344 | history_input.append(state) 345 | 346 | 347 | # Training block starts 348 | ################################################################################### 349 | 350 | # sampling a batch from memory buffer for training 351 | batch = memory.sample(batch_size,step_size) 352 | 353 | # matrix of rank 4 354 | # shape [NUM_USERS,batch_size,step_size,state_size] 355 | states = get_states_user(batch) 356 | 357 | # matrix of rank 3 358 | # shape [NUM_USERS,batch_size,step_size] 359 | actions = get_actions_user(batch) 360 | 361 | # matrix of rank 3 362 | # shape [NUM_USERS,batch_size,step_size] 363 | rewards = get_rewards_user(batch) 364 | 365 | # matrix of rank 4 366 | # shape [NUM_USERS,batch_size,step_size,state_size] 367 | next_states = get_next_states_user(batch) 368 | 369 | # Converting [NUM_USERS,batch_size] -> [NUM_USERS * batch_size] 370 | # first two axis are converted into first axis 371 | 372 | states = np.reshape(states,[-1,states.shape[2],states.shape[3]]) 373 | actions = np.reshape(actions,[-1,actions.shape[2]]) 374 | rewards = np.reshape(rewards,[-1,rewards.shape[2]]) 375 | next_states = np.reshape(next_states,[-1,next_states.shape[2],next_states.shape[3]]) 376 | 377 | # creating target vector (possible best action) 378 | target_Qs = sess.run(mainQN.output,feed_dict={mainQN.inputs_:next_states}) 379 | 380 | 381 | # Q_target = reward + gamma * Q_next 382 | targets = rewards[:,-1] + gamma * np.max(target_Qs,axis=1) 383 | 384 | # calculating loss and train using Adam optimizer 385 | loss, _ = sess.run([mainQN.loss,mainQN.opt], 386 | feed_dict={mainQN.inputs_:states, 387 | mainQN.targetQs_:targets, 388 | mainQN.actions_:actions[:,-1]}) 389 | 390 | 391 | # Training block ends 392 | ######################################################################################## 393 | 394 | if time_step %5000 == 4999: 395 | plt.figure(1) 396 | plt.subplot(211) 397 | #plt.plot(np.arange(1000),total_rewards,"r+") 398 | #plt.xlabel('Time Slots') 399 | #plt.ylabel('total rewards') 400 | #plt.title('total rewards given per time_step') 401 | #plt.show() 402 | plt.plot(np.arange(5001),cum_collision,"r-") 403 | plt.xlabel('Time Slot') 404 | plt.ylabel('cumulative collision') 405 | #plt.show() 406 | plt.subplot(212) 407 | plt.plot(np.arange(5001),cum_r,"r-") 408 | plt.xlabel('Time Slot') 409 | plt.ylabel('Cumulative reward of all users') 410 | #plt.title('Cumulative reward of all users') 411 | plt.show() 412 | 413 | total_rewards = [] 414 | cum_r = [0] 415 | cum_collision = [0] 416 | saver.save(sess,'checkpoints/dqn_multi-user.ckpt') 417 | #print time_step,loss , sum(reward) , Qs 418 | 419 | print ("*************************************************") 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | --------------------------------------------------------------------------------