Skip to content

Commit

Permalink
Merge pull request #201 from tcld/numpy_plates
Browse files Browse the repository at this point in the history
world.plates->numpy, some optimizations
  • Loading branch information
ftomassetti committed Nov 18, 2015
2 parents 187e720 + ba0edf2 commit 3e8030e
Show file tree
Hide file tree
Showing 8 changed files with 20 additions and 113 deletions.
40 changes: 1 addition & 39 deletions tests/common_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import unittest
import numpy
from worldengine.common import Counter, anti_alias, array_to_matrix, get_verbose, \
matrix_min_and_max, rescale_value, set_verbose
from worldengine.common import Counter, anti_alias, get_verbose, set_verbose


class TestCommon(unittest.TestCase):
Expand All @@ -16,19 +15,6 @@ def test_get_and_set_verbose(self):
set_verbose(False)
self.assertEqual(False, get_verbose())

def test_matrix_min_and_max(self):
m1 = [
[-0.8, -0.5, 1.0, 2.6],
[-0.8, -1.5, 8.8, 2.7]
]
m2 = []
m3 = [[], []]
m4 = [[0.0]]
self.assertEqual((-1.5, 8.8), matrix_min_and_max(m1))
self.assertEqual((None, None), matrix_min_and_max(m2))
self.assertEqual((None, None), matrix_min_and_max(m3))
self.assertEqual((0.0, 0.0), matrix_min_and_max(m4))

def test_counter(self):
c = Counter()
self.assertEqual("", c.to_str())
Expand All @@ -40,25 +26,6 @@ def test_counter(self):
c.count("a")
self.assertEqual("a : 1\nb : 3\n", c.to_str())

def test_rescale_value(self):
self.assertAlmostEqual(0.0, rescale_value(0.0, 0.0, 1.0, 0.0, 10.0))
self.assertAlmostEqual(2.5, rescale_value(0.25, 0.0, 1.0, 0.0, 10.0))
self.assertAlmostEqual(5.0, rescale_value(0.5, 0.0, 1.0, 0.0, 10.0))
self.assertAlmostEqual(7.5, rescale_value(0.75, 0.0, 1.0, 0.0, 10.0))
self.assertAlmostEqual(10.0, rescale_value(1.0, 0.0, 1.0, 0.0, 10.0))

self.assertAlmostEqual(5.0, rescale_value(0.0, 0.0, 1.0, 5.0, 10.0))
self.assertAlmostEqual(6.25, rescale_value(0.25, 0.0, 1.0, 5.0, 10.0))
self.assertAlmostEqual(7.5, rescale_value(0.5, 0.0, 1.0, 5.0, 10.0))
self.assertAlmostEqual(8.75, rescale_value(0.75, 0.0, 1.0, 5.0, 10.0))
self.assertAlmostEqual(10.0, rescale_value(1.0, 0.0, 1.0, 5.0, 10.0))

self.assertAlmostEqual(-10.0, rescale_value(0.0, 0.0, 1.0, -10.0, 10.0))
self.assertAlmostEqual(-5.0, rescale_value(0.25, 0.0, 1.0, -10.0, 10.0))
self.assertAlmostEqual(0.0, rescale_value(0.5, 0.0, 1.0, -10.0, 10.0))
self.assertAlmostEqual(5.0, rescale_value(0.75, 0.0, 1.0, -10.0, 10.0))
self.assertAlmostEqual(10.0, rescale_value(1.0, 0.0, 1.0, -10.0, 10.0))

