Commit 55c7cdcd authored by David A.. Werner's avatar David A.. Werner
Browse files

muscle_stength is now part of ManducaBodyProperty. Added mutate/mate methods....

muscle_stength is now part of ManducaBodyProperty. Added mutate/mate methods. Added change_muscle_value method
parent fa9808ea
......@@ -16,13 +16,13 @@ class ManducaDeformityError(Exception):
# k: Elastic spring constant
# m: Leg mass
# c: Viscosity constant
ManducaBodyProperties = namedtuple('ManducaBodyProperties', ['L0', 'k', 'c', 'm'])
ManducaBodyProperties = namedtuple('ManducaBodyProperties', ['L0', 'k', 'c', 'm','muscle_strength'])
class Manduca(object):
""" A simple model of a Manduca Sexta consiting of legs and muscles, modeled as masses and
springs, respectively."""
default_body_properties = ManducaBodyProperties(500, 1, 2, 1)
damped_body_properties = ManducaBodyProperties(500, 1, 3, 1)
default_body_properties = ManducaBodyProperties(500, 1, 2, 1, 100)
damped_body_properties = ManducaBodyProperties(500, 1, 3, 1, 100)
def __init__(self, legs, muscles, time_step, body_properties=None, fitness_function=None):
self._legs = self._muscles = None
self.legs = np.copy(legs)
......@@ -130,6 +130,48 @@ class Manduca(object):
# Make sure the number of time steps is consistant
self.num_time_steps
def change_muscle_value(self, current_value, rng=np.random):
return rng.rand()*self.body_properties.muscle_strength
# First randomly choose the number of mutations (1 up to MAX_MUTATIONS)
# - flip a random leg-locked
# - flip a random muscle-on
# - do the secret-sauce mutation if desired.
def mutate (self, rng=np.random, max_mutations = 20):
n_mutations = rng.randing(1,MAX_MUTATIONS)
for _ in num_mutations:
mutation_type = rng.randint(2)
if mutation_type == 0:
time_step = rng.randint(self.num_time_steps)
leg_idx = np.randint(self.num_legs)
value = self.legs[time_step][leg_idx]
self.legs[time_step][leg_idx] = int(value) ^ 1
elif mutation_type == 1:
time_step = rng.randint(self.num_time_steps)
muscle_idx = rng.randint(self.num_muscles)
value = self.muscles[time_step][muscle_idx]
new_value = self.change_muscle_value(value, rng=rng)
self.muscles[time_step][muscle_idx] = new_value
elif mutation_type == 2:
raise("Not supported")
pass # SECRET SAUCE and change mutation_type rng
# Pick entire rows from one parent or the other. Each row of the child comes
# from the first parent or the second (with equal likelihood).
# This is the final function that you write yourself.
def mate (self, parent2, rng=np.random):
child = self.copy()
num_time_steps = child.num_time_steps
for time_step in range(num_time_steps):
dna_src = rng.randint(1)
if dna_src == 0:
continue
else:
child.legs[time_step] = parent2.legs[time_step]
child.muscles[time_step] = parent2.muscles[time_step]
return child
def __repr__(self):
output = StringIO.StringIO()
output.write("Body Properties: {}\n".format(self.body_properties))
......@@ -170,7 +212,7 @@ class Manduca(object):
returns recored positions, velocities, and times
"""
L0, k, c, m = self.body_properties
L0, k, c, m, muscle_strength = self.body_properties
def calculate_slopes(x, t):
"""This function computes the derivatives of x
......@@ -319,24 +361,26 @@ class Manduca(object):
class SimpleManduca(Manduca):
"""A Manduca that has legs and muscles that are either ON/OFF"""
def __init__(self, legs, muscles, time_step, muscle_strength=None, **kwargs):
def __init__(self, legs, muscles, time_step, **kwargs):
super(SimpleManduca, self).__init__(legs, muscles, time_step, **kwargs)
locked_legs, unlocked_legs = self.legs == 1, self.legs == 0
assert (locked_legs | unlocked_legs).all(), "Legs must be 0 or 1"
if muscle_strength is None:
msg = "Cannot detemine muscle_strength when all values are 0"
assert self.muscles.max() != 0, msg
muscle_strength = self.muscles.max()
musc_on, musc_off = self.muscles == 0, self.muscles == muscle_strength
msg = "Muscles must be 0 or {}".format(muscle_strength)
musc_on, musc_off = self.muscles == 0, self.muscles == self.body_properties.muscle_strength
msg = "Muscles must be 0 or {}".format(self.body_properties.muscle_strength)
assert (musc_on | musc_off).all(), msg
@staticmethod
def random_individual(num_legs, time_segments, time_step, **kwargs):
muscle_strength = kwargs.pop('muscle_strength', 100)
body_properties = kwargs.pop('body_properties', None)
if body_properties == None:
body_properties = Manduca.default_body_properties
kwargs['body_properties'] = body_properties
muscle_strength = body_properties.muscle_strength
rng = kwargs.pop('rng', np.random)
legs = rng.choice([0, 1], size=(time_segments, num_legs))
muscles = rng.choice([0, muscle_strength],
size=(time_segments, num_legs-1))
return SimpleManduca(legs, muscles, time_step,
muscle_strength=muscle_strength, **kwargs)
return SimpleManduca(legs, muscles, time_step, **kwargs)
def change_muscle_value(self, current_value):
return current_value^self.body_properties.muscle_strength
from manduca import Manduca, ManducaBodyProperties
all_body_properties = [None, Manduca.default_body_properties, Manduca.damped_body_properties,
ManducaBodyProperties(10,4,6,2)]
ManducaBodyProperties(10,4,6,2,10)]
all_fitness_functions = [Manduca.distance_traveled, lambda m: 1/sum(m.muscles+0.001)]
all_num_time_steps = [2, 3, 5, 10]
all_num_legs = [2, 3, 4, 5, 10]
......
import pytest
import numpy as np
#import random
from manduca import Manduca, SimpleManduca, ManducaDeformityError
from manduca import Manduca, SimpleManduca, ManducaDeformityError, ManducaBodyProperties
def test_too_many_muscles(num_legs, num_time_steps):
legs = np.zeros((num_time_steps,num_legs))
......@@ -22,9 +22,10 @@ def test_well_formed(num_legs, num_time_steps):
def test_simple_save(tmpdir, num_legs, num_time_steps, time_step, compressed):
file_name = tmpdir.join('manduca.npz').strpath
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step, muscle_strength=100)
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step,
body_properties=Manduca.default_body_properties)
man.save(file_name, compressed=compressed)
new_man = SimpleManduca.load(file_name, muscle_strength=100)
new_man = SimpleManduca.load(file_name,body_properties=Manduca.default_body_properties)
assert new_man == man
@pytest.mark.parametrize("loaded_muscle_strength", [0.1, 1.0, 100])
......@@ -32,40 +33,39 @@ def test_simple_save_with_different_muscle_strengths(tmpdir, muscle_strength, lo
time_step, num_time_steps = 1, 1
num_legs = 2
file_name = tmpdir.join('manduca.npz').strpath
default = Manduca.default_body_properties
body_properties = ManducaBodyProperties(default.L0, default.k, default.c,
default.m, muscle_strength)
loaded_body_properties = ManducaBodyProperties(default.L0, default.k, default.c,
default.m, loaded_muscle_strength)
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step,
muscle_strength=muscle_strength)
body_properties=body_properties)
man.save(file_name, compressed=compressed)
if muscle_strength != loaded_muscle_strength:
with pytest.raises(AssertionError):
new_man = SimpleManduca.load(file_name, muscle_strength=loaded_muscle_strength)
# This will raise an assertion error if the muscle_strength is mismatched
new_man = SimpleManduca.load(file_name, body_properties=loaded_body_properties)
# If all of the muscles are 0, then it will accept any muscle_strength
ok_to_fail = new_man.muscles.max() == 0
# We expect assertion error, so raise one if we expected this kind of failure
test_failed_as_expected = not(ok_to_fail)
assert test_failed_as_expected
# The actual muscle_strength should be auto-detected, UNLESS all values are 0
try:
new_man = SimpleManduca.load(file_name)
assert new_man == man
except AssertionError:
# This should only happen if all values are 0, so ensure that is the case
new_man = SimpleManduca.load(file_name, muscle_strength=muscle_strength)
assert np.count_nonzero(new_man.muscles) == 0
assert new_man == man
new_man = SimpleManduca.load(file_name)
assert new_man == man
else:
new_man = SimpleManduca.load(file_name, muscle_strength=loaded_muscle_strength)
new_man = SimpleManduca.load(file_name, body_properties=loaded_body_properties)
assert new_man == man
@pytest.mark.parametrize("fitness", [None, 0.01, 1.0, 10])
def test_simple_save_with_fitness(tmpdir, fitness, compressed):
time_step, num_time_steps, num_legs = 1, 1, 2
file_name = tmpdir.join('manduca.npz').strpath
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step, muscle_strength=100)
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step)
# Manualy set fitness
man._fitness = fitness
man.save(file_name, compressed=compressed)
new_man = SimpleManduca.load(file_name, muscle_strength=100)
new_man = SimpleManduca.load(file_name)
assert new_man._fitness == man._fitness
assert new_man._fitness == fitness
......@@ -73,12 +73,10 @@ def test_simple_save_with_body_properties(tmpdir, body_properties, compressed):
time_step, num_time_steps, num_legs = 1, 1, 2
file_name = tmpdir.join('manduca.npz').strpath
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step,
body_properties=body_properties,
muscle_strength=100)
body_properties=body_properties)
man.save(file_name, compressed=compressed)
new_man_1 = SimpleManduca.load(file_name, muscle_strength=100)
new_man_2 = SimpleManduca.load(file_name, body_properties=body_properties,
muscle_strength=100)
new_man_1 = SimpleManduca.load(file_name)
new_man_2 = SimpleManduca.load(file_name, body_properties=body_properties)
assert new_man_1 == man
assert new_man_1 == new_man_2
......@@ -86,11 +84,9 @@ def test_simple_load_with_specified_fitness_function(tmpdir, fitness_function):
time_step, num_time_steps, num_legs = 1, 1, 2
file_name = tmpdir.join('manduca.npz').strpath
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step,
muscle_strength=100,
fitness_function=fitness_function)
fitness_function=fitness_function)
man.save(file_name, compressed=True)
new_man = SimpleManduca.load(file_name, muscle_strength=100,
fitness_function=fitness_function)
new_man = SimpleManduca.load(file_name, fitness_function=fitness_function)
assert new_man == man
assert new_man.fitness == man.fitness
assert new_man.fitness_function == man.fitness_function
......@@ -98,13 +94,13 @@ def test_simple_load_with_specified_fitness_function(tmpdir, fitness_function):
@pytest.mark.parametrize("record_level", [0,1,2,None])
@pytest.mark.parametrize("sub_intervals", [1,2,10])
def test_simulate_result_sizes(num_legs, num_time_steps, time_step, record_level, sub_intervals):
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step, muscle_strength=100)
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step)
x,v,t = man.simulate(record_level=record_level, sub_intervals=sub_intervals)
x,v,t = np.array(x), np.array(v), np.array(t)
assert x.shape == v.shape
assert len(x) == len(t)
if record_level == 1:
assert len(x) == num_time_steps + 1
assert len(x) == num_time_steps + 1
elif record_level == 2:
assert len(x) == num_time_steps*sub_intervals + 1
else:
......@@ -112,7 +108,7 @@ def test_simulate_result_sizes(num_legs, num_time_steps, time_step, record_level
@pytest.mark.parametrize("sub_intervals", [1,2,10])
def test_simulate_result_sizes(num_legs, num_time_steps, time_step, sub_intervals):
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step, muscle_strength=100)
man = SimpleManduca.random_individual(num_legs, num_time_steps, time_step)
x0,v0,t0 = man.simulate(record_level=0, sub_intervals=sub_intervals)
x0,v0,t0 = np.array(x0), np.array(v0), np.array(t0)
......@@ -133,7 +129,7 @@ def test_simulate_result_sizes(num_legs, num_time_steps, time_step, sub_interval
x2,v2,t2 = np.array(x2), np.array(v2), np.array(t2)
def test_fitness_reset():
man = SimpleManduca.random_individual(2, 2, 1, muscle_strength=100)
man = SimpleManduca.random_individual(2, 2, 1)
assert man._fitness == None
man.fitness
assert man._fitness != None
......
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