I have a multi-dimensional matrix (6D) that I need to iterate over in order to make a new 6D-matrix. Right now I use list comprehensions to make the code as clean as possible, however it is really small. I was hoping there were some build-in numpy function to help me out, but because of the own functions used within the lists it is hard to find such functions.
I already tried np.fromIter, but this errors, because I use a multidimensional list. World.allReachableCoords(x1, y1, len(Q1), len(Q1[0]) returns a set of all surrounding coordinates ({(x1, y1), (x1 + 1, y1), (x1, y1 + 1) ...}) and world.amountOfPossibleActions just returns 5.
The algorithm starts with
Q1 = np.zeros((heightWorld, widthWorld, heightWorld, widthWorld, world.amountOfPossibleActions,
world.amountOfPossibleActions))
and then iterates the process below several times.
Q1 = np.array([[[[[[sum(
world.joinedTransition((x1, y1), sf1, (x2, y2), sf2, action1, action2) *
(world.joinedU((x1, y1), sf1, (x2, y2), sf2, action1, action2, player) +
world.joinedU((x1, y1), sf1, (x2, y2), sf2, action2, action1, otherPlayer) +
gamma * np.amax(Q1[sf1[1]][sf1[0]][sf2[1]][sf2[0]]))
for sf1 in World.allReachableCoords(x1, y1, len(Q1), len(Q1[0]), world)
for sf2 in World.allReachableCoords(x2, y2, len(Q1), len(Q1[0]), world)
)
for action1 in range(world.amountOfPossibleActions)]
for action2 in range(world.amountOfPossibleActions)]
for x1 in range(widthWorld)] for y1 in range(heightWorld)]
for x2 in range(widthWorld)] for y2 in range(heightWorld)])
where the joined transition is mostly a string of if-statements:
# Transition function: Returns 0 if the final state is out of bounds, impassable terrain or too far from the
# initial state. If the given action explains the relation between si and sf return 1, otherwise 0.
def standardTransition(self, si, sf, a):
if not (0 <= sf[0] <= len(self.grid[0]) and 0 <= sf[1] <= len(self.grid)):
return 0
if not (0 <= si[0] <= len(self.grid[0]) and 0 <= si[1] <= len(self.grid)):
return 0
if self.grid[sf[1]][sf[0]] == self.i or self.grid[si[1]][si[0]] == self.i:
return 0
if abs(si[0] - sf[0]) > 1 or abs(si[1] - sf[1]) > 1:
return 0
return {
0: 1 if sf[0] == si[0] and sf[1] == si[1] else 0, # Stay
1: 1 if sf[0] == si[0] and sf[1] == si[1] + 1 else 0, # Down
2: 1 if sf[0] == si[0] and sf[1] == si[1] - 1 else 0, # Up
3: 1 if sf[0] == si[0] - 1 and sf[1] == si[1] else 0, # Left
4: 1 if sf[0] == si[0] + 1 and sf[1] == si[1] else 0 # Right
}[a]
def joinedTransition(self, si1, sf1, si2, sf2, a1, a2):
if sf1 == sf2: return 0 # Ending in the same square is impossible.
if si1 == sf2 and si2 == sf1: return 0 # Going through each other is impossible.
# Fighting for the same square.
if si1 == sf1 and performAction(si1, a1) == sf2: # Player 1 loses the fight
return self.standardTransition(si1, sf2, a1) * self.standardTransition(si2, sf2,
a2) * self.chanceForPlayer1ToWinDuel
if si2 == sf2 and performAction(si2, a2) == sf1: # Player 2 loses the fight
return self.standardTransition(si1, sf1, a1) * self.standardTransition(si2, sf1, a2) * (
1 - self.chanceForPlayer1ToWinDuel)
return self.standardTransition(si1, sf1, a1) * self.standardTransition(si2, sf2, a2)
and, allReachableCoords is like said above:
def allReachableCoords(x1, y1, height, width, world):
li = {(x2, y1) for x2 in range(x1 - 1, x1 + 2)}.union({(x1, y2) for y2 in range(y1 - 1, y1 + 2)})
li = list(filter(lambda r: 0 <= r[0] < width and 0 <= r[1] < height, li))
return list(filter(lambda r: world.grid[r[1]][r[0]] != world.i, li))
Are there any ways to improve performance? I suppose the solution is numpy, but other solutions are also welcome. I was also wondering if this is something that can be done more elegantly and efficiently in tensorflow.