Skip to content

Commit

Permalink
Merge pull request #17 from stefanbschneider/dev
Browse files Browse the repository at this point in the history
Add demo notebook, update readme, some adjustments
  • Loading branch information
Stefan Schneider authored Dec 13, 2021
2 parents b13e8c2 + 0d14a36 commit 51238b2
Show file tree
Hide file tree
Showing 16 changed files with 6,332 additions and 121 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
results*

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Python package](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-package.yml/badge.svg)](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-package.yml)
[![Documentation Status](https://readthedocs.org/projects/mobile-env/badge/?version=latest)](https://mobile-env.readthedocs.io/en/latest/?badge=latest)
[![Publish](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-publish.yml/badge.svg)](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-publish.yml)
[![CI](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-package.yml/badge.svg)](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-package.yml)
[![PyPI](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-publish.yml/badge.svg)](https://github.com/stefanbschneider/mobile-env/actions/workflows/python-publish.yml)
[![Documentation](https://readthedocs.org/projects/mobile-env/badge/?version=latest)](https://mobile-env.readthedocs.io/en/latest/?badge=latest)
[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/stefanbschneider/mobile-env/blob/master/examples/tutorial.ipynb)


Expand All @@ -24,6 +24,8 @@ To maximize QoE globally, the policy must recognize that (1) the data rate of an

<p align="center">
<img src="https://user-images.githubusercontent.com/36734964/139288123-7732eff2-24d4-4c25-87fd-ac906f261c93.gif" width="65%"/>
<br>
<sup><a href="https://thenounproject.com/search/?q=base+station&i=1286474" target="_blank">Base station icon</a> by Clea Doltz from the Noun Project</sup>
</p>


Expand Down
Binary file added docs/images/utility.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions docs/scripts/utility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import matplotlib.pyplot as plt
import numpy as np


MIN_UTILITY = -20
MAX_UTILITY = 20


def log_utility(curr_dr):
"""
More data rate increases the utility following a log function: High initial increase, then flattens.
:param curr_dr: Current data rate
:param factor: Factor to multiply the log function with
:param add: Add to current data rate before passing to log function
:return: Utility
"""
# 4*log(0.1+x) looks good: around -10 for no dr; 0 for 0.9 dr; slightly positive for more
# 10*log10(0.1+x) is even better because it's steeper, is exactly -10 for dr=0, and flatter for larger dr
# with many UEs where each UE only gets around 0.1 data rate, 100*log(0.9+x) looks good (eg, 50 UEs on medium env)

# better: 10*log10(x) --> clip to [-20, 20]; -20 for <= 0.01 dr; +20 for >= 100 dr
# ensure min/max utility are set correctly for this utility function
assert MIN_UTILITY == -20 and MAX_UTILITY == 20, "The chosen log utility requires min/max utility to be -20/+20"
if curr_dr == 0:
return MIN_UTILITY
return np.clip(10 * np.log10(curr_dr), MIN_UTILITY, MAX_UTILITY)


dr = [i for i in range(100)]
util = [log_utility(dr) for dr in dr]
plt.plot(dr, util)
plt.xlabel("Data Rate [Mbit/s]")
plt.ylabel("Utility (QoE)")
plt.title("User utility based on data rate")
plt.show()
2,972 changes: 2,972 additions & 0 deletions examples/demo.ipynb

Large diffs are not rendered by default.

3,315 changes: 3,274 additions & 41 deletions examples/tutorial.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion mobile_env/core/arrival.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def departure(self, ue: UserEquipment) -> int:


class NoDeparture(Arrival):
"""Alle UEs immediately request service and do not depart thereafter."""
"""All UEs immediately request service and do not depart thereafter."""
def __init__(self, **kwargs):
super().__init__(**kwargs)

Expand Down
22 changes: 19 additions & 3 deletions mobile_env/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def default_config(cls):
"""Set default configuration of environment dynamics."""
# set up configuration of environment
width, height = 200, 200
ep_time = 250
ep_time = 100
config = {
# environment parameters:
"width": width,
Expand All @@ -106,6 +106,22 @@ def default_config(cls):
"movement": RandomWaypointMovement,
"utility": BoundedLogUtility,
"handler": MComCentralHandler,

# default cell config
"bs": {
"bw": 9e6,
"freq": 2500,
"tx": 30,
"height": 50
},

# default UE config
"ue": {
"velocity": 1.5,
"snr_tr": 2e-8,
"noise": 1e-9,
"height": 1.5,
},
}

# set up default configuration parameters for arrival pattern, ...
Expand Down Expand Up @@ -203,8 +219,8 @@ def check_connectivity(self, bs: BaseStation, ue: UserEquipment) -> bool:
snr = self.channel.snr(bs, ue)
return snr > ue.snr_threshold

def available_connections(self, ue: UserEquipment) -> Dict:
"""Returns dict of what basestations users could connect to."""
def available_connections(self, ue: UserEquipment) -> Set:
"""Returns set of what base stations users could connect to."""
stations = self.stations.values()
return {bs for bs in stations if self.check_connectivity(bs, ue)}

Expand Down
10 changes: 5 additions & 5 deletions mobile_env/handlers/central.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@


class MComCentralHandler(Handler):
ftrs = ["connections", "snrs", "utility"]
features = ["connections", "snrs", "utility"]

@classmethod
def ue_obs_size(cls, env) -> int:
return sum(env.feature_sizes[ftr] for ftr in cls.ftrs)
return sum(env.feature_sizes[ftr] for ftr in cls.features)

@classmethod
def action_space(cls, env) -> spaces.Dict:
def action_space(cls, env) -> spaces.MultiDiscrete:
# define multi-discrete action space for central setting
# each element of a multi-discrete action denotes one UE's decision
return spaces.MultiDiscrete([env.NUM_STATIONS + 1 for _ in env.users])

@classmethod
def observation_space(cls, env) -> spaces.Dict:
def observation_space(cls, env) -> spaces.Box:
# observation is a single vector of concatenated UE representations
size = cls.ue_obs_size(env)
return spaces.Box(low=-1.0, high=1.0, shape=(env.NUM_USERS * size,))
Expand All @@ -40,7 +40,7 @@ def observation(cls, env) -> np.ndarray:
"""Select from & flatten observations from MA setting."""
# select observations considered in the central setting
obs = {
ue_id: [obs_dict[key] for key in cls.ftrs]
ue_id: [obs_dict[key] for key in cls.features]
for ue_id, obs_dict in env.features().items()
}

Expand Down
17 changes: 11 additions & 6 deletions mobile_env/handlers/handler.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
import abc
from typing import Dict
from abc import abstractclassmethod

from gym.spaces.space import Space


class Handler:
"""Defines Gym interface methods called by core simulation."""

@abstractclassmethod
@classmethod
@abc.abstractmethod
def action_space(cls, env) -> Space:
"""Defines action space for passed environment."""
pass

@abstractclassmethod
@classmethod
@abc.abstractmethod
def observation_space(cls, env) -> Space:
"""Defines observation space for passed environment."""
pass

@abstractclassmethod
@classmethod
@abc.abstractmethod
def action(cls, env, action) -> Dict[int, int]:
"""Transform passed action(s) to dict shape expected by simulation."""
pass

@abstractclassmethod
@classmethod
@abc.abstractmethod
def observation(cls, env):
"""Computes observations for agent."""
pass

@abstractclassmethod
@classmethod
@abc.abstractmethod
def reward(cls, env):
"""Computes rewards for agent."""
pass
Expand Down
8 changes: 5 additions & 3 deletions mobile_env/handlers/multi_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@


class MComMAHandler(Handler):
ftrs = ["connections", "snrs", "utility", "bcast", "stations_connected"]
features = [
"connections", "snrs", "utility", "bcast", "stations_connected"
]

@classmethod
def ue_obs_size(cls, env) -> int:
return sum(env.feature_sizes[ftr] for ftr in cls.ftrs)
return sum(env.feature_sizes[ftr] for ftr in cls.features)

@classmethod
def action_space(cls, env) -> spaces.Dict:
Expand Down Expand Up @@ -73,7 +75,7 @@ def observation(cls, env) -> Dict[int, np.ndarray]:

# select observations for multi-agent setting from base feature set
obs = {
ue_id: [obs_dict[key] for key in cls.ftrs]
ue_id: [obs_dict[key] for key in cls.features]
for ue_id, obs_dict in features.items()
}

Expand Down
18 changes: 0 additions & 18 deletions mobile_env/scenarios/large.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,3 @@ def __init__(self, config={}):
]

super().__init__(stations, ues, config)

@classmethod
def default_config(cls):
config = super().default_config()
config.update({
"bs": {"bw": 9e6, "freq": 2500, "tx": 30, "height": 50}
})
config.update(
{
"ue": {
"velocity": 1.5,
"snr_tr": 2e-8,
"noise": 1e-9,
"height": 1.5,
}
}
)
return config
18 changes: 0 additions & 18 deletions mobile_env/scenarios/medium.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,3 @@ def __init__(self, config={}):
]

super().__init__(stations, ues, config)

@classmethod
def default_config(cls):
config = super().default_config()
config.update({
"bs": {"bw": 9e6, "freq": 2500, "tx": 30, "height": 50}
})
config.update(
{
"ue": {
"velocity": 1.5,
"snr_tr": 2e-8,
"noise": 1e-9,
"height": 1.5,
}
}
)
return config
23 changes: 2 additions & 21 deletions mobile_env/scenarios/small.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ def __init__(self, config={}):
# set unspecified parameters to default configuration
config = {**self.default_config(), **config}

stations = [(110, 130), (65, 80), (120, 30)]
stations = [(x, y) for x, y in stations]
station_pos = [(110, 130), (65, 80), (120, 30)]
stations = [
BaseStation(bs_id, pos, **config["bs"])
for bs_id, pos in enumerate(stations)
for bs_id, pos in enumerate(station_pos)
]
num_ues = 5
ues = [
Expand All @@ -20,21 +19,3 @@ def __init__(self, config={}):
]

super().__init__(stations, ues, config)

@classmethod
def default_config(cls):
config = super().default_config()
config.update({
"bs": {"bw": 9e6, "freq": 2500, "tx": 30, "height": 50}
})
config.update(
{
"ue": {
"velocity": 1.5,
"snr_tr": 2e-8,
"noise": 1e-9,
"height": 1.5,
}
}
)
return config
File renamed without changes.
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
setup(
name="mobile-env",
# always use higher version in dev than what's on PyPI
version="2.0.0dev",
version="0.3.0",
author="Stefan Schneider, Stefan Werner",
description="mobile-env: An Open Environment for Autonomous Coordination in Mobile Networks",
description="mobile-env: An Open Environment for Autonomous Coordination in Wireless Mobile Networks",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/stefanbschneider/mobile-env",
Expand Down

0 comments on commit 51238b2

Please sign in to comment.