def test_antialias(self):
original = numpy.array([[0.5, 0.12, 0.7, 0.15, 0.0],
[0.0, 0.12, 0.7, 0.7, 8.0],
Expand All @@ -71,10 +38,5 @@ def test_antialias(self):
antialiased = anti_alias(original, 10)
self.assertAlmostEquals(0.8, antialiased[0][0])

def test_array_to_matrix(self):
array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
self.assertEqual([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]], array_to_matrix(array, 5, 2))
self.assertEqual([[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]], array_to_matrix(array, 2, 5))

if __name__ == '__main__':
unittest.main()
2 changes: 2 additions & 0 deletions tests/serialization_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def test_protobuf_serialize_unserialize(self):
unserialized = World.protobuf_unserialize(serialized)
self.assertTrue(_equal(w.elevation['data'], unserialized.elevation['data']))
self.assertEqual(w.elevation['thresholds'], unserialized.elevation['thresholds'])
self.assertTrue(_equal(w.plates, unserialized.plates))
self.assertTrue(_equal(w.ocean, unserialized.ocean))
self.assertTrue(_equal(w.biome, unserialized.biome))
self.assertTrue(_equal(w.humidity, unserialized.humidity))
Expand Down Expand Up @@ -48,6 +49,7 @@ def test_hdf5_serialize_unserialize(self):
unserialized = load_world_to_hdf5(filename)
self.assertTrue(_equal(w.elevation['data'], unserialized.elevation['data']))
self.assertEqual(w.elevation['thresholds'], unserialized.elevation['thresholds'])
self.assertTrue(_equal(w.plates, unserialized.plates))
self.assertTrue(_equal(w.ocean, unserialized.ocean))
self.assertTrue(_equal(w.biome, unserialized.biome))
self.assertTrue(_equal(w.humidity['quantiles'], unserialized.humidity['quantiles']))
Expand Down
4 changes: 2 additions & 2 deletions worldengine/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
import numpy
import worldengine.generation as geo
from worldengine.common import array_to_matrix, set_verbose, print_verbose
from worldengine.common import set_verbose, print_verbose
from worldengine.draw import draw_ancientmap_on_file, draw_biome_on_file, draw_ocean_on_file, \
draw_precipitation_on_file, draw_grayscale_heightmap_on_file, draw_simple_elevation_on_file, \
draw_temperature_levels_on_file, draw_riversmap_on_file, draw_scatter_plot_on_file, \
Expand Down Expand Up @@ -114,7 +114,7 @@ def generate_plates(seed, world_name, output_dir, width, height,

world = World(world_name, width, height, seed, num_plates, -1.0, "plates")
world.set_elevation(numpy.array(elevation).reshape(height, width), None)
world.set_plates(array_to_matrix(plates, width, height))
world.set_plates(numpy.array(plates, dtype=numpy.uint16).reshape(height, width))

# Generate images
filename = '%s/plates_%s.png' % (output_dir, world_name)
Expand Down
32 changes: 0 additions & 32 deletions worldengine/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,6 @@ def print_self(self):
sys.stdout.write(self.to_str)


def matrix_min_and_max(matrix):
_min = None
_max = None
for row in matrix:
for el in row:
val = el
if _min is None or val < _min:
_min = val
if _max is None or val > _max:
_max = val
return _min, _max


def rescale_value(original, prev_min, prev_max, min, max):
"""Rescale a given value.
Given the value, the current min and max and the new min and max
produce the rescaled value
"""
f = float(original - prev_min) / (prev_max - prev_min)
return min + ((max - min) * f)


def anti_alias(map, steps):#TODO: There is probably a bit of numpy-optimization that can be done here.
"""
Execute the anti_alias operation steps times on the given map
Expand Down Expand Up @@ -111,16 +89,6 @@ def anti_alias_point(original, x, y):
return current


def array_to_matrix(array, width, height):
if len(array) != (width * height):
raise Exception("Array as not expected length")
matrix = []
for y in range(height):
matrix.append([])
for x in range(width):
matrix[y].append(array[y * width + x])
return matrix

def _equal(a, b):
#recursion on subclasses of types: tuple, list, dict
#specifically checks : float, ndarray
Expand Down
34 changes: 7 additions & 27 deletions worldengine/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,19 @@ def center_land(world):
"""Translate the map horizontally and vertically to put as much ocean as
possible at the borders. It operates on elevation and plates map"""

min_sum_on_y = None
y_with_min_sum = None
latshift = 0
for y in range(world.height):
sum_on_y = world.elevation['data'][y].sum()
if min_sum_on_y is None or sum_on_y < min_sum_on_y:
min_sum_on_y = sum_on_y
y_with_min_sum = y
y_sums = world.elevation['data'].sum(1) # 1 == sum along x-axis
y_with_min_sum = y_sums.argmin()
if get_verbose():
print("geo.center_land: height complete")

min_sum_on_x = None
x_with_min_sum = None
for x in range(world.width):
sum_on_x = world.elevation['data'].T[x].sum()
if min_sum_on_x is None or sum_on_x < min_sum_on_x:
min_sum_on_x = sum_on_x
x_with_min_sum = x
x_sums = world.elevation['data'].sum(0) # 0 == sum along y-axis
x_with_min_sum = x_sums.argmin()
if get_verbose():
print("geo.center_land: width complete")

new_elevation_data = [] #TODO: this should fully use numpy
new_plates = []
for y in range(world.height):
new_elevation_data.append([])
new_plates.append([])
src_y = (y_with_min_sum + y - latshift) % world.height
for x in range(world.width):
src_x = (x_with_min_sum + x) % world.width
new_elevation_data[y].append(world.elevation['data'][src_y, src_x])
new_plates[y].append(world.plates[src_y][src_x])
world.elevation['data'] = numpy.array(new_elevation_data)
world.plates = new_plates
latshift = 0
world.elevation['data'] = numpy.roll(numpy.roll(world.elevation['data'], -y_with_min_sum + latshift, axis=0), -x_with_min_sum, axis=1)
world.plates = numpy.roll(numpy.roll(world.plates, -y_with_min_sum + latshift, axis=0), -x_with_min_sum, axis=1)
if get_verbose():
print("geo.center_land: width complete")

Expand Down
4 changes: 1 addition & 3 deletions worldengine/hdf5_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ def save_world_to_hdf5(world, filename):
elevation_data.write_direct(world.elevation['data'])

plates_data = f.create_dataset("plates", (world.height, world.width), dtype=numpy.uint16)
for y in range(world.height):
for x in range(world.width):
plates_data[y, x] = world.plates[y][x]
plates_data.write_direct(world.plates)

ocean_data = f.create_dataset("ocean", (world.height, world.width), dtype=numpy.bool)
ocean_data.write_direct(world.ocean)
Expand Down
3 changes: 1 addition & 2 deletions worldengine/plates.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from worldengine.generation import Step, add_noise_to_elevation, center_land, generate_world, \
get_verbose, initialize_ocean_and_thresholds, place_oceans_at_map_borders
from worldengine.common import array_to_matrix
from worldengine.world import World


Expand Down Expand Up @@ -48,7 +47,7 @@ def _plates_simulation(name, width, height, seed, temps=
world = World(name, width, height, seed, num_plates, ocean_level, step, temps,
humids, gamma_curve, curve_offset)
world.set_elevation(numpy.array(e_as_array).reshape(height, width), None)
world.set_plates(array_to_matrix(p_as_array, width, height))
world.set_plates(numpy.array(p_as_array, dtype=numpy.uint16).reshape(height, width))
return world


Expand Down
14 changes: 6 additions & 8 deletions worldengine/world.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ def _to_protobuf_matrix(matrix, p_matrix, transformation=None):
'''
if type(cell) is numpy.bool_:
value = bool(cell)
elif type(cell) is numpy.uint16:
value = int(cell)
else:
value = cell
if transformation:
Expand Down Expand Up @@ -246,7 +248,7 @@ def _from_protobuf_world(cls, p_world):
w.set_elevation(e, e_th)

# Plates
w.set_plates(World._from_protobuf_matrix(p_world.plates))
w.set_plates(numpy.array(World._from_protobuf_matrix(p_world.plates)))

# Ocean
w.set_ocean(numpy.array(World._from_protobuf_matrix(p_world.ocean)))
Expand Down Expand Up @@ -790,11 +792,7 @@ def is_chaparral(self, pos):
#

def n_actual_plates(self):
res = -1
for row in self.plates:
for cell in row:
res = max([cell, res])
return res + 1
return self.plates.max() + 1

#
# Setters
Expand All @@ -809,11 +807,11 @@ def set_elevation(self, data, thresholds):
self.elevation = {'data': data, 'thresholds': thresholds}

def set_plates(self, data):
if (len(data) != self.height) or (len(data[0]) != self.width):
if (data.shape[0] != self.height) or (data.shape[1] != self.width):
raise Exception(
"Setting plates map with wrong dimension. "
"Expected %d x %d, found %d x %d" % (
self.width, self.height, len(data[0]), len(data)))
self.width, self.height, data.shape[1], data.shape[0]))
self.plates = data

def set_biome(self, biome):
Expand Down

0 comments on commit 3e8030e

Please sign in to comment.