Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Mobility Model and Extend RADP Library with Essential Helper Functions #13

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ce31359
Fixed requirements issue
tanzim10 Sep 10, 2024
73e1546
added handler to the mobility directory
WaterMenon09 Sep 10, 2024
c4d379a
added blank test file for handler
WaterMenon09 Sep 10, 2024
bb87c98
fixed import issue in handler
WaterMenon09 Sep 10, 2024
046535c
Deleted some dump files
tanzim10 Sep 10, 2024
a4b69ac
CCO file issue resolved
tanzim10 Sep 10, 2024
5cffe2e
Fixed CCO Notebook
tanzim10 Sep 10, 2024
5397fa5
Kept the changes in the notebook locally
tanzim10 Sep 10, 2024
c912b3b
Updated requirements issue from CI test
tanzim10 Sep 10, 2024
9fb5d73
Fixed scikit learn issue
tanzim10 Sep 11, 2024
f51a6b6
Merge pull request #1 from tanzim10/tanzim-patch-1
tanzim10 Sep 11, 2024
92bd8af
Merge branch 'lf-connectivity:main' into main
tanzim10 Sep 13, 2024
42d8bc5
UE Generation Handler using distributions data
tanzim10 Sep 15, 2024
c0f8526
Modified ue_tracks as per PR Requirements
tanzim10 Sep 20, 2024
bfa3c08
Fixed a bug in the ue_tracks, generate_as_lon_lat_points
tanzim10 Sep 20, 2024
a828bc1
Solving the CI test issue
tanzim10 Sep 20, 2024
27b57cc
Addes new file ue_tracks_param to handle job data
tanzim10 Sep 23, 2024
0c5ac92
Added missing import in ue_tracks file
tanzim10 Sep 23, 2024
99d969c
Formatted code with Black
tanzim10 Sep 24, 2024
ac7f9fe
Test ue generation handler (#4)
WaterMenon09 Sep 24, 2024
a28cd66
Formatted all the codes using Black
tanzim10 Sep 24, 2024
0419f1b
Created Parameter Regression model and updated requirements.txt in di…
tanzim10 Sep 24, 2024
66323df
Added Docstring for ParameterRegression
tanzim10 Sep 24, 2024
b6b5167
Added unittest cases for param regression
tanzim10 Sep 25, 2024
b750a19
Fixed scipy requirement.txt issue
tanzim10 Sep 25, 2024
5ebb2fb
Fixed Dependency issue for scipy import
tanzim10 Sep 25, 2024
32a047e
Removed package version to solve issue
tanzim10 Sep 25, 2024
1fa7351
Created helper functions in radp library and mobility_model notebook
tanzim10 Sep 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion notebooks/coo_with_radp_digital_twin.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
"version": "3.11.5"
},
"varInspector": {
"cols": {
Expand Down
833 changes: 833 additions & 0 deletions notebooks/mobility_model.ipynb

Large diffs are not rendered by default.

240 changes: 240 additions & 0 deletions notebooks/radp_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import fastkml
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
import pandas as pd
import rasterio.features
Expand All @@ -25,6 +26,9 @@
NormMethod,
)
from radp.digital_twin.utils.gis_tools import GISTools
from radp.digital_twin.mobility.ue_tracks_params import UETracksGenerationParams
from radp.digital_twin.mobility.ue_tracks import UETracksGenerator
from radp.digital_twin.mobility.param_regression import ParameterRegression

Boundary = Union[geometry.Polygon, geometry.MultiPolygon]
KML_NS = "{http://www.opengis.net/kml/2.2}"
Expand Down Expand Up @@ -936,3 +940,239 @@ def rfco_to_best_server_shapes(
key=lambda x: x[1],
)
return shapes


# Mobility Model helper functions


def get_ue_data(params: dict) -> pd.DataFrame:
"""
Generates user equipment (UE) tracks data using specified simulation parameters.

This function initializes a UETracksGenerationParams object using the provided parameters
and then iterates over batches generated by the UETracksGenerator. Each batch of UE tracks
data is consolidated into a single DataFrame which captures mobility tracks across multiple
ticks and batches, as per the defined parameters.

Using the UETracksGenerator, the UE tracks are returned in form of a dataframe
The Dataframe is arranged as follows:

+------------+------------+-----------+------+
| mock_ue_id | lon | lat | tick |
+============+============+===========+======+
| 0 | 102.219377 | 33.674572 | 0 |
| 1 | 102.415954 | 33.855534 | 0 |
| 2 | 102.545935 | 33.878075 | 0 |
| 0 | 102.297766 | 33.575942 | 1 |
| 1 | 102.362725 | 33.916477 | 1 |
| 2 | 102.080675 | 33.832793 | 1 |
+------------+------------+-----------+------+
"""

# Initialize the UE data
data = UETracksGenerationParams(params)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call this ue_tracks_params


ue_tracks_generation = pd.DataFrame() # Initialize an empty DataFrame
for ue_tracks_generation_batch in UETracksGenerator.generate_as_lon_lat_points(
rng_seed=data.rng_seed,
lon_x_dims=data.lon_x_dims,
lon_y_dims=data.lon_y_dims,
num_ticks=data.num_ticks,
num_UEs=data.num_UEs,
num_batches=data.num_batches,
alpha=data.alpha,
variance=data.variance,
min_lat=data.min_lat,
max_lat=data.max_lat,
min_lon=data.min_lon,
max_lon=data.max_lon,
mobility_class_distribution=data.mobility_class_distribution,
mobility_class_velocities=data.mobility_class_velocities,
mobility_class_velocity_variances=data.mobility_class_velocity_variances,
):
# Append each batch to the main DataFrame
if ue_tracks_generation.empty:
ue_tracks_generation = ue_tracks_generation_batch
else:
ue_tracks_generation = pd.concat(
[ue_tracks_generation, ue_tracks_generation_batch], ignore_index=True
)
Comment on lines +994 to +999
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for if/else -- concat works on empty


return ue_tracks_generation


def plot_ue_tracks(df) -> None:
"""
Plots the movement tracks of unique UE IDs on a grid of subplots.
"""

# Initialize an empty list to store batch indices
batch_indices = []

# Identify where tick resets and mark the indices
for i in range(1, len(df)):
if df.loc[i, "tick"] == 0 and df.loc[i - 1, "tick"] != 0:
batch_indices.append(i)

# Add the final index to close the last batch
batch_indices.append(len(df))

# Now, iterate over the identified batches
start_idx = 0
for batch_num, end_idx in enumerate(batch_indices):
batch_data = df.iloc[start_idx:end_idx]

# Create a new figure
plt.figure(figsize=(10, 6))

# Generate a color map with different colors for each ue_id
color_map = cm.get_cmap("tab20", len(batch_data["mock_ue_id"].unique()))

# Plot each ue_id's movement over ticks in this batch
for idx, ue_id in enumerate(batch_data["mock_ue_id"].unique()):
ue_data = batch_data[batch_data["mock_ue_id"] == ue_id]
color = color_map(idx) # Get a unique color for each ue_id

# Plot the path with arrows
for i in range(len(ue_data) - 1):
x_start = ue_data.iloc[i]["lon"]
y_start = ue_data.iloc[i]["lat"]
x_end = ue_data.iloc[i + 1]["lon"]
y_end = ue_data.iloc[i + 1]["lat"]

# Calculate the direction vector
dx = x_end - x_start
dy = y_end - y_start

# Plot the line with an arrow with reduced width and unique color
plt.quiver(
x_start,
y_start,
dx,
dy,
angles="xy",
scale_units="xy",
scale=1,
color=color,
width=0.002,
headwidth=3,
headlength=5,
)

# Plot starting points as circles with the same color
plt.scatter(
ue_data["lon"].iloc[0],
ue_data["lat"].iloc[0],
color=color,
label=f"Start UE {ue_id}",
)

# Set plot title and labels
plt.title(f"UE Tracks with Direction for Batch {batch_num + 1}")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.legend(loc="upper right", bbox_to_anchor=(1.2, 1))

# Display the plot
plt.show()

# Update start_idx for the next batch
start_idx = end_idx

def plot_ue_tracks_side_by_side(df1, df2):
"""
Plots the movement tracks of unique UE IDs from two DataFrames side by side.
"""
# Set up subplots with 2 columns for side by side plots
fig, axes = plt.subplots(1, 2, figsize=(25, 10)) # 2 rows, 2 columns (side by side)

# Plot the first DataFrame
plot_ue_tracks_on_axis(df1, axes[0], title='DataFrame 1')

# Plot the second DataFrame
plot_ue_tracks_on_axis(df2, axes[1], title='DataFrame 2')

# Adjust layout and show
plt.tight_layout()
plt.show()

def plot_ue_tracks_on_axis(df, ax, title):
"""
Helper function to plot UE tracks on a given axis.
"""
data = df
unique_ids = data['mock_ue_id'].unique()
num_plots = len(unique_ids)

color_map = cm.get_cmap('tab20', num_plots)

for idx, ue_id in enumerate(unique_ids):
ue_data = data[data['mock_ue_id'] == ue_id]

for i in range(len(ue_data) - 1):
x_start = ue_data.iloc[i]['lon']
y_start = ue_data.iloc[i]['lat']
x_end = ue_data.iloc[i + 1]['lon']
y_end = ue_data.iloc[i + 1]['lat']

dx = x_end - x_start
dy = y_end - y_start
ax.quiver(x_start, y_start, dx, dy, angles='xy', scale_units='xy', scale=1, color=color_map(idx))

ax.scatter(ue_data['lon'], ue_data['lat'], color=color_map(idx), label=f'UE {ue_id}')

ax.set_title(title)
ax.legend()


def calculate_distances_and_velocities(group):
"""Calculating distances and velocities for each UE based on sorted data by ticks."""
group["prev_longitude"] = group["lon"].shift(1)
group["prev_latitude"] = group["lat"].shift(1)
group["distance"] = group.apply(
lambda row: GISTools.get_log_distance(
row["prev_latitude"], row["prev_longitude"], row["lat"], row["lon"]
)
if not pd.isna(row["prev_longitude"])
else 0,
axis=1,
)
# Assuming time interval between ticks is 1 unit, adjust below if different
group["velocity"] = (
group["distance"] / 1
) # Convert to m/s by dividing by the seconds per tick, here assumed to be 1s
return group


def preprocess_ue_data(data):
"""Preprocessing data to calculate distances and velocities for each UE."""
# Ensure data is sorted by UE ID and tick to ensure accurate shift operations
data.sort_values(by=["mock_ue_id", "tick"], inplace=True)
data = data.groupby("mock_ue_id").apply(calculate_distances_and_velocities)

# Drop the temporary columns
data.drop(["prev_longitude", "prev_latitude", "distance"], axis=1, inplace=True)

return data


def get_predicted_alpha(data, alpha0):
"""
Estimate the alpha parameter for a Gauss-Markov mobility model using regression analysis
on the velocity data derived from user equipment (UE) tracks.

This function processes the provided UE track data to calculate velocities,
fits a regression model using polynomial regression and non linear least squares,
to estimate the alpha parameter that best describes the randomness or directionality in the mobility pattern,
and returns the optimized alpha.
"""
# Preprocess data to calculate velocities
velocity_df = preprocess_ue_data(data)

# ParameterRegression is used to regress the data to predict alpha using velocity
regression = ParameterRegression(velocity_df)

# Optimize alpha using the initial guess alpha0
predicted_alpha, predicted_cov = regression.optimize_alpha(alpha0)

return float(predicted_alpha)
Loading