Skip to content

Commit 6b14f2b

Browse files
committed
CNSM2020 branch first commit
0 parents  commit 6b14f2b

File tree

11 files changed

+713
-0
lines changed

11 files changed

+713
-0
lines changed

.gitignore

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# IDE
2+
.idea/
3+
results
4+
5+
# Byte-compiled / optimized / DLL files
6+
__pycache__/
7+
*.py[cod]
8+
*$py.class
9+
10+
# C extensions
11+
*.so
12+
13+
# Distribution / packaging
14+
.Python
15+
build/
16+
develop-eggs/
17+
dist/
18+
downloads/
19+
eggs/
20+
.eggs/
21+
lib/
22+
lib64/
23+
parts/
24+
sdist/
25+
var/
26+
wheels/
27+
*.egg-info/
28+
.installed.cfg
29+
*.egg
30+
MANIFEST
31+
32+
# PyInstaller
33+
# Usually these files are written by a python script from a template
34+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
35+
*.manifest
36+
*.spec
37+
38+
# Installer logs
39+
pip-log.txt
40+
pip-delete-this-directory.txt
41+
42+
# Unit test / coverage reports
43+
htmlcov/
44+
.tox/
45+
.coverage
46+
.coverage.*
47+
.cache
48+
nosetests.xml
49+
coverage.xml
50+
*.cover
51+
.hypothesis/
52+
.pytest_cache/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
63+
# Flask stuff:
64+
instance/
65+
.webassets-cache
66+
67+
# Scrapy stuff:
68+
.scrapy
69+
70+
# Sphinx documentation
71+
docs/_build/
72+
73+
# PyBuilder
74+
target/
75+
76+
# Jupyter Notebook
77+
.ipynb_checkpoints
78+
79+
# pyenv
80+
.python-version
81+
82+
# celery beat schedule file
83+
celerybeat-schedule
84+
85+
# SageMath parsed files
86+
*.sage.py
87+
88+
# Environments
89+
.env
90+
.venv
91+
env/
92+
venv/
93+
ENV/
94+
env.bak/
95+
venv.bak/
96+
97+
# Spyder project settings
98+
.spyderproject
99+
.spyproject
100+
101+
# Rope project settings
102+
.ropeproject
103+
104+
# mkdocs documentation
105+
/site
106+
107+
# mypy
108+
.mypy_cache/

