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

added examples #29

Merged
merged 4 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
234 changes: 234 additions & 0 deletions examples/AircraftTurnaround/prepare_aircraft_turnaround_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import os
from pathlib import Path

# additional imports
import queue
import random
import re
from collections import defaultdict
from functools import reduce

import matplotlib.colors as mcolors
import networkx as nx
import numpy as np
import pandas as pd

CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
EXAMPLE_DIR = os.path.dirname(CURRENT_DIR)
HOME_DIR = os.path.dirname(EXAMPLE_DIR)
os.chdir(HOME_DIR)
CURRENT_DATA_DIR = os.path.join(CURRENT_DIR, "data")
CURRENT_OUTPUT_DIR = os.path.join(CURRENT_DIR, "output")
CURRENT_CONFIG_FILENAME = os.path.join(CURRENT_DIR, "config.json")


def graph_from_df(
df,
num_str: str = "No.",
op_str: str = "Operation",
act_str: str = "Activity",
prec_str: str = "Precedence",
):
# Select a color for an operation
operations = df[op_str].unique()
colors = list(mcolors.CSS4_COLORS.keys())

# Select colros at equidistant
indices = np.round(
np.linspace(0, len(mcolors.CSS4_COLORS) - 1, len(operations))
).astype(int)
selected_colors = {operations[i]: colors[idx] for i, idx in enumerate(indices)}

g = nx.DiGraph()

for _, row in df.iterrows():
# Add Nodes
curr = row[num_str]
act = row[act_str]
op = row[op_str]
color = selected_colors[op]
g.add_node(curr, Activity=act, Operation=op, fillcolor=color, style="filled")

# Add Edges
precs = row.get(prec_str)
if precs is not None:
values = re.findall(r"\d+", precs)
if len(values) != 0:
for value in values:
value = int(value)
g.add_edge(value, curr)

return g


def __listoflist_to_list(list_of_list):
return reduce(lambda a, b: a + b, list_of_list)


def merge_nodes_by_column_type(g, df, op_str: str = "Operation", num_str: str = "No."):

# Select a color for an operation
operations = df[op_str].unique()
colors = list(mcolors.CSS4_COLORS.keys())

# Select colros at equidistant
indices = np.round(
np.linspace(0, len(mcolors.CSS4_COLORS) - 1, len(operations))
).astype(int)
selected_colors = {operations[i]: colors[idx] for i, idx in enumerate(indices)}

G = nx.DiGraph()
operation_nodes = {}
incomings = {}
outgoings = {}

for operation in df[op_str].unique():
nodes = df[df[op_str] == operation][num_str]
operation_nodes[operation] = nodes
innodes = __listoflist_to_list([list(g.predecessors(n)) for n in nodes])
outnodes = __listoflist_to_list([list(g.successors(n)) for n in nodes])
incomings[operation] = set([g.nodes[n][op_str] for n in innodes])
outgoings[operation] = set([g.nodes[n][op_str] for n in outnodes])

color = selected_colors[operation]
G.add_node(operation, fillcolor=color, style="filled")

for operation, sources in incomings.items():
for source in sources:
G.add_edge(source, operation)

for operation, targets in outgoings.items():
for target in targets:
G.add_edge(operation, target)

# remove self loop
for operation in df[op_str].unique():
if operation in G[operation]:
G.remove_edge(operation, operation)

trG = nx.transitive_reduction(G)
for n in trG.nodes():
nodes = list(G.nodes())
for k, v in G.nodes[n].items():
trG.nodes[n][k] = v
return trG


def sample_trace(
G, means: dict, stds: dict, max_thresholds: dict, set_bound: bool = False
):
"""
Sample traces from a TPO.
"""

inits = [n for n in G.nodes() if len(list(G.predecessors(n))) == 0]

Q = queue.Queue()
for init in inits:
Q.put(init)
visitedE = defaultdict(lambda: [])
times = {}

trace = []
visited_events = []

while not Q.empty():
curr_node = Q.get()

if any([p not in visitedE[curr_node] for p in G.predecessors(curr_node)]):
continue

