Commit 87a8f0b7 authored by Volkan Kayatas's avatar Volkan Kayatas
Browse files

Added random episode generator and episode to edgelist function, updated outdated project structure

parent f9a816aa
{
"python.pythonPath": "/Users/Vikram/anaconda3/bin/python"
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import logging
import os
from io import open
from time import time
from six.moves import range
from six import iterkeys
from collections import Iterable
import random
import numpy as np
from Utils import defaultdict
logger = logging.getLogger("Episode generator")
"""
Helper class to generate episodes from a given maze.
"""
class Graph(defaultdict):
"""Efficient basic implementation of nx `Graph' – Undirected graphs with self loops"""
def __init__(self):
super(Graph, self).__init__(list)
def nodes(self):
return self.keys()
def make_undirected(self):
t0 = time()
for v in list(self):
for other in self[v]:
if v != other:
self[other].append(v)
t1 = time()
logger.info('make_directed: added missing edges {}s'.format(t1 - t0))
self.make_consistent()
return self
def make_consistent(self):
t0 = time()
for k in iterkeys(self):
self[k] = list(sorted(set(self[k])))
t1 = time()
logger.info('make_consistent: made consistent in {}s'.format(t1 - t0))
self.remove_self_loops()
return self
def remove_self_loops(self):
removed = 0
t0 = time()
for x in self:
if x in self[x]:
self[x].remove(x)
removed += 1
t1 = time()
logger.info('remove_self_loops: removed {} loops in {}s'.format(removed, (t1 - t0)))
return self
def has_edge(self, v1, v2):
if v2 in self[v1] or v1 in self[v2]:
return True
return False
def degree(self, nodes=None):
if isinstance(nodes, Iterable):
return {v: len(self[v]) for v in nodes}
else:
return len(self[nodes])
def order(self):
"Returns the number of nodes in the graph"
return len(self)
def number_of_edges(self):
"Returns the number of nodes in the graph"
return sum([self.degree(x) for x in self.keys()]) / 2
def number_of_nodes(self):
"Returns the number of nodes in the graph"
return self.order()
# helper function to get the obstacle nodes (nodes with no edges (with no output edges for directed case))
def detect_obstacle_loc(self):
# TODO: with this undirected case never will have a obstacle for our mazes (method works correct, need to alter mazes or
# TODO: simply select set of directed nodes for obstacle generation)
nodes = list(self.nodes())
nodes.sort()
# get obstacle nodes by calculating the symmetric difference
all_nodes = np.arange(0, nodes[-1] + 1)
obstacles = list(set(nodes).symmetric_difference(set(all_nodes)))
return obstacles
# TODO: doesnt check if end node is reachable
def random_walk(self, start, end, max_path_length):
""" Returns a truncated random walk.
start: the start node of the random walk.
end: the end node of the random walk.
max_path_length: Length of the random walk if end node is not reached.
"""
G = self
rand = random.Random()
# select start node
if start is not None:
path = [start]
else:
# select random node as start node
path = [rand.choice(list(G.keys()))]
# Search until end node is reached or max_path_length reached
while path[-1] != end:
# check if max path length is reached
if max_path_length is not None:
if len(path) < max_path_length:
break
cur = path[-1]
# check if graph is valid
if len(G[cur]) > 0:
# select random next node
path.append(rand.choice(G[cur]))
else:
break
#return episode
return [str(node) for node in path]
# helper function to transform a edgelist to a matrix (Nodes -> adjacent edges)
def load_edgelist(file_, undirected=True):
G = Graph()
with open(file_) as f:
for l in f:
x, y = l.strip().split()[:2]
x = int(x)
y = int(y)
G[x].append(y)
if undirected:
G[y].append(x)
G.make_consistent()
return G
def generate_random_episodes(edgelist_path, max_episodes, end, undirected=True, start=None, max_path_length=None):
"""
Generates random episodes from start node to a given end node.
:param edgelist_path: path to edgelist file.
:param max_episodes: number of episodes.
:param max_path_length: breaking condition.
:param end: target node of maze.
:param start: start node of search. If None, random start node for each episode selected.
:param undirected: load undirected or directed graph.
:return: list of random episodes.
"""
maze_graph = load_edgelist(edgelist_path, undirected)
episodes = []
for i in range(max_episodes):
episode = maze_graph.random_walk(max_path_length=max_path_length, start=start, end=end)
# check if episode ended with end node
if episode[-1] != end | end is None:
raise("Episode does not contain specified end node")
episodes.append(episode)
print("Episode {}: Target found with path length {}.".format(i+1, len(episode)))
return episodes
def episode_to_edgelist(episode, file, folder):
"""
Save episodes as edge_list.
:param episode: path of nodes.
:param folder: episode folder of specific maze
:param file: file name.
:return: Graph format of path.
"""
path = os.getcwd() + folder + file+".edgelist"
# create folder if necessary
if not os.path.isdir(os.getcwd() + folder):
os.makedirs(os.getcwd() + folder)
with open(path, 'w') as f:
# iterate over every edge in path..
for i, j in zip(episode[:-1], episode[1:]):
# .. and save edge in file
f.write("{} {}\n".format(i, j))
if __name__ == "__main__":
MAZE_FOLDER = os.getcwd() + "\\Maze\\"
MAX_EPISODES = 10
# generate episodes for every maze in maze folder
for EDGELIST_FILE in os.listdir(MAZE_FOLDER):
print(EDGELIST_FILE)
#EDGELIST_FILE = "maze1.edgelist"
prefix = EDGELIST_FILE.replace(".edgelist","")
EPISODES_FOLDER = "\\Episodes\\{}\\".format(prefix)
# generate 10 episodes from a random start node to a specific end node..
episodes = generate_random_episodes(MAZE_FOLDER + EDGELIST_FILE, max_episodes=MAX_EPISODES, start=0, end=99)
# .. and save the episodes as edgelists
for i in range(len(episodes)):
episode_to_edgelist(episodes[i], file="episode_"+str(i+1), folder=EPISODES_FOLDER)
print("End of process.")
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment