#Copyright(C) 2023 Intel Corporation
#SPDX-License-Identifier: Apache-2.0
#File : adapter.py
import network_gym_client.adapter
import sys
from gymnasium import spaces
import numpy as np
from pathlib import Path
import pandas as pd
import json
[docs]class Adapter(network_gym_client.adapter.Adapter):
"""network_slicing environment adapter.
Args:
Adapter (network_gym_client.adapter.Adapter): the base class
"""
def __init__(self, config_json):
"""Initialize the adapter.
Args:
config_json (json): the configuration file
"""
super().__init__(config_json)
self.env = Path(__file__).resolve().parent.name
self.size_per_feature = len(self.config_json['env_config']['slice_list'])
self.num_features = 3
num_users = 0
for item in self.config_json['env_config']['slice_list']:
num_users += item['num_users']
self.config_json['env_config']['num_users'] = num_users
rbg_size = self.get_rbg_size(self.config_json['env_config']['LTE']['resource_block_num'])
self.rbg_num = self.config_json['env_config']['LTE']['resource_block_num']/rbg_size
if config_json['env_config']['env'] != self.env:
sys.exit("[ERROR] wrong environment Adapter. Configured environment: " + str(config_json['env_config']['env']) + " != Launched environment: " + str(self.env))
def get_action_space(self):
"""Get action space for network_slicing env.
Returns:
spaces: action spaces
"""
return spaces.Box(low=0, high=1, shape=(self.size_per_feature,), dtype=np.float32)
#consistent with the get_observation function.
def get_observation_space(self):
"""Get observation space for network_slicing env.
Returns:
spaces: observation spaces
"""
# for network slicing, the user number is configured using the slice list. Cannot use the argument parser!
return spaces.Box(low=0, high=1000,
shape=(self.num_features, self.size_per_feature), dtype=np.float32)
def get_observation(self, df):
"""Prepare observation for network_slicing env.
This function should return the same number of features defined in the :meth:`get_observation_space`.
Args:
df (pd.DataFrame): the network stats measurements
Returns:
spaces: observation spaces
"""
print (df)
dl_cell_rb_usage = None
dl_network_tx_rate = None
dl_network_rate = None
dl_network_qos_rate = None
dl_network_delay_violation = None
for index, row in df.iterrows():
if row['source'] == 'lte':
if row['name'] == 'dl::cell::max_rate':
self.action_data_format = row
elif row['name'] == 'dl::cell::rb_usage':
dl_cell_rb_usage = row
elif row['source'] == 'gma':
if row['name'] == 'dl::network::tx_rate':
dl_network_tx_rate = row
elif row['name'] == 'dl::network::rate':
dl_network_rate = row
elif row['name'] == 'dl::network::qos_rate':
dl_network_qos_rate = row
elif row['name'] == 'dl::network::delay_violation':
dl_network_delay_violation = row
self.wandb_log_buffer_append(self.slice_df_to_dict(dl_cell_rb_usage))
self.wandb_log_buffer_append(self.slice_df_to_dict(dl_network_tx_rate))
self.wandb_log_buffer_append(self.slice_df_to_dict(dl_network_rate))
self.wandb_log_buffer_append(self.slice_df_to_dict(dl_network_qos_rate))
self.wandb_log_buffer_append(self.slice_df_to_dict(dl_network_delay_violation))
#warning, need to modify the following if use more than one base stations.
observation = np.vstack([pd.json_normalize(dl_network_rate["value"])["value"].to_list(), pd.json_normalize(dl_cell_rb_usage["value"])["value"].to_list(), pd.json_normalize(dl_network_delay_violation["value"])["value"].to_list()])
print('Observation --> ' + str(observation))
return observation
def get_policy(self, action):
"""Prepare the network policy for network_slicing env.
Args:
action (spaces): the action from RL agent
Returns:
json: the network policy
"""
# you may also check other constraints for action... e.g., min, max.
# TODO: the sum of action should be smaller than 1!!!! Therefore the sum of scaled_action is smaller than the rbg_num
scaled_action= np.interp(action, (0, 1), (0, self.rbg_num/self.size_per_feature))
scaled_action = np.round(scaled_action).astype(int) # force it to be an integer.
# this function will convert the action to a nested json format
policy1 = json.loads(self.action_data_format.to_json())
policy1["name"] = "drb_allocation"
for item in policy1["value"]:
item["value"] = np.zeros(len(scaled_action)).tolist()
policy2 = json.loads(self.action_data_format.to_json())
policy2["name"] = "prb_allocation"
for item in policy2["value"]:
item["value"] = scaled_action.tolist()
policy3 = json.loads(self.action_data_format.to_json())
policy3["name"] = "srb_allocation"
for item in policy3["value"]:
item["value"] = list(np.ones(len(scaled_action))*self.rbg_num)
policy = [policy1, policy2, policy3]
print('Action --> ' + str(policy))
return policy
def get_reward(self, df):
"""Prepare reward for the network_slicing env.
Args:
df (pd.DataFrame): network stats measurements
Returns:
spaces: reward spaces
"""
#TODO: add a reward function for you customized env
reward = 0
# send info to wandb
self.wandb_log_buffer_append({"reward": reward})
return reward
def slice_df_to_dict(self, df, id_name='id'):
"""Transform datatype from pandas.dataframe to dictionary.
Args:
df (pandas.dataframe): a pandas.dataframe object
description (string): a descritption for the data
Returns:
dict : converted data with dictionary format
"""
if df is None:
return {}
description = df['source'] + "::" + df['name']
get_key = lambda u, v: description+"::"+id_name+f'={u}'+"::slice"+f'={v}'
id_list = []
slice_list = []
value_list= []
for i, item in enumerate(df['id']):
for j, sub in enumerate(df['value'][i]['slice']):
id_list.append(item)
slice_list.append(sub)
value_list.append(df['value'][i]['value'][j])
#print(id_list)
#print(slice_list)
#print(value_list)
dict_key = list(map(get_key, id_list, slice_list))
#print(dict_key)
#print(value_list)
data = dict(zip(dict_key, value_list))
#print(data)
return data
def get_rbg_size (self, bandwidth):
"""Compute the resource block group size based on the bandwith (RB number).
This code is coppied from ns3.
PF type 0 allocation RBG
Args:
bandwidth (int): the resouce block number
Returns:
int: the resouce block group size
"""
# PF type 0 allocation RBG
PfType0AllocationRbg = [10,26,63,110] # see table 7.1.6.1-1 of 36.213
for i in range(len(PfType0AllocationRbg)):
if (bandwidth < PfType0AllocationRbg[i]):
return (i + 1)
return (-1)