curr_time = max([0.0] + [times[p] for p in visitedE[curr_node]])
duration = np.random.normal(means[curr_node], stds[curr_node])
duration = max(0, duration)
if set_bound:
duration = min(duration, max_thresholds[curr_node])
curr_time += duration

# Record Time
times[curr_node] = curr_time
if curr_node not in visited_events:
trace.append((curr_time, curr_node))
visited_events.append(curr_node)

# Move onto the next node
next_nodes = list(G.successors(curr_node))
random.shuffle(next_nodes)

for next_node in next_nodes:
visitedE[next_node].append(curr_node)
Q.put(next_node)

return trace


def sample_traces(ntrace: int, G, means, stds, max_thresholds, set_bound):
traces = []
for i in range(ntrace):
traces.append(sample_trace(G, means, stds, max_thresholds, set_bound))
return traces


def connect_sink_states(df):
# Add 6 to 7
index = df[df["No."] == 7].index
df.loc[index, "Precedence"] = df.loc[index, "Precedence"] + ",6"
# Add 10 to 11
index = df[df["No."] == 11].index
df.loc[index, "Precedence"] = df.loc[index, "Precedence"] + ",10"
# Add 16 to 17
index = df[df["No."] == 17].index
df.loc[index, "Precedence"] = df.loc[index, "Precedence"] + ",16"
# Add 29 to 30
index = df[df["No."] == 30].index
df.loc[index, "Precedence"] = df.loc[index, "Precedence"] + ",29"
# sink states (7, 15, 18, 20) to the last node 44.
index = df[df["No."] == 44].index
df.loc[index, "Precedence"] = df.loc[index, "Precedence"] + ",7,15,18,20,41"
pd.set_option("display.max_rows", df.shape[0] + 1)

return df


def get_distributions(df):
means = {}
stds = {}
max_thresholds = {}
for i, row in df.iterrows():
if i == 0:
continue
operation = row.index[1]
mean = row.index[2]
std = row.index[3]
delay = row.index[4]
means[row[operation]] = int(row[mean])
stds[row[operation]] = int(row[std])
max_thresholds[row[operation]] = int(row[delay])
return means, stds, max_thresholds


def load_aircraft_turnaround_data():
filename = os.path.join(CURRENT_DATA_DIR, "ground_services_by_operations.csv")
df = pd.read_csv(filename)
filename = os.path.join(CURRENT_DATA_DIR, "duration.csv")
duration_df = pd.read_csv(filename)

# TODO: this should apply to graph G and not df
df = connect_sink_states(df)

# Get the distributions
means, stds, max_thresholds = get_distributions(duration_df)
# Create a graph
g = graph_from_df(df)

# Merge nodes by column type
G = merge_nodes_by_column_type(g, df, op_str="Operation")

# Sample traces
traces = sample_traces(10000, G, means, stds, max_thresholds, set_bound=True)

return traces


if __name__ == "__main__":
load_aircraft_turnaround_data()
File renamed without changes.
28 changes: 28 additions & 0 deletions examples/demo/learning_example2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import sys

import specless as sl


# Add the AicraftTurnaroundA directory to sys.path for importing the data preparation script
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
EXAMPLE_DIR = os.path.dirname(CURRENT_DIR)
AIRCRAFT_TURNAROUND_DIR = os.path.join(EXAMPLE_DIR, "AircraftTurnaround")
print(CURRENT_DIR, EXAMPLE_DIR, AIRCRAFT_TURNAROUND_DIR)
sys.path.append(AIRCRAFT_TURNAROUND_DIR)
from prepare_aircraft_turnaround_data import load_aircraft_turnaround_data


def main():
# Load a list of traces sampled from an aircraft turnaround task
demonstrations = load_aircraft_turnaround_data()

inference = sl.TPOInferenceAlgorithm()
specification: sl.Specification = inference.infer(demonstrations)

sl.draw_graph(specification, filepath=os.path.join(CURRENT_DIR, "tpo"))
print(specification)


if __name__ == "__main__":
main()
107 changes: 0 additions & 107 deletions examples/demo/planning.py

This file was deleted.

Loading
Loading