.travis.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
language: python
2+
python:
3+
- "3.6"
4+
5+
install:
6+
- python setup.py install
7+
8+
script:
9+
- flake8 src

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# common-utils
2+
3+
Interface definition between coordination algorithms and environments. Includes a dummy environment as example.
4+
5+
<p align="center">
6+
<img src="https://raw.githubusercontent.com/RealVNF/coord-sim/master/docs/realvnf_logo.png" height="150" hspace="30"/>
7+
<img src="https://raw.githubusercontent.com/RealVNF/coord-sim/master/docs/upb.png" width="200" hspace="30"/>
8+
<img src="https://raw.githubusercontent.com/RealVNF/coord-sim/master/docs/huawei_horizontal.png" width="250" hspace="30"/>
9+
</p>
10+
11+
## Project structure
12+
13+
- `src/spinterface`: Interface definition between the scale and place algorithm and the environment.
14+
- `src/dummy-env`: Dummy environment/simulation implementation.
15+
16+
## Interface definition
17+
18+
The main interface definition is described by the `SimulatorInterface` class.
19+
20+
The interface is guided by the gym interface (`gmy.Env`),
21+
The `init()` aligns to the `reset()` function of gym and resets and
22+
initializes the environment with all necessary information including a seed.
23+
The `apply()` function reflects gym's `step()` method:
24+
It sends a placement description for all nodes and
25+
sets the new routing/scheduling.
26+
27+
To pass the information, we utilizes two data classes:
28+
29+
- `SimulatorAction` to pass the scaling and placement to the environment.
30+
- `simulatorState` to report back the current State of the environment.
31+
32+
## Installation
33+
34+
Requires [Python 3.6](https://www.python.org/downloads/release/) and (recommended) [venv](https://docs.python.org/3/library/venv.html).
35+
36+
```bash
37+
python setup.py install
38+
```
39+
40+
## Usage
41+
42+
### Interface
43+
44+
To use the interface you need to use this module and import the needed classes:
45+
46+
`from spinterface import SimulatorAction, SimulatorInterface, SimulatorState`
47+
48+
### Dummy environment
49+
50+
To use the dummy environment use:
51+
52+
`from dummy_env import DummySimulator as Simulator`
53+
54+
## Acknowledgement
55+
56+
This project has received funding from German Federal Ministry of Education and Research ([BMBF](https://www.bmbf.de/)) through Software Campus grant 01IS17046 ([RealVNF](https://realvnf.github.io/)).
57+
58+
<p align="center">
59+
<img src="https://raw.githubusercontent.com/RealVNF/coord-sim/master/docs/software_campus.png" width="200"/>
60+
<img src="https://raw.githubusercontent.com/RealVNF/coord-sim/master/docs/BMBF_sponsored_by.jpg" width="250"/>
61+
</p>

setup.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[flake8]
2+
# ignore unused imports
3+
ignore = F401
4+
max-line-length = 120

setup.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# -*- coding: utf-8 -*-
2+
from setuptools import setup, find_packages
3+
4+
5+
requirements = [
6+
'networkx',
7+
'PyYAML',
8+
'numpy'
9+
]
10+
11+
test_requirements = [
12+
'flake8'
13+
]
14+
15+
16+
setup(
17+
name='common-utils',
18+
version='1.0.0',
19+
author="RealVNF",
20+
description="Interface definition between coordination algorithms and environments. "
21+
"Includes a dummy environment.",
22+
url='https://github.com/RealVNF/common-utils',
23+
package_dir={'': 'src'},
24+
packages=find_packages('src'),
25+
install_requires=requirements + test_requirements,
26+
tests_require=test_requirements,
27+
zip_safe=False,
28+
)

src/common/__init__.py

Whitespace-only changes.

src/common/common_functionalities.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import numpy as np
2+
import os
3+
import yaml
4+
import networkx as nx
5+
from shutil import copyfile
6+
7+
# url = 'https://github.com/numpy/numpy/blob/master/numpy/random/mtrand.pyx#L778'
8+
# a threshold for floating point arithmetic error handling
9+
accuracy = np.sqrt(np.finfo(np.float64).eps)
10+
11+
12+
def normalize_scheduling_probabilities(input_list: list) -> list:
13+
""" returns a rounded off list with the sum of all elements in the list to be equal to 1.0
14+
Handles these case:
15+
1) All the elements of the list are 0 -> the Probabilities are equally distributed
16+
2) When the sum(input_list) is away from 1.0 by an offset -> each prob. is divided by sum(input_list) and
17+
the difference of the sum of this new list to 1.0 is added to the first element of the list.
18+
3) An empty list is provided as input -> simply returns an empty list.
19+
Because of [1] an error range of +-0.000000014901161193847656 in the sum has to be handled.
20+
[1]: https://stackoverflow.com/questions/588004/is-floating-point-math-broken
21+
"""
22+
23+
output_list = []
24+
# to handle the empty list case, we just return the empty list back
25+
if len(input_list) == 0:
26+
return output_list
27+
28+
offset = 1 - sum(input_list)
29+
30+
# a list with all elements 0, will be equally distributed to sum-up to 1.
31+
# sum can also be 0 if some elements of the list are negative.
32+
# In our case the list contains probabilities and they are not supposed to be negative, hence the case won't arise
33+
if sum(input_list) == 0:
34+
output_list = [round(1 / len(input_list), 10)] * len(input_list)
35+
36+
# Because of floating point precision (.59 + .33 + .08) can be equal to .99999999
37+
# So we correct the sum only if the absolute difference is more than a tolerance(0.000000014901161193847656)
38+
else:
39+
if abs(offset) > accuracy:
40+
sum_list = sum(input_list)
41+
# we divide each number in the list by the sum of the list, so that Prob. Distribution is approx. 1
42+
output_list = [round(prob / sum_list, 10) for prob in input_list]
43+
else:
44+
output_list = input_list.copy()
45+
46+
# 1 - sum(output_list) = the diff. by which the elements of the list are away from 1.0, could be +'ive /-i've
47+
new_offset = 1 - sum(output_list)
48+
if new_offset != 0:
49+
i = 0
50+
while output_list[i] + new_offset < 0:
51+
i += 1
52+
# the difference is added/subtracted from the 1st element of the list, which is also rounded to 2 decimal points
53+
output_list[i] = output_list[i] + new_offset
54+
assert abs(1 - sum(output_list)) < accuracy, "Sum of list not equal to 1.0"
55+
return output_list
56+
57+
58+
def create_input_file(target_dir, num_ingress, algo):
59+
input_file_loc = f"{target_dir}/input.yaml"
60+
os.makedirs(f"{target_dir}", exist_ok=True)
61+
with open(input_file_loc, "w") as f:
62+
inputs = {"num_ingress": num_ingress, "algorithm": algo}
63+
yaml.dump(inputs, f, default_flow_style=False)
64+
65+
66+
def num_ingress(network_path):
67+
no_ingress = 0
68+
network = nx.read_graphml(network_path, node_type=int)
69+
for node in network.nodes(data=True):
70+
if node[1]["NodeType"] == "Ingress":
71+
no_ingress += 1
72+
return no_ingress
73+
74+
75+
def copy_input_files(target_dir, network_path, service_path, sim_config_path):
76+
"""Create the results directory and copy input files"""
77+
new_network_path = f"{target_dir}/{os.path.basename(network_path)}"
78+
new_service_path = f"{target_dir}/{os.path.basename(service_path)}"
79+
new_sim_config_path = f"{target_dir}/{os.path.basename(sim_config_path)}"
80+
81+
os.makedirs(target_dir, exist_ok=True)
82+
copyfile(network_path, new_network_path)
83+
copyfile(service_path, new_service_path)
84+
copyfile(sim_config_path, new_sim_config_path)
85+
86+
87+
def get_ingress_nodes_and_cap(network, cap=False):
88+
"""
89+
Gets a NetworkX DiGraph and returns a list of ingress nodes in the network and the largest capacity of nodes
90+
Parameters:
91+
network: NetworkX Digraph
92+
cap: boolean to return the capacity also if True
93+
Returns:
94+
ing_nodes : a list of Ingress nodes in the Network
95+
node_cap : the single largest capacity of all the nodes of the network
96+
"""
97+
ing_nodes = []
98+
node_cap = {}
99+
for node in network.nodes(data=True):
100+
node_cap[node[0]] = node[1]['cap']
101+
if node[1]["type"] == "Ingress":
102+
ing_nodes.append(node[0])
103+
if cap:
104+
return ing_nodes, node_cap
105+
else:
106+
return ing_nodes

src/dummy_env/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
""" Package to containing a dummy implementation for the environment.
2+
"""
3+
from .dummy_simulator import DummySimulator
4+
5+
__all__ = ['DummySimulator']

0 commit comments

Comments
 